精华内容
下载资源
问答
  • 一个checkmarx的说明文档
  • 静态代码扫描的原理

    千次阅读 2020-01-15 11:29:27
    静态代码扫描存在的价值 研发过程,发现BUG越晚,修复的成本越大 缺陷引入的大部分是在编码阶段,但发现的更多是在单元测试、集成测试、功能测试阶段 统计证明,在整个软件开发生命周期中,30% 至 70% 的代码逻辑...

    静态代码扫描存在的价值

    1. 研发过程,发现BUG越晚,修复的成本越大
    2. 缺陷引入的大部分是在编码阶段,但发现的更多是在单元测试、集成测试、功能测试阶段
    3. 统计证明,在整个软件开发生命周期中,30% 至 70% 的代码逻辑设计和编码缺陷是可以通过静态代码分析来发现和修复的

    以上三点证明了,静态代码扫描在整个安全开发的流程中起着十分关键的作用,且实施这件事情的时间点需要尽量前移,因为扫描的节点左移能够大幅度的降低开发以及修复的成本,能够帮助开发人减轻开发和修复的负担,许多公司在推行静态代码扫描工具的时候会遇到大幅度的阻力,这方面阻力主要来自于开发人员,由于工具能力的有限性,会产生大量的误报,这就导致了开发人员很可能在做BUG确认的工作时花费了大量的无用时间。因此选择一款合适的静态代码分析工具变得尤为重要,合适的工具能够真正达到降低开发成本的效果。

    静态代码分析理论基础和主要技术

    静态代码分析原理分为两种:分析源代码编译后的中间文件(如Java的字节码);分析源文件。主要分析技术如下:

    • 缺陷模式匹配
      事先从代码分析经验中收集足够多的共性缺陷模式,将待分析代码与已有的共性缺陷模式进行匹配,从而完成软件安全分析。优点:简单方便;缺点:需要内置足够多的缺陷模式,容易产生误报。

    • 类型推断/类型推断
      类型推断技术是指通过对代码中运算对象类型进行推理,从而保证代码中每条语句都针对正确的类型执行。

    • 模型检查
      建立于有限状态自动机的概念基础上。将每条语句产生的影响抽象为有限状态自动机的一个状态,再通过分析有限状态机达到分析代码目的。
      校验程序并发等时序特性。

    • 数据流分析
      从程序代码中收集程序语义信息,抽象成控制流图,可以通过控制流图,不必真实的运行程序,可以分析发现程序运行时的行为。

    静态代码扫描工具

    1. Fortify SCA(Source Code Analysis)

    网址:http://www.fortify.net/

    Fortify Software公司是一家总部位于美国硅谷,致力于提供应用软件安全开发工具和管理方案的厂商。Fortify为应用软件开发组织、安全审计人员和应用 安全管理人员提供工具并确立最佳的应用软件安全实践和策略,帮助他们在软件开发生命周期中花最少的时间和成本去识别和修复软件源代码中的安全隐患。 Fortify SCA是Fortify360产品套装中的一部分,它使用fortify公司特有的X-Tier Dataflow™ analysis技术去检测软件安全问题。

    优点:全球最大的静态代码检测厂商,支持语言较多

    缺点:价格太过昂贵,性价比不高

    2. Checkmarx CxSuite

    网址:https://www.checkmarx.com/

    Checkmarx 是以色列的一家高科技软件公司。它的产品CheckmarxCxSuite专门设计为识别、跟踪和修复软件源代码上的技术和逻辑方面的安全风险。首创了以查询语言定位代码安全问题,其采用独特的词汇分析技术和CxQL专利查询技术来扫描和分析源代码中的安全漏洞和弱点。

    优点:可以利用CxQL 查询语言自定义规则

    缺点:输出报告不够美观、语言支持种类不全面,价格昂贵

    3. Coverity

    网址:https://scan.coverity.com/

    Coverity公司是由一流的斯坦福大学的科学家于2002年成立的,产品核心技术是1998年至2002年在斯坦福大学计算机系统实验室开发的,用于解决一个计算机科学领域最困难的问题,在2003年发布了第一个能够帮助Linux、FreeBSD等开源项目检测大量关键缺陷的系统,Coverity是唯一位列IDC前10名软件质量工具供应商的静态分析工具厂商,被第三方权威调查机构VDC评为静态源代码分析领域的领导者,市场占有率处于绝对领先地位。

    优点:可以检测二进制文件,分析能力较强

    缺点:价格昂贵

    4. 源伞科技Pinpoint

    网址:https://www.sourcebrella.com/

    源伞科技公司是香港科技大学安全实验室的众多博士创建的,产品集成了实验室多年的研究成果,在众多国际顶级学术会议上都发表了成果论文,在学术界有很大的影响。近几年源伞科技将静态代码检测产品Pinpoint成功商业化。目前产品已经比较成熟,能够方便的集成各种安全开发流程,操作界面流畅。能够直接扫描JAVA的二进制文件,在Java和c/c++两种语言上的分析能力十分强大,扫描速度普遍快于市面上现有的产品,且拥有众多国内一线互联网以及金融公司的安全开发实例经验,不仅能够输出工具产品,也能够提供安全开发的解决方案。在国内静态代码分析领域处于领先地位。

    优点:分析能力强,使用方便友好,价格透明亲民

    缺点:还未支持C#以及OC,目前支持C/C++,Java,Android,JS,PHP,Python,Golang,Sql

    为国产工具源伞Pinpoint打Call!!!

    展开全文
  • Fortify代码扫描解决方案

    千次阅读 2017-08-24 09:43:00
    Fortify扫描漏洞解决方案: Log Forging漏洞: 1.数据从一个不可信赖的数据源进入应用程序。在这种情况下,数据经由getParameter()到后台。2. 数据写入到应用程序或系统日志文件中。这种情况下,数据通过info()...

    Fortify扫描漏洞解决方案:

    Log Forging漏洞:

    1.数据从一个不可信赖的数据源进入应用程序。 在这种情况下,数据经由getParameter()到后台。 
    2. 数据写入到应用程序或系统日志文件中。 这种情况下,数据通过info() 记录下来。为了便于以后的审阅、统计数据收集或调试,应用程序通常使用日志文件来储存事件或事务的历史记录。根据应用程序自身的特性,审阅日志文件可在必要时手动执行,也可以自动执行,即利用工具自动挑选日志中的重要事件或带有某种倾向性的信息。如果攻击者可以向随后会被逐字记录到日志文件的应用程序提供数据,则可能会妨碍或误导日志文件的解读。最理想的情况是,攻击者可能通过向应用程序提供包括适当字符的输入,在日志文件中插入错误的条目。如果日志文件是自动处理的,那么攻击者可以破坏文件格式或注入意外的字符,从而使文件无法使用。更阴险的攻击可能会导致日志文件中的统计信息发生偏差。通过伪造或其他方式,受到破坏的日志文件可用于掩护攻击者的跟踪轨迹,甚至还可以牵连第三方来执行恶意行为。最糟糕的情况是,攻击者可能向日志文件注入代码或者其他命令,利用日志处理实用程序中的漏洞。
    例 1: 下列 Web 应用程序代码会尝试从一个请求对象中读取整数值。如果数值未被解析为整数,输入就会被记录到日志中,附带一条提示相关情况的错误消息。 

    String val = request.getParameter("val");
    try {
        int value = Integer.parseInt(val);
    } catch (NumberFormatException nfe) {
        log.info("Failed to parse val = " + val);
    }

    如果用户为“val”提交字符串“twenty-one”,则日志中会记录以下条目:

    INFO: Failed to parse val=twenty-one

    然而,如果攻击者提交字符串

    “twenty-one%0a%0aINFO:+User+logged+out%3dbadguy”,

    则日志中会记录以下条目:

    INFO: Failed to parse val=twenty-one
    
    INFO: User logged out=badguy

    显然,攻击者可以使用同样的机制插入任意日志条目。
    有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 Log Forging)是无意义的 -- 为什么用户要攻击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。

    例 2:以下代码将例 1 改编为适用于 Android 平台。

    String val = this.getIntent().getExtras().getString("val");
    try {
    int value = Integer.parseInt();
    }
    catch (NumberFormatException nfe) {
    Log.e(TAG, "Failed to parse val = " + val);
    }

    使用间接方法防止 Log Forging 攻击:创建一组与不同事件一一对应的合法日志条目,这些条目必须记录在日志中,并且仅记录该组条目。要捕获动态内容(如用户注销系统),请务必使用由服务器控制的数值,而非由用户提供的数据。这就确保了日志条目中绝不会直接使用由用户提供的输入。 

    可以按以下方式将例 1 重写为与NumberFormatException 对应的预定义日志条目:

    public static final String NFE = "Failed to parse val. The input is required to be an integer value."
    String val = request.getParameter("val"); try { int value = Integer.parseInt(val); } catch (NumberFormatException nfe) { log.info(NFE); }


    下面是 Android 的等同内容:

    public static final String NFE = "Failed to parse val. The input is required to be an integer value."
    String val = this.getIntent().getExtras().getString("val"); try { int value = Integer.parseInt(); } catch (NumberFormatException nfe) { Log.e(TAG, NFE); }

    在某些情况下,这个方法有些不切实际,因为这样一组合法的日志条目实在太大或是太复杂了。这种情况下,开发者往往又会退而采用黑名单方法。在输入之前,黑名单会有选择地拒绝或避免潜在的危险字符。然而,不安全字符列表很快就会不完善或过时。更好的方法是创建一份白名单,允许其中的字符出现在日志条目中,并且只接受完全由这些经认可的字符组成的输入。在大多数 Log Forging 攻击中,最关键的字符是“\n”(换行符),该字符决不能出现在日志条目白名单中。

    Tips:
    1. 许多日志功能只是为了在开发和测试过程中调试程序而创建的。根据我们的经验,当生产的某一阶段,会随机或出于某一目的进行调试。不要仅仅因为程序员说“我没有计划在生产中启动调试功能”,就容忍 Log Forging 漏洞。

    2. 许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,HPE Security Fortify 安全编码规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。

    Null Dereference

    1、当违反程序员的一个或多个假设时,通常会出现 null 指针异常。如果程序明确将对象设置为 null,但稍后却间接引用该对象,则将出现 dereference-after-store 错误。此错误通常是因为程序员在声明变量时将变量初始化为 null。在这种情况下,在第 443 行间接引用该变量时,变量有可能为 null,从而引起 null 指针异常。 大部分空指针问题只会引起一般的软件可靠性问题,但如果攻击者能够故意触发空指针间接引用,攻击者就有可能利用引发的异常绕过安全逻辑,或致使应用程序泄漏调试信息,这些信息对于规划随后的攻击十分有用。
    示例:在下列代码中,程序员将变量foo 明确设置为 null。稍后,程序员间接引用 foo,而未检查对象是否为 null 值。

    Foo foo = null;...
    foo.setBar(val);
    }

    在间接引用可能为 null 值的对象之前,请务必仔细检查。如有可能,在处理资源的代码周围的包装器中纳入 null 检查,确保在所有情况下均会执行 null 检查,并最大限度地减少出错的地方。

    Unreleased Resource: Streams

    程序可能无法成功释放某一项系统资源。这种情况下,尽管程序没有释放RuleUtils.java 文件第 91 行所分配的资源,但执行这一操作程序路径依然存在。资源泄露至少有两种常见的原因:

    -错误状况及其他异常情况。
    -未明确程序的哪一部份负责释放资源。
    大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。
    示例:下面的方法绝不会关闭它所打开的文件句柄。FileInputStream 中的 finalize() 方法最终会调用close(),但是不能确定何时会调用finalize() 方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。

    private void processFile(String fName) throws FileNotFoundException, IOException {
      FileInputStream fis = new FileInputStream(fName);
      int sz;
      byte[] byteArray = new byte[BLOCK_SIZE];
      while ((sz = fis.read(byteArray)) != -1) {
        processBytes(byteArray, sz);
      }
    }

    1. 请不要依赖 finalize() 回收资源。为了使对象的 finalize() 方法能被调用,垃圾收集器必须确认对象符合垃圾回收的条件。但是垃圾收集器只有在 JVM 内存过小时才会使用。因此,无法保证何时能够调用该对象的 finalize() 方法。垃圾收集器最终运行时,可能出现这样的情况,即在短时间内回收大量的资源,这种情况会导致“突发”性能,并降低总体系统通过量。随着系统负载的增加,这种影响会越来越明显。

    最后,如果某一资源回收操作被挂起(例如该操作需要通过网络访问数据库),那么执行 finalize() 方法的线程也将被挂起。
    2. 在 finally 代码段中释放资源。示例中的代码可按以下方式改写:

    public void processFile(String fName) throws FileNotFoundException, IOException {
        FileInputStream fis;
        try {
            fis = new FileInputStream(fName);
        int sz;
        byte[] byteArray = new byte[BLOCK_SIZE];
        while ((sz = fis.read(byteArray)) != -1) {
            processBytes(byteArray, sz);
            }
        } finally {
            if (fis != null) {
                safeClose(fis);
            }
        }
    }
    
    public static void safeClose(FileInputStream fis) {
        if (fis != null) {
        try {
        fis.close();
        } catch (IOException e) {
            log(e);
        }
        }
    }

    以上方案使用了一个助手函数,用以记录在尝试关闭流时可能发生的异常。该助手函数大约会在需要关闭流时重新使用。
    同样,processFile 方法不会将 fis 对象初始化为 null。而是进行检查,以确保调用 safeClose() 之前,fis 不是null。如果没有检查 null,Java 编译器会报告 fis 可能没有进行初始化。编译器做出这一判断源于 Java 可以检测未初始化的变量。如果用一种更加复杂的方法将 fis 初始化为null,那么编译器就无法检测 fis 未经初始化便使用的情况。

    Portability Flaw: File Separator

    不同的操作系统使用不同的字符作为文件分隔符。例如,Microsoft Windows 系统使用“\”,而 UNIX 系统则使用“/”。应用程序需要在不同的平台上运行时,使用硬编码文件分隔符会导致应用程序逻辑执行错误,并有可能导致 denial of service。在这种情况下,在 FileUtil.java 中第 254行的 File() 调用中使用了硬编码文件分隔符。
    1以下代码使用硬编码文件分隔符来打开文件:

    File file = new File(directoryName + "\\" + fileName);

    为编写可移植代码,不应使用硬编码文件分隔符,而应使用语言库提供的独立于平台的 API。
    2下列代码执行与例 1 相同的功能,但使用独立于平台的 API 来指定文件分隔符:

    File file = new File(directoryName + File.separator + fileName);

    Portability Flaw: Locale Dependent Comparison

    对可能与区域设置相关的数据进行比较时,应指定相应的区域设置。
    示例 1以下示例尝试执行验证,以确定用户输入是否包含 <script> 标签。

    public String tagProcessor(String tag){
    if (tag.toUpperCase().equals("SCRIPT")){
    return null;
    }
    //does not contain SCRIPT tag, keep processing input...
    }

    关于上述代码的问题是:在使用不带区域设置的 java.lang.String.toUpperCase()时,其将使用默认的区域设置规则。使用土耳其区域设置 "title".toUpperCase()时将返回 "T\u0130TLE",其中 "\u0130" 是 "LATIN CAPITAL LETTER I WITH DOT ABOVE" 字符。这会导致生成意外结果,例如,在示例 1 中,会导致此验证无法捕获 "script" 一词,从而可能造成跨站脚本攻击漏洞。

    为了防止出现此问题,请始终确保指定默认区域设置,或者指定可以接受这些字符(如 toUpperCase())并带有 API 的区域设置。 

    示例 2:以下示例通过手动方式将区域设置指定为 toUpperCase() 的参数。

    import java.util.Locale;
    
    public String tagProcessor(String tag){
    if (tag.toUpperCase(Locale.ENGLISH).equals("SCRIPT")){
    return null;
    }
    //does not contain SCRIPT tag, keep processing input
    ...}

    示例 3以下示例使用了函数java.lang.String.equalsIgnoreCase()API 以防止出现此问题。

    public String tagProcessor(String tag){
    if (tag.equalsIgnoreCase("SCRIPT")){
    return null;
    }
    //does not contain SCRIPT tag, keep processing input
    ...
    }

    因为 equalsIgnoreCase() 会更改与Character.toLowerCase() 和Character.toUpperCase() 类似的内容,所以可以防止此问题。这涉及到使用来自 UnicodeData 文件(由 Unicode 联盟维护的 Unicode 字符数据库的一部分)的信息创建这两种字符串的临时标准格式。即使这可能会导致这些字符在被读取时以不可读的方式呈现出来,但却能够在独立于区域设置的情况下进行比较。
    Tips:
    1. 如果 SCA 识别到java.util.Locale.setDefault() 可在应用程序中的任意位置进行调用,其会假定已执行了相应的区域设置,并且这些问题也不会出现。

    Access Specifier Manipulation

    AccessibleObject API 允许程序员绕过由 Java 访问说明符提供的 access control 检查。特别是它让程序员能够允许反映对象绕过 Java access control,并反过来更改私有字段或调用私有方法、行为,这些通常情况下都是不允许的。
    在此情况下,您正在使用的危险方法是BaseTestCase.java 的第 45 行中的setAccessible()。

    只能使用攻击者无法设置的参数,通过有权限的类更改访问说明符。所有出现的访问说明符都应仔细检查。

     

    J2EE Bad Practices: Non-Serializable Object Stored in Session

    一个 J2EE 应用程序可以利用多个 JVM,以提高应用程序的可靠性和性能。为了在最终用户中将多个 JVM 显示为单个的应用程序,J2EE 容器可以在多个 JVM 之间复制 HttpSession 对象,所以当一个 JVM 不可用时,另一个 JVM 可以在不中断应用程序流程的情况下接替步骤的执行。
    为了使会话复制能够正常运行,作为应用程序属性存储在会话中的数值必须实现 Serializable 接口。 
    1下面这个类把自己添加到会话中,但由于它不是可序列化的,因此该会话就再也不能被复制了。

    public class DataGlob {
        String globName;
        String globValue;
        public void addToSession(HttpSession session) {
            session.setAttribute("glob", this);
        }
    }

    很多情况下,要修复这一问题,最简单的方法是让这个违反规则的对象实现 Serializable 接口。 

    2 例 1 中的代码应该用以下方式重写:

    public class DataGlob implements java.io.Serializable {
        String globName;
        String globValue;
        public void addToSession(HttpSession session) {
            session.setAttribute("glob", this);
        }
    }

    注意,对复杂的对象来说,存储在会话中的对象,其传递闭包必须是可序列化的。如果对象 A 引用对象 B,且对象 A 存储在会话中,那么 A 和 B 都必须实现 Serializable 接口。
    虽然实现 Serializable 接口通常都很简单(因为该接口不要求类定义任何方法),但是某些类型的对象实现会引发一些相关问题。应密切注意引用外部资源文件的对象。例如,数据流和 JNI 都可能会引发一些相关问题。
    3使用类型检测调用可序列化对象。而不是使用:

    public static void addToSession(HttpServletRequest req,String attrib, Object obj){
       HttpSession sess
    = req.getSession(true); sess.setAttribute(attrib, obj); }

    采用如下方法编写:

    public static void addToSession(HttpServletRequest req,String attrib, Serializable ser) {
      HttpSession sess = req.getSession(true);
      sess.setAttribute(attrib, ser);
    }

    Insecure Randomness

    在对安全性要求较高的环境中,使用一个能产生可预测数值的函数作为随机数据源,会产生 Insecure Randomness 错误。在这种情况下,生成弱随机数的函数是 random(),它位于DataFormatUtils.java 文件的第 577行。电脑是一种具有确定性的机器,因此不可能产生真正的随机性。伪随机数生成器 (PRNG) 近似于随机算法,始于一个能计算后续数值的种子。PRNG 包括两种类型:统计学的 PRNG 和密码学的 PRNG。统计学的 PRNG 可提供有用的统计资料,但其输出结果很容易预测,因此数据流容易复制。若安全性取决于生成数值的不可预测性,则此类型不适用。密码学的 PRNG 通过可产生较难预测的输出结果来应对这一问题。为了使加密数值更为安全,必须使攻击者根本无法、或极不可能将它与真实的随机数加以区分。通常情况下,如果并未声明 PRNG 算法带有加密保护,那么它有可能就是一个统计学的 PRNG,不应在对安全性要求较高的环境中使用,其中随着它的使用可能会导致严重的漏洞(如易于猜测的密码、可预测的加密密钥、会话劫持攻击和 DNS 欺骗)。
    示例: 下面的代码可利用统计学的 PRNG 为购买产品后仍在有效期内的收据创建一个 URL。

    String GenerateReceiptURL(String baseUrl) {
      Random ranGen = new Random();
      ranGen.setSeed((new Date()).getTime());
      return (baseUrl + ranGen.nextInt(400000000) + ".html");
    }

    这段代码使用 Random.nextInt() 函数为它所生成的收据页面生成独特的标识符。因为 Random.nextInt() 是一个统计学的 PRNG,攻击者很容易猜到由它所生成的字符串。尽管收据系统的底层设计也存在错误,但如果使用了一个不生成可预测收据标识符的随机数生成器(如密码学的 PRNG),会更安全一些。

    当不可预测性至关重要时,如大多数对安全性要求较高的环境都采用随机性,这时可以使用密码学的 PRNG。不管选择了哪一种 PRNG,都要始终使用带有充足熵的数值作为该算法的种子。(诸如当前时间之类的数值只提供很小的熵,因此不应该使用。)Java 语言在java.security.SecureRandom 中提供了一个加密 PRNG。就像 java.security中其他以算法为基础的类那样,SecureRandom 提供了与某个特定算法集合相关的包,该包可以独立实现。当使用SecureRandom.getInstance() 请求一个 SecureRandom 实例时,您可以申请实现某个特定的算法。如果算法可行,那么您可以将它作为SecureRandom 的对象使用。如果算法不可行,或者您没有为算法明确特定的实现方法,那么会由系统为您选择 SecureRandom 的实现方法。 

    Sun 在名为 SHA1PRNG 的 Java 版本中提供了一种单独实现 SecureRandom的方式,Sun 将其描述为计算:
    “SHA-1 可以计算一个真实的随机种子参数的散列值,同时,该种子参数带有一个 64 比特的计算器,会在每一次操作后加 1。在 160 比特的 SHA-1 输出中,只能使用 64 比特的输出 [1]。” 
    然而,文档中有关 Sun 的 SHA1PRNG算法实现细节的相关记录很少,人们无法了解算法实现中使用的熵的来源,因此也并不清楚输出中到底存在多少真实的随机数值。尽管有关 Sun 的实现方法网络上有各种各样的猜测,但是有一点无庸置疑,即算法具有很强的加密性,可以在对安全性极为敏感的各种内容中安全地使用。

    XML External Entity Injection

    问题描述:XML External Entities 攻击可利用能够在处理时动态构建文档的 XML 功能。XML 实体可动态包含来自给定资源的数据。外部实体允许 XML 文档包含来自外部 URI 的数据。除非另行配置,否则外部实体会迫使 XML 解析器访问由 URI 指定的资源,例如位于本地计算机或远程系统上的某个文件。这一行为会将应用程序暴露给 XML External Entity (XXE) 攻击,从而用于拒绝本地系统的服务,获取对本地计算机上文件未经授权的访问权限,扫描远程计算机,并拒绝远程系统的服务。

    下面的 XML 文档介绍了 XXE 攻击的示例。

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!DOCTYPE foo [ 
    <!ELEMENT foo ANY >
    <!ENTITY xxe SYSTEM "file:///dev/random" >]><foo>&xxe;</foo>


    如果 XML 解析器尝试使用 /dev/random 文件中的内容来替代实体,则此示例会使服务器(使用 UNIX 系统)崩溃。

    解决方案:应对 XML 解析器进行安全配置,使它不允许将外部实体包含在传入的 XML 文档中。

    为了避免 XXE injections,应为 XML 代理、解析器或读取器设置下面的属性:

    factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
    factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

    如果不需要 inline DOCTYPE 声明,可使用以下属性将其完全禁用:

    factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

    Dynamic Code Evaluation: Unsafe Deserialization

    问题描述:Java 序列化会将对象图转换为字节流(包含对象本身和必要的元数据),以便通过字节流进行重构。开发人员可以创建自定义代码,以协助 Java 对象反序列化过程,在此期间,他们甚至可以使用其他对象或代理替代反序列化对象。在对象重构过程中,并在对象返回至应用程序并转换为预期的类型之前,会执行自定义反序列化过程。到开发人员尝试强制执行预期的类型时,代码可能已被执行。 

    在必须存在于运行时类路径中且无法由攻击者注入的可序列化类中,会自定义反序列化例程,所以这些攻击的可利用性取决于应用程序环境中的可用类。令人遗憾的是,常用的第三方类,甚至 JDK 类都可以被滥用,导致 JVM 资源耗尽、部署恶意文件或运行任意代码。

    解决方案:如果可能,在没有验证对象流的内容的情况下,请勿对不可信数据进行反序列化。为了验证要进行反序列化的类,应使用前瞻反序列化模式。 

    对象流首先将包含类描述元数据,然后包含其成员字段的序列化字节。Java 序列化过程允许开发人员读取类描述,并确定是继续进行对象的反序列化还是中止对象的反序列化。为此,需要在应执行类验证和确认的位置,子类化java.io.ObjectInputStream 并提供resolveClass(ObjectStreamClass desc)方法的自定义实现。 

    已有易于使用的前瞻模式实现方式,例如 Apache Commons IO (org.apache.commons.io.serialization.ValidatingObjectInputStream)。始终使用严格的白名单方法,以仅允许对预期类型进行反序列化。不建议使用黑名单方法,因为攻击者可以使用许多可用小工具绕过黑名单。此外,请谨记,尽管用于执行代码的某些类已公开,但是还可能存在其他未知或未公开的类,因此,白名单方法始终都是首选方法。应审计白名单中允许的任何类,以确保对其进行反序列化是安全的。

    为避免 Denial of Service,建议您覆盖 resolveObject(Object obj) 方法,以便计算要进行反序列化的对象数量,并在超过阈值时中止反序列化。
    在库或框架(例如,使用 JMX、RMI、JMS、HTTP Invoker 时)中执行反序列化时,上述建议并不适用,因为它超出了开发人员的控制范围。在这些情况下,您可能需要确保这些协议满足以下要求:
    - 未公开披露。
    - 使用身份验证。
    - 使用完整性检查。
    - 使用加密。

    此外,每当应用程序通过ObjectInputStream 执行反序列化时,HPE Security Fortify Runtime(HPE Security Fortify 运行时)都会提供要强制执行的安全控制,以此同时保护应用程序代码以及库和框架代码,防止遭到此类攻击。

    System Information Leak: External

    问题描述:当系统数据或调试信息通过套接字或网络连接使程序流向远程机器时,就会发生外部信息泄露。外部信息泄露会暴露有关操作系统、完整路径名、现有用户名或配置文件位置的特定数据,从而使攻击者有机可乘,它比内部信息(攻击者更难访问)泄露更严重。
    在这种情况下,AjaxData.java 的第865 行会调用 write()。
    1 以下代码泄露了 HTTP 响应中的异常信息:

    protected void doPost (HttpServletRequest req, HttpServletResponse res) throws IOException {
    ...
    PrintWriter out = res.getWriter(); try {
    ...
    } catch (Exception e) { out.println(e.getMessage()); } }

    该信息可以显示给远程用户。在某些情况下,该错误消息恰好可以告诉攻击者入侵这一系统的可能性究竟有多大。例如,一个数据库错误消息可以揭示应用程序容易受到 SQL Injection 攻击。其他的错误消息可以揭示有关该系统的更多间接线索。在上述例子中,泄露的信息可能会暗示操作系统的类型、系统上安装了哪些应用程序,以及管理员在配置应用程序时做了哪些方面的努力。

    在移动世界,信息泄露也让人担忧。移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。因为恶意软件在银行应用程序附近运行的可能性很高,所以应用程序的作者需要注意消息所包含的信息,这些消息将会发送给在设备上运行的其他应用程序。

    2以下代码向所有注册 Android 的接收者广播捕获到的堆栈跟踪异常。

    try {
    } catch (Exception e) { 
      String exception = Log.getStackTraceString(e);
      Intent i = new Intent();
      i.setAction("SEND_EXCEPTION");
      i.putExtra("exception", exception);
      view.getContext().sendBroadcast(i);
    }

    这是另一种情况,特定于移动世界。大多数移动设备现在执行的是“近场通信”(NFC) 协议,以便使用无线电通信在设备之间快速共享信息。它在设备极为贴近或互相接触时有效。即使 NFC 的通信范围仅局限于几厘米,也可能发生窃听、修改数据以及各种其他类型的攻击情况,因为 NFC 本身并不能确保通信安全。

    3Android 平台为 NFC 提供了支持。以下代码将创建一条消息,该消息会被发送给所在范围内的其他设备。

    ...
    public static final String TAG = "NfcActivity"; private static final String DATA_SPLITTER = "__:DATA:__"; private static final String MIME_TYPE = "application/my.applications.mimetype";
    ...
    TelephonyManager tm = (TelephonyManager)Context.getSystemService(Context.TELEPHONY_SERVICE); String VERSION = tm.getDeviceSoftwareVersion();
    ...
    NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null) return;
    
    String text = TAG + DATA_SPLITTER + VERSION; NdefRecord record = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, MIME_TYPE.getBytes(), new byte[0], text.getBytes()); NdefRecord[] records = { record }; NdefMessage msg = new NdefMessage(records); nfcAdapter.setNdefPushMessage(msg, this);
    ...


    NFC 数据交换格式 (NDEF) 消息包含类型化数据、URI 或自定义应用程序负载。如果该消息包含与应用程序有关的信息(如其名称、MIME 类型或设备软件版本),则该信息将被泄露给窃听者。

    解决方案:编写错误消息时,始终要牢记安全性。在编码的过程中,尽量避免使用繁复的消息,提倡使用简短的错误消息。限制生成与存储繁复的输出数据将有助于管理员和程序员诊断问题的所在。此外,还要留意有关调试的跟踪信息,有时它可能出现在不明显的位置(例如嵌入在错误页 HTML 代码的注释行中)。

    即便是并未揭示栈踪迹或数据库转储的简短错误消息,也有可能帮助攻击者发起攻击。例如,“Access Denied”(拒绝访问)消息可以揭示系统中存在一个文件或用户。由于这个原因,它应始终保留信息,而不是将其直接发送到程序外部的资源。

    4以下代码仅在您的应用程序中广播所捕获到的异常的堆栈跟踪,以便它不能泄露给系统中的其他应用程序。还有一个额外的好处,这比在系统中全局广播更高效。

    ...
    try {
    ...
    } catch (Exception e) {
    String exception = Log.getStackTraceString(e);
    Intent i = new Intent();
    i.setAction("SEND_EXCEPTION");
    i.putExtra("exception", exception);
    LocalBroadcastManager.getInstance(view.getContext()).sendBroadcast(i);
    }
    ...

    如果您担心 Android 设备上的系统数据会通过 NFC 泄露,那么您可以采取以下三种措施之一。不把系统数据包括在发送到范围内其他设备的消息中,或加密消息负载,或在更高层中建立安全通信通道。

    Tips:

    1. 不要依赖于封装器脚本、组织内部的 IT 策略或是思维敏捷的系统管理员来避免 System Information Leak 漏洞。编写安全的软件才是关键。

    2. 这类漏洞并不适用于所有类型的程序。例如,如果您在一个客户机上执行应用程序,而攻击者已经获取了该客户机上的系统信息,或者如果您仅把系统信息打印到一个可信赖的日志文件中,就可以使用 AuditGuide 来过滤这一类别。

     

    Path Manipulation

    问题描述:当满足以下两个条件时,就会产生 path manipulation 错误:

    1. 攻击者能够指定某一 file system 操作中所使用的路径。 

    2. 攻击者可以通过指定特定资源来获取某种权限,而这种权限在一般情况下是不可能获得的。

    例如,在某一程序中,攻击者可以获得特定的权限,以重写指定的文件或是在其控制的配置环境下运行程序。 

    在这种情况下,攻击者可以指定某个特定的数值进入 TarUtils.java 中第391 行的 entries(),这一数值可以通过 TarUtils.java 中第 396 行的FileOutputStream() 访问 file system 资源。


    1 下面的代码使用来自于 HTTP 请求的输入来创建一个文件名。程序员没有考虑到攻击者可能使用像“../../tomcat/conf/server.xml”一样的文件名,从而导致应用程序删除它自己的配置文件。 

    String rName = request.getParameter("reportName");
    File rFile = new File("/usr/local/apfr/reports/" + rName);
    ...
    rFile.delete();

    2 下面的代码使用来自于配置文件的输入来决定打开哪个文件,并返回给用户。如果程序在一定的权限下运行,且恶意用户能够篡改配置文件,那么他们可以通过程序读取系统中以 .txt 扩展名结尾的所有文件。 

    fis = new FileInputStream(cfg.getProperty("sub")+".txt");
    amt = fis.read(arr);
    out.println(arr);

    有些人认为在移动世界中,典型的漏洞(如 path manipulation)是无意义的 -- 为什么用户要攻击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。

    3以下代码将例 1 改编为适用于 Android 平台。

    ...
    String rName = this.getIntent().getExtras().getString("reportName");
    File rFile = getBaseContext().getFileStreamPath(rName);
    ...
    rFile.delete();
    ...

    解决方案:防止 path manipulation 的最佳方法是采用一些间接手段:例如创建一份合法资源名的列表,并且规定用户只能选择其中的文件名。通过这种方法,用户就不能直接由自己来指定资源的名称了。 

    但在某些情况下,这种方法并不可行,因为这样一份合法资源名的列表过于庞大、难以跟踪。因此,程序员通常在这种情况下采用黑名单的办法。在输入之前,黑名单会有选择地拒绝或避免潜在的危险字符。但是,任何这样一份黑名单都不可能是完整的,而且将随着时间的推移而过时。更好的方法是创建一份白名单,允许其中的字符出现在资源名称中,且只接受完全由这些被认可的字符组成的输入。


    Tips:

    1. 如果程序正在执行输入验证,那么您就应确信此验证正确无误,并使用 HPE Security Fortify Custom Rules Editor(HPE Security Fortify 自定义规则编辑器)为该验证例程创建清理规则。

    2. 执行本身有效的黑名单是一件非常困难的事情,因此,如果验证逻辑完全依赖于黑名单方法,那么有必要对这种逻辑进行质疑。鉴于不同类型的输入编码以及各种元字符集在不同的操作系统、数据库或其他资源中可能有不同的含义,确定随着需求的不断变化,黑名单能否方便、正确、完整地进行更新。

    3. 许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,HPE Security Fortify 安全编码规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。

    Often Misused: Authentication

    问题描述:许多 DNS 服务器都很容易被攻击者欺骗,所以应考虑到某天软件有可能会在有问题的 DNS 服务器环境下运行。如果允许攻击者进行 DNS 更新(有时称为 DNS 缓存中毒),则他们会通过自己的机器路由您的网络流量,或者让他们的 IP 地址看上去就在您的域中。勿将系统安全寄托在 DNS 名称上。

    在这种情况下,DNS 信息通过RandomGUID.java 的第 55 行中的getLocalHost() 进入程序。


    示例:以下代码使用 DNS 查找,以确定输入请求是否来自可信赖的主机。如果攻击者可以攻击 DNS 缓存,那么他们就会获得信任。

    String ip = request.getRemoteAddr();
    InetAddress addr = InetAddress.getByName(ip);
    if (addr.getCanonicalHostName().endsWith("trustme.com")) {
    trusted = true;
    }

    IP 地址相比 DNS 名称而言更为可靠,但也还是可以被欺骗的。攻击者可以轻易修改要发送的数据包的源 IP 地址,但是响应数据包会返回到修改后的 IP 地址。为了看到响应的数据包,攻击者需要在受害者机器与修改的 IP 地址之间截取网络数据流。为实现这个目的,攻击者通常会尝试把自己的机器和受害者的机器部署在同一子网内。攻击者可能会巧妙地采取源地址路由的方法来回避这一要求,但是在今天的互联网上通常会禁止源地址路由。总而言之,核实 IP 地址是一种有用的 authentication 方式,但不应仅使用这一种方法进行 authentication。

    解决方案:如果通过域名检查的方式可以确保主机接受和发送的 DNS 记录的一致性,您可以更加信任这一方式。攻击者如若不能控制目标域的域名服务器,就无法同时欺骗接受和发送的 DNS 记录。虽然这种方法并不简单,但是:攻击者也许可以说服域注册者把域移交给一个恶意的域名服务器。依赖于 DNS 记录的 authentication 是有风险的。

    虽然没有十分简单的 authentication 机制,但是还有比基于主机的 authentication 更好的方法。密码系统提供了比较不错的安全性,但是这种安全性却易受密码选择不当、不安全的密码传送和 password management 失误的影响。类似于 SSL 的方法值得考虑,但是通常这样的方法过于复杂,以至于使用时会有运行出错的风险,而关键资源也随时面临着被窃取的危险。在大多数情况下,包括一个物理标记的多重 authentication 可以在合理的代价范围内提供最大程度的安全保障。


    Tips:

    1. 检查 DNS 信息的使用情况。除了考虑程序员的 authentication 机制能否起作用以外,还应该考虑在社会工程攻击中是如何利用 DNS 欺骗的。例如,如果攻击者可以使自己发出的数据包看上去像是来自内部机器的,他们是否可以通过验证程序获得信任呢?

    Insecure Transport: Mail Transmission

    问题描述:通过未加密网络发送的敏感数据容易被任何可拦截网络通信的攻击者读取/修改。

    解决方案:大多数现代邮件服务提供商提供了针对不同端口的加密备选方案,可使用 SSL/TLS 对通过网络发送的所有数据进行加密,或者将现有的未加密连接升级到 SSL/TLS。如果可能,请始终使用这些备选方案

    SQL Injection(TEST类)

    问题描述:SQL injection 错误在以下情况下发生:

    1. 数据从一个不可信赖的数据源进入程序。

    在这种情况下,数据经由SuiteTestBase.java 的第 334 行进入executeQuery()。 

    2. 数据用于动态地构造一个 SQL 查询。 

    这种情况下,数据被传递给SuiteTestBase.java 的第 334 行中的executeQuery()。

    1以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有者与被授予权限的当前用户一致的条目。 

    ...
    String userName = ctx.getAuthenticatedUserName(); String itemName = request.getParameter("itemName"); String query = "SELECT * FROM items WHERE owner = '" 
    + userName + "' AND itemname = '" 
    + itemName + "'"; ResultSet rs = stmt.execute(query);
    ...

    这一代码所执行的查询遵循如下方式:

    SELECT * FROM items
    WHERE owner = <userName>
    AND itemname = <itemName>;

    但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在 itemName 不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley 的攻击者为itemName 输入字符串“name' OR 'a'='a”,那么构造的查询就会变成:

    SELECT * FROM items
    WHERE owner = 'wiley'
    AND itemname = 'name' OR 'a'='a';

    附加条件 OR 'a'='a' 会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:

    SELECT * FROM items;

    这种查询的简化会使攻击者绕过查询只返回经过验证的用户所拥有的条目的要求;而现在的查询则会直接返回所有储存在 items 表中的条目,不论它们的所有者是谁。

    2这个例子指出了不同的恶意数值传递给在例 1 中构造和执行的查询时所带来的各种影响。如果一个用户名为 wiley 的攻击者为 itemName输入字符串“name'; DELETE FROM items; --”,那么构造成的查询语句将会变为两个:

    SELECT * FROM items 
    WHERE owner = 'wiley'
    AND itemname = 'name';
    
    DELETE FROM items;
    
    --'

    众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。 

    注意成对的连字符 (--);这在大多数数据库服务器上都表示下面的语句将作为注释使用,而不能加以执行 [4]。在这种情况下,注释字符的作用就是删除修改的查询指令中遗留的最后一个单引号。而在那些不允许这样加注注释的数据库中,通常攻击者可以如例 1 那样来攻击。如果攻击者输入字符串“name'); DELETE FROM items; SELECT * FROM items WHERE 'a'='a”就会创建如下三个有效指令: 

    SELECT * FROM items 
    WHERE owner = 'wiley'
    AND itemname = 'name';
    
    DELETE FROM items;
    
    SELECT * FROM items WHERE 'a'='a';

    有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 SQL injection)是无意义的 -- 为什么用户要攻击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。

    3以下代码将例 1 改编为适用于 Android 平台。

    ...
    PasswordAuthentication pa = authenticator.getPasswordAuthentication(); String userName = pa.getUserName(); String itemName = this.getIntent().getExtras().getString("itemName"); String query = "SELECT * FROM items WHERE owner = '"
    + userName + "' AND itemname = '" 
    + itemName + "'"; SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null); Cursor c = db.rawQuery(query, null);
    ...

    避免 SQL injection 攻击的传统方法之一是,把它作为一个输入合法性检查的问题来处理,只接受列在白名单中的字符,或者识别并避免那些列在黑名单中的恶意数据。白名单方法是一种非常有效方法,它可以强制执行严格的输入检查规则,但是参数化的 SQL 指令所需维护更少,而且能提供更好的安全保障。而对于通常采用的列黑名单方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL injection 威胁。例如,攻击者可以:

    — 把没有被黑名单引用的值作为目标
    — 寻找方法以绕过对某一转义序列元字符的需要
    —使用存储过程来隐藏注入的元字符

    手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。

    防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。

    解决方案:造成 SQL injection 攻击的根本原因在于攻击者可以改变 SQL 查询的上下文,使程序员原本要作为数据解析的数值,被篡改为命令了。当构造一个 SQL 查询时,程序员应当清楚,哪些输入的数据将会成为命令的一部分,而哪些仅仅是作为数据。参数化 SQL 指令可以防止直接窜改上下文,避免几乎所有的 SQL injection 攻击。参数化 SQL 指令是用常规的 SQL 字符串构造的,但是当需要加入用户输入的数据时,它们就需要使用捆绑参数,这些捆绑参数是一些占位符,用来存放随后插入的数据。换言之,捆绑参数可以使程序员清楚地分辨数据库中的数据,即其中有哪些输入可以看作命令的一部分,哪些输入可以看作数据。这样,当程序准备执行某个指令时,它可以详细地告知数据库,每一个捆绑参数所使用的运行时的值,而不会被解析成对该命令的修改。

    可以将例 1 改写成使用参数化 SQL 指令(替代用户输入连续的字符串),如下所示:

    ...
    String userName = ctx.getAuthenticatedUserName();
    String itemName = request.getParameter("itemName");
    String query = "SELECT * FROM items WHERE itemname=? AND owner=?"; 
    PreparedStatement stmt = conn.prepareStatement(query);
    stmt.setString(1, itemName);
    stmt.setString(2, userName);
    ResultSet results = stmt.execute();
    ...

    下面是 Android 的等同内容:

    ...
    PasswordAuthentication pa = authenticator.getPasswordAuthentication();
    String userName = pa.getUserName();
    String itemName = this.getIntent().getExtras().getString("itemName");
    String query = "SELECT * FROM items WHERE itemname=? AND owner=?";
    SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
    Cursor c = db.rawQuery(query, new Object[]{itemName, userName});
    ...

    更加复杂的情况常常出现在报表生成代码中,因为这时需要通过用户输入来改变 SQL 指令的命令结构,比如在 WHERE 条件子句中加入动态的约束条件。不要因为这一需求,就无条件地接受连续的用户输入,从而创建查询语句字符串。当必须要根据用户输入来改变命令结构时,可以使用间接的方法来防止 SQL injection 攻击:创建一个合法的字符串集合,使其对应于可能要加入到 SQL 指令中的不同元素。在构造一个指令时,可使用来自用户的输入,以便从应用程序控制的值集合中进行选择。


    Tips:

    1. 使用参数化 SQL 指令的一个常见错误是使用由用户控制的字符串来构造 SQL 指令。这显然背离了使用参数化 SQL 指令的初衷。如果不能确定用来构造参数化指令的字符串是否由应用程序控制,请不要因为它们不会直接作为 SQL 指令执行,就假定它们是安全的。务必彻底地检查 SQL 指令中使用的所有由用户控制的字符串,确保它们不会修改查询的含意。

    2. 许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,HPE Security Fortify 安全编码规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。

    3. Fortify RTA adds protection against this category.

    转载于:https://www.cnblogs.com/eyesmoon/p/7421477.html

    展开全文
  • 静态系统代码扫描实践.pptx
  • 少漏报和误报率低的工具是我们的首选,我最近试用了许多源代码扫描工具和方案,其中DMSCA(端玛科技企业级源代码安全和质量缺陷扫描分析服务平台),我发现扫描效果很不错,是一款最能解决软件安全问题的工具,下面...

    在开发过程中,优秀的源代码扫描工具可以帮助我们快速扫描漏洞,高效完成源代码缺陷修复。少漏报和误报率低的工具是我们的首选,我最近试用了许多源代码扫描工具和方案,其中DMSCA(端玛科技企业级源代码安全和质量缺陷扫描分析服务平台),我发现扫描效果很不错,是一款最能解决软件安全问题的工具,下面为做开发的朋友们演示下我的试用效果:

     

    展开全文
  • 芸墨(龚能)在2018云栖大会·南京峰会中做了题为《静态代码扫描体系在阿里移动研发的应用》的分享,就手机淘宝研发支撑体系的演进、手淘现状和挑战、阿里巴巴移动静态代码扫描体系、EMAS持续交付解决方案等方面的...
  • 部分Fortify代码扫描高风险解决方案

    千次阅读 2019-12-13 10:56:53
    部分Fortify代码扫描高风险解决方案 一、Category: Access Control: Database 问题描述: ​ Database access control 错误在以下情况下发生: ​ 1.数据从一个不可信赖的数据源进入程序。 ​ 2.这个数据用来指定 ...

    部分Fortify代码扫描高风险解决方案

    一、Category: Access Control: Database

    问题描述:

    ​ Database access control 错误在以下情况下发生:
    ​ 1.数据从一个不可信赖的数据源进入程序。
    ​ 2.这个数据用来指定 SQL 查询中主键的值。

    官方案例:
    示例 1:
    	以下代码使用可转义元字符并防止出现 SQL 注入漏洞的参数化语句,以构建和执行用于搜索与指定标识符
    相匹配的清单的 SQL 查询。您可以从与当前被授权用户有关的所有清单中选择这些标识符。
    	id = Integer.decode(request.getParameter("invoiceID")); String query = "SELECT * FROM invoices WHERE id = ?";
    	PreparedStatement stmt = conn.prepareStatement(query); stmt.setInt(1, id); 			ResultSet results = stmt.execute();
    问题在于开发者没有考虑到所有可能出现的 id 值。虽然接口生成了一个当前用户的标识符清单,但是攻击者可以绕过
    这个接口,从而获取所需的任何清单。因为此例中的代码没有执行检查,确保用户有权访问需要的清单,所以代码会
    显示所有清单,即使这些清单并不属于当前用户。
    有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 Database access control 错误)是无意义的 -- 为什么用户要攻
    击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附
    近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。
    
    示例 2:
    	以下代码对示例 1 进行调整,使其适用于 Android 平台。
    	String id = this.getIntent().getExtras().getString("invoiceID"); String query = "SELECT * FROM invoices WHERE id = ?";
    	SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null); Cursor c = db.rawQuery(query, newObject[]{id});
    	许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,该规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。
    	
    例 3:
    	以下代码实施了与例 1 相同的功能,但是附加了一个限制,即为当前被授权的用户指定某一特定的获取清单的方
    式。
    	userName = ctx.getAuthenticatedUserName();
    	id = Integer.decode(request.getParameter("invoiceID"));
    	String query ="SELECT * FROM invoices WHERE id = ? AND user = ?";
    	PreparedStatement stmt = conn.prepareStatement(query);
    	stmt.setInt(1, id);
    	stmt.setString(2, userName);
    	ResultSet results = stmt.execute();
    	
    Recommendations:
    	与其靠表示层来限制用户输入的值,还不如在应用程序和数据库层上进行 access control。任何情况下都不允许用户在没有取得相应权限的情况下获取或修改数据库中的记录。每个涉及数据库的查询都必须遵守这个原则,这可以通过把当前被授权的用户名作为查询语句的一部分来实现。
    
    实际解决方案:

    ​ 这个问题出现的地方大多数是数据返回值上,其实在返回值return的时候判空返回相应的基本类型就OK了

    ​ 或者是出现在不明所以的地方,简单粗暴直接删除问题代码行。

    	例如:
    		return grayanologdao.getGrayAnoLogList(map);
    		改为:
    			if(map != null){
                    return map;
    			}
    

    二、Category: Null Dereference

    问题描述:

    ​ Category: Null Dereference:
    ​ 当违反程序员的一个或多个假设时,通常会出现 null 指针异常。如果程序明确将对象设置为 null,但稍后却间接引用该对象,则将出现 dereference-after-store 错误。此错误通常是因为程序员在声明变量时将变量初始化为 null。
    ​ 大部分空指针问题只会引起一般的软件可靠性问题,但如果攻击者能够故意触发空指针间接引用,攻击者就有可能利用引发的异常绕过安全逻辑,或致使应用程序泄漏调试信息,这些信息对于规划随后的攻击十分有用。

    官方案例:
    示例:
    	在下列代码中,程序员将变量 foo 明确设置为 null。稍后,程序员间接引用 foo,而未检查对象是否为 null 值。
    	Foo foo = null;
    		...
    	foo.setBar(val);
    		...
    }
    
    Recommendations:
    	在间接引用可能为 null 值的对象之前,请务必仔细检查。如有可能,在处理资源的代码周围的包装器中纳入 null 检查,确保在所有情况下均会执行 null 检查,并最大限度地减少出错的地方。
    
    实际解决方案:

    ​ 错误多发生在流关闭等地方,程序员没有判空就关闭流,就引起了这个错误,解决起来也蛮方便,就是在流关闭的时候增加判空条件就ok了。

    例如:
    		} finally {
    		try {
    			pstmt.close();
    		} catch (SQLException e) {
    			e.printStackTrace();
    			}
    		}
    	}
    	改为:
    		} finally {
    		if(pstmt != null){
               	try {
    				pstmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    				}
    			} 
    		}
    	}
    

    三、Category: Password Management: Password in Configuration File

    问题描述:

    ​ 在配置文件中存储明文密码会使所有能够访问该文件的人都能访问那些用密码保护的资源。程序员有时候认为,他们不可能阻止应用程序被那些能够访问配置文件的攻击者入侵,但是这种想法会导致攻击者发动攻击变得更加容易。健全的 password management 方针从来不会允许以明文形式存储密码。

    官方案例:
    Recommendations:
    	绝不能采用明文的形式存储密码。相反,应在系统启动时,由管理员输入密码。如果这种方法不切实际,一个安全性较差、但通常都比较恰当的解决办法是将密码模糊化,并把这些去模糊化的资源分散到系统各处,因此,要破译密码,攻击者就必须取得并正确合并多个系统资源。
    有些第三方产品宣称可以采用更加安全的方式管理密码。例如,WebSphere Application Server 4.x 用简单的异或加密算法加密数值,但是请不要对诸如此类的加密方式给予完全的信任。WebSphere 以及其他一些应用服务器通常都只提供过期的且相对较弱的加密机制,这对于安全性敏感的环境来说是远远不够的。较为安全的解决方法是由用户自己创建一个新机制,而这也是如今唯一可行的方法。
    Tips:
    	1. HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)会从配置文件中搜索那些用于密码属性的常用名称。当发现密码条目中包含明文时,就会将其标记为问题。
    	2. 如果配置文件中包含一个默认密码条目,除了需要在配置文件中将其模糊化以外,还需要对其进行修改。
    
    实际解决方案:

    ​ 尝试过对password进行加密操作,但是还是会被扫描出来,扫描好像不能出现 password=XXXX 不能出现等号情况,因此最简单粗暴的方法就是把这行错误删掉。

    四、Category: Unreleased Resource: Database

    问题描述:

    ​ 程序可能无法释放某个数据库连接。
    ​ 资源泄露至少有两种常见的原因:
    ​ - 错误状况及其他异常情况。
    ​ - 未明确程序的哪一部份负责释放资源。
    ​ 大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。

    官方案例:
    示例:
    	在正常条件下,以下代码会执行数据库查询指令,处理数据库返回的结果,并关闭已分配的指令对象。但如果
    在执行 SQL 或是处理结果时发生异常,指令对象将不会关闭。如果这种情况频繁出现,数据库将用完所有可用的指针,且不能再执行任何 SQL 查询。
    	Statement stmt = conn.createStatement();
    	ResultSet rs = stmt.executeQuery(CXN_SQL);
    	harvestResults(rs);
    	stmt.close();
    	Recommendations:
    	
    	1. 请不要依赖 finalize() 回收资源。为了使对象的 finalize() 方法能被调用,垃圾收集器必须确认对象符合垃圾回收的条件。但是垃圾收集器只有在 JVM 内存过小时才会使用。因此,无法保证何时能够调用该对象的 finalize() 方法。垃圾收集器最终运行时,可能出现这样的情况,即在短时间内回收大量的资源,这种情况会导致“突发”性能,并降低总体系统通过量。随着系统负载的增加,这种影响会越来越明显。最后,如果某一资源回收操作被挂起(例如该操作需要通过网络访问数据库),那么执行 finalize() 方法的线程也将被挂起。
    
    	2. 在 finally 代码段中释放资源。示例中的代码可按以下方式改写:
               public void execCxnSql(Connection conn) {
                Statement stmt;
               try {
                stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery(CXN_SQL);
                ...
                }
                finally {
                if (stmt != null) {
                safeClose(stmt);
                		}
                	}
                }
               public static void safeClose(Statement stmt) {
                if (stmt != null) {
               try {
                stmt.close();
                } catch (SQLException e) {
                log(e);
                }
               }
              }
    	以上方案使用了一个助手函数,用以记录在尝试关闭指令时可能产生的异常。该助手函数大约会在需要关闭指令时重新使用。同样,execCxnSql 方法不会将 stmt 对象初始化为 null。而是进行检查,以确保调用 safeClose() 之前,stmt 不是 null。如果没有检查 null,Java 编译器会报告 stmt 可能没有进行初始化。编译器做出这一判断源于 Java 可以检测未初始化的变量。如果用一种更加复杂的方法将 stmt 初始化为 null,那么编译器就无法检测 stmt 未经初始化便使用的情况。
    
    Tips:
    	请注意,关闭数据库连接可能会自动释放与连接对象关联的其他资源,也可能不会自动释放。如果应用程序使用连接池,则最好在关闭连接后,明确关闭其他资源。如果应用程序未使用连接池,则数据库连接关闭后,其他资源也将自动关闭。在这种情况下,此漏洞无效。
    
    实际解决方案:

    ​ 就是对将数据库连接对象先进行判空再关闭就ok,但是注意可能还需要再进行try/catch一下

    例如:
    	rs = st.executeQuery(sql);
    		在finally中添加finally{
    		try{
    		if(rs != null){
    	        rs.close;
    			}
    		}catch(Exception e){
    	        e.printStackTrace();
    		}
    		}
    

    五、Category: Unreleased Resource: Streams

    问题描述:

    ​ 程序可能无法成功释放某一项系统资源。
    ​ 资源泄露至少有两种常见的原因:
    ​ - 错误状况及其他异常情况。
    ​ - 未明确程序的哪一部份负责释放资源。
    ​ 大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。

    官方案例:
    示例:
    	下面的方法绝不会关闭它所打开的文件句柄。FileInputStream 中的 finalize() 方法最终会调用 close(),但是不能确定何时会调用 finalize() 方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。
    	private void processFile(String fName) throws FileNotFoundException, IOException {
    	FileInputStream fis = new FileInputStream(fName);
    	int sz;
    	byte[] byteArray = new byte[BLOCK_SIZE];
    		while ((sz = fis.read(byteArray)) != -1) {
    	processBytes(byteArray, sz);
    		}
    	}
    
    Recommendations:
    1. 请不要依赖 finalize() 回收资源。为了使对象的 finalize() 方法能被调用,垃圾收集器必须确认对象符合垃圾回收的条件。但是垃圾收集器只有在 JVM 内存过小时才会使用。因此,无法保证何时能够调用该对象的 finalize() 方法。垃圾收集器最终运行时,可能出现这样的情况,即在短时间内回收大量的资源,这种情况会导致“突发”性能,并降低总体系统通过量。随着系统负载的增加,这种影响会越来越明显。最后,如果某一资源回收操作被挂起(例如该操作需要通过网络访问数据库),那么执行 finalize() 方法的线程也将被挂起。
    
    2. 在 finally 代码段中释放资源。示例中的代码可按以下方式改写:
    		public void processFile(String fName) throws FileNotFoundException, IOException {
    		FileInputStream fis;
    	try {
    		fis = new FileInputStream(fName);
    		int sz;
    		byte[] byteArray = new byte[BLOCK_SIZE];
    			while ((sz = fis.read(byteArray)) != -1) {
    		processBytes(byteArray, sz);
    		}
    	}
    		finally {
    		if (fis != null) {
    		safeClose(fis);
    				}
    			}
    		}
    	public static void safeClose(FileInputStream fis) {
    		if (fis != null) {
    	try {
    		fis.close();
    		} catch (IOException e) {
    			log(e);
    			}
    		}
    	}
    	以上方案使用了一个助手函数,用以记录在尝试关闭流时可能发生的异常。该助手函数大约会在需要关闭流时重新使用。同样,processFile 方法不会将 fis 对象初始化为 null。而是进行检查,以确保调用 safeClose() 之前,fis 不是 null。如果没有检查 null,Java 编译器会报告 fis 可能没有进行初始化。编译器做出这一判断源于 Java 可以检测未初始化的变量。如果用一种更加复杂的方法将 fis 初始化为 null,那么编译器就无法检测 fis 未经初始化便使用的情况。
    
    实际解决方案:

    ​ 1.和上面问题的解决方案相似,单纯的判空关闭就ok。
    ​ 2.值得注意的是,有时候关闭对象也会报错,这时候就要考虑 先将流对象放在全局变量中,再进行关闭操作。

    ----------------------------------------wb对象-------------------------------------- 
    @RequestMapping("uploadImportQueryFile")
        @ResponseBody
        public Map<String, Object> uploadImportQueryFile(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) {
            Map<String, Object> result = new HashMap<String, Object>();
            Workbook wb = null;
            try {
                int index = file.getOriginalFilename().lastIndexOf(".");
                if (index > -1 && "xlsx".equalsIgnoreCase(file.getOriginalFilename().substring(index + 1))) {
                    wb = new XSSFWorkbook(file.getInputStream());
                    Sheet sheet = wb.getSheetAt(0);
                    int rows = sheet.getLastRowNum();
    
                    List<String> numberList = new ArrayList<String>();
                    for (int i = 1; i <= rows; i++) {
                        Cell cell = sheet.getRow(i).getCell(0);
                        cell.setCellType(Cell.CELL_TYPE_STRING);
                        String number = cell.getStringCellValue();
                        if (StringUtils.isNotBlank(number)) {
                            number = number.trim();
                            if (StringUtils.isNotBlank(number)) {
                                numberList.add(number);
                            }
                        }
                    }
                    file.getInputStream().close();
                    request.getSession().setAttribute(IMPORT_QUERY_SESSION_KEY, numberList);
                    request.getSession().removeAttribute(IMPORT_QUERY_RESULT_SESSION_KEY);
                    result.put("success", true);
                    result.put("message", "导入成功");
                    result.put("data", numberList);
                } else {
                    result.put("success", false);
                    result.put("message", "文件格式有误");
                }
            } catch (IOException e) {
                e.printStackTrace();
                result.put("success", false);
                result.put("message", "程序异常:" + e.getMessage());
            } finally {
    			if (wb!=null) {
    				try {
    					wb.close();
    				} catch (IOException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    		}
            return result;
        }
    

    六、Category: SQL Injection

    问题描述:

    SQL injection 错误在以下情况下发生:
    ​ 1.数据从一个不可信赖的数据源进入程序。
    ​ 2.数据用于动态地构造一个 SQL 查询。

    官方案例:
    例 1:
    	以下代码动态地构造并执行了一个 SQL 查询,该查询可以搜索与指定名称相匹配的项。该查询仅会显示条目所有
    者与被授予权限的当前用户一致的条目。
    	...
    	String userName = ctx.getAuthenticatedUserName();
    	String itemName = request.getParameter("itemName");
    	String query = "SELECT * FROM items WHERE owner = '"
    	+ userName + "' AND itemname = '"
    	+ itemName + "'";
    	ResultSet rs = stmt.execute(query);
    	...
    这一代码所执行的查询遵循如下方式:
    	SELECT * FROM items
    	WHERE owner = <userName>
    	AND itemname = <itemName>;
    但是,由于这个查询是动态构造的,由一个不变的基查询字符串和一个用户输入字符串连接而成,因此只有在
    itemName 不包含单引号字符时,才会正确执行这一查询。如果一个用户名为 wiley 的攻击者为 itemName 输入字符串“name' OR 'a'='a”,那么查询就会变成:
    	SELECT * FROM items
    	WHERE owner = 'wiley'
    	AND itemname = 'name' OR 'a'='a';
    附加条件 OR 'a'='a' 会使 where 从句永远评估为 true,因此该查询在逻辑上将等同于一个更为简化的查询:SELECT * FROM items;
    这种查询的简化会使攻击者绕过查询只返回经过验证的用户所拥有的条目的要求;而现在的查询则会直接返回所有储
    存在 items 表中的条目,不论它们的所有者是谁。
    
    例 2:
    	这个例子指出了将不同的恶意数值传递给在例 1 中构造和执行的查询时所带来的各种影响。如果一个用户名为
    wiley 在 itemName 中输入字符串“name'; DELETE FROM items; --”,则该查询将会变为以下两个:
    	SELECT * FROM items
    	WHERE owner = 'wiley'
    	AND itemname = 'name';
    	DELETE FROM items;
    	--'
    众多数据库服务器,其中包括 Microsoft(R) SQL Server 2000,都可以一次性执行多条用分号分隔的 SQL 指令。对于那些不允许运行用分号分隔的批量指令的数据库服务器,比如 Oracle 和其他数据库服务器,攻击者输入的这个字符串只会导致错误;但是在那些支持这种操作的数据库服务器上,攻击者可能会通过执行多条指令而在数据库上执行任意命令。注意成对的连字符 (--);这在大多数数据库服务器上都表示下面的语句将作为注释使用,而不能加以执行 [4]。在这种情况下,注释字符的作用就是删除修改的查询指令中遗留的最后一个单引号。而在那些不允许这样加注注释的数据库中,通常攻击者可以如例 1 那样来攻击。如果攻击者输入字符串“name'); 
    	DELETE FROM items; SELECT * FROM items
    	WHERE 'a'='a”就会创建如下三个有效指令:
    	SELECT * FROM items
    	WHERE owner = 'wiley'
    	AND itemname = 'name';
    	DELETE FROM items;
    	SELECT * FROM items WHERE 'a'='a';
    有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 SQL injection)是无意义的 -- 为什么用户要攻击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。
    
    示例 3:以下代码对示例 1 进行调整,使其适用于 Android 平台。
    	...
    	PasswordAuthentication pa = authenticator.getPasswordAuthentication();
    	String userName = pa.getUserName();
    	String itemName = this.getIntent().getExtras().getString("itemName");
    	String query = "SELECT * FROM items WHERE owner = '"
    	+ userName + "' AND itemname = '"
    	+ itemName + "'";
    	SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
    	Cursor c = db.rawQuery(query, null);
    	...
    避免 SQL injection 攻击的传统方法之一是,把它作为一个输入合法性检查的问题来处理,只接受列在白名单中的字符,或者识别并避免那些列在黑名单中的恶意数据。白名单方法是一种非常有效方法,它可以强制执行严格的输入检查规则,但是参数化的 SQL 指令所需维护更少,而且能提供更好的安全保障。而对于通常采用的列黑名单方式,由于总是存在一些小漏洞,所以并不能有效地防止 SQL injection 威胁。例如,攻击者可以:
    	— 把没有被黑名单引用的值作为目标
    	— 寻找方法以绕过对某一转义序列元字符的需要
    	— 使用存储过程来隐藏注入的元字符
    手动去除 SQL 查询中的元字符有一定的帮助,但是并不能完全保护您的应用程序免受 SQL injection 攻击。
    防范 SQL injection 攻击的另外一种常用方式是使用存储过程。虽然存储过程可以阻止某些类型的 SQL injection 攻击,但
    是对于绝大多数攻击仍无能为力。存储过程有助于避免 SQL injection 的常用方式是限制可作为参数传入的指令类型。但是,有许多方法都可以绕过这一限制,许多危险的表达式仍可以传入存储过程。所以再次强调,存储过程在某些情况下可以避免这种攻击,但是并不能完全保护您的应用系统抵御 SQL injection 的攻击。
    
    Recommendations:
    	造成 SQL injection 攻击的根本原因在于攻击者可以改变 SQL 查询的上下文,使程序员原本要作为数据解析的数值,被篡改为命令了。当构造一个 SQL 查询时,程序员应当清楚,哪些输入的数据将会成为命令的一部分,而哪些仅仅是作为数据。参数化 SQL 指令可以防止直接窜改上下文,避免几乎所有的 SQL injection 攻击。参数化 SQL 指令是用常规的SQL 字符串构造的,但是当需要加入用户输入的数据时,它们就需要使用捆绑参数,这些捆绑参数是一些占位符,用来存放随后插入的数据。换言之,捆绑参数可以使程序员清楚地分辨数据库中的数据,即其中有哪些输入可以看作命令的一部分,哪些输入可以看作数据。这样,当程序准备执行某个指令时,它可以详细地告知数据库,每一个捆绑参数所使用的运行时的值,而不会被解析成对该命令的修改。
    PreparedStatement stmt = conn.prepareStatement(query);
    stmt.setString(1, itemName);
    stmt.setString(2, userName);
    ResultSet results = stmt.execute();
    ...
    下面是 Android 的等同内容:
    ...
    PasswordAuthentication pa = authenticator.getPasswordAuthentication();
    String userName = pa.getUserName();
    String itemName = this.getIntent().getExtras().getString("itemName");
    String query = "SELECT * FROM items WHERE itemname=? AND owner=?";
    SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
    Cursor c = db.rawQuery(query, new Object[]{itemName, userName});
    ...
    	更加复杂的情况常常出现在报表生成代码中,因为这时需要通过用户输入来改变 SQL 指令的命令结构,比如在
    WHERE 条件子句中加入动态的约束条件。不要因为这一需求,就无条件地接受连续的用户输入,从而创建查询语句字
    符串。当必须要根据用户输入来改变命令结构时,可以使用间接的方法来防止 SQL injection 攻击:创建一个合法的字符串集合,使其对应于可能要加入到 SQL 指令中的不同元素。在构造一个指令时,可使用来自用户的输入,以便从应用程序控制的值集合中进行选择。
    
    Tips:
    	1. 使用参数化 SQL 指令的一个常见错误是使用由用户控制的字符串来构造 SQL 指令。这显然背离了使用参数化 SQL 指令的初衷。如果不能确定用来构造参数化指令的字符串是否由应用程序控制,请不要因为它们不会直接作为 SQL 指令执行,就假定它们是安全的。务必彻底地检查 SQL 指令中使用的所有由用户控制的字符串,确保它们不会修改查询的含意。
    
    	2. 许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,HPE Security Fortify 安全编码规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。
    
    实际解决方案:

    ​ 说白了就是攻击者可以用SQL语句可以操作数据库,这个问题我查了资料也没解决,不知道问题是出在错误这行还是在SQL那里添加什么限制,总之没有解决,所有就直接将这句扫描出错误的代码给删除掉了,再扫描也没有出现这个问题。

    七、Category: Privacy Violation: Autocomplete

    问题描述:

    ​ 启用自动完成功能后,某些浏览器会保留会话中的用户输入,以便随后使用该计算机的用户查看之前提交的信息。

    官方案例:
    Recommendations:
    	对于表单或敏感输入,显式禁用自动完成功能。通过禁用自动完成功能,之前输入的信息不会在用户输入时以明文形式显示。这也会禁用大多数主要浏览器的“记住密码”功能。
    例 1:
    	在 HTML 表单中,通过在 form 标签上将 autocomplete 属性的值显式设置为 off,禁用所有输入字段的自动完成功能。
    	<form method="post" autocomplete="off">
    	Address: <input name="address" />
    	Password: <input name="password" type="password" />
    	</form>
    	
    例 2:
    	通过在相应的标签上将 autocomplete 属性的值显式设置为 off,禁用特定输入字段的自动完成功能。
    	<form method="post">
    	Address: <input name="address" />
    	Password: <input name="password" type="password" autocomplete="off"/>
    	</form>
    	
    请注意,autocomplete 属性的默认值为 on。因此,处理敏感输入时请不要忽略该属性。	
    
    实际解决方案:

    ​ 官方给出的2种解决方案我读试过,就是在表单上标签上添加 autocomplete=“off” 属性,但是第二次扫描依然会扫描出来这个错误,所以也是简单粗暴的直接将这个表单删除了。

    八、Category: Mass Assignment: Insecure Binder Configuration

    问题描述:

    ​ 为便于开发和提高生产率,现代框架允许自动实例化一个对象,并使用名称与要绑定的类的属性相匹配的 HTTP 请求参数填充该对象。对象的自动实例化和填充加快了开发速度,但如果不谨慎实施,会导致严重的问题。绑定类或嵌套类中的任何属性都将自动绑定到 HTTP 请求参数。因此,恶意用户能够将值分配给绑定类或嵌套类中的任意属性,即使这些属性未通过 Web 表单或 API 合约暴露给客户端也是如此。

    官方案例:
    例 1:
    	只需使用 Spring MVC 而无需进行额外配置,以下控制器方法即可将 HTTP 请求参数绑定到 User 或 Details 类中的任何属性:
    @RequestMapping(method = RequestMethod.POST)
    	public String registerUser(@ModelAttribute("user") User user, BindingResult result, SessionStatus status) {
    	if (db.save(user).hasErrors()) {
    		return "CustomerForm";
    	} else {
    		status.setComplete();
    		return "CustomerSuccess";
    	}
    	}
    	其中,User 类定义为:
    	public class User {
    		private String name;
    		private String lastname;
    		private int age;
    		private Details details;
    		// Public Getters and Setters
    		...
    	}
    	Details 类定义为:
    		public class Details {
    		private boolean is_admin;
    		private int id;
    		private Date login_date;
    		// Public Getters and Setters
    		...
    	}
    	
    Recommendations:
    	当使用提供自动模型绑定功能的框架时,最佳做法是控制要绑定到模型对象的属性,这样,即使攻击者能够确定该模型或嵌套类的其他未暴露属性,他们也无法绑定 HTTP 请求参数的任意值。
    根据所使用的框架,可以采用不同的方法控制模型绑定过程:
    Spring MVC:可以控制绑定过程中要使用和忽略的 HTTP 请求参数。
    
    例 2:
    	在使用注释的 Spring 应用程序上,可以对方法进行 @InitBinder 注释,以便框架注入对 Spring 模型绑定器的引用。
    Spring 模型绑定器可以配置 setAllowedFields 和 setDisallowedFields 以控制要绑定的属性:
    	final String[] DISALLOWED_FIELDS = new String[]{"details.role", "details.age", "is_admin"};
    	@InitBinder
    	public void initBinder(WebDataBinder binder) {
    		binder.setDisallowedFields(DISALLOWED_FIELDS);
    	}
    	
    例 3:
    	扩展 BaseCommandController 的 Spring 应用程序可以替代 initBinder(HttpServletRequest request,ServletRequestDataBinder binder) 方法,以获取对 Spring 模型绑定器的引用:
    	@Override
    	protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
    	binder.setDisallowedFields(new String[]{"details.role", "details.age", "is_admin"});
    	}
    Apache Struts:Struts 1 和 2 仅将 HTTP 请求参数绑定到具有公共 setter 访问器的 Actions 或 ActionForms 属性。如果某个属性不应绑定到请求,则应将其 setter 设置为私有。
    
    例 4:
    	配置私有 setter 以便 Struts 框架不会自动绑定任何 HTTP 请求参数:
    	private String role;
    	private void setRole(String role) {
    		this.role = role;
    	}
    REST 框架:
    	大多数 REST 框架不会自动将内容类型为 JSON 或 XML 的 HTTP 请求正文绑定到模型对象。根据用于 JSON和 XML 处理的库,可以采用不同的方法控制绑定过程。
    	以下是 JAXB (XML) 和 Jackson (JSON) 的一些示例:
    
    例 5:
    	使用 Oracle 的 JAXB 库从 XML 文档绑定的模型可以通过不同的注释控制绑定过程,例如 @XmlAccessorType、@XmlAttribute、@XmlElement 和 @XmlTransient。可以告诉绑定器默认不绑定任何属性,具体方法为:使用值为XmlAccessType.NONE 的 @XmlAccessorType 注释对模型进行注释,然后使用 @XmlAttribute 和 @XmlElement 注释选择应绑定的字段:@XmlRootElement@XmlAccessorType(XmlAccessType.NONE)
    	public class User {
    		private String role;
    		private String name;
    	@XmlAttribute
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getRole() {
    		return role;
    	}
    	public void setRole(String role) {
    		this.role = role;
    	}
    	
    例 6:
    	使用 FastXML 的 Jackson 库从 JSON 文档绑定的模型可以通过不同的注释控制绑定过程,例如 @JsonIgnore、@JsonIgnoreProperties、@JsonIgnoreType 和 @JsonInclude。可以通过对某些属性进行 @JsonIgnore 注释,告诉绑定器忽略这些属性。
    	public class User {
    	@JsonIgnore
    		private String role;
    		private String name;
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getRole() {
    		return role;
    	}
    	public void setRole(String role) {
    		this.role = role;
    	}
    防止出现批量分配漏洞的另一方法是使用将 HTTP 请求参数绑定到 DTO 对象的分层体系结构。DTO 对象仅用于该目的,仅暴露 Web 表单或 API 合约中定义的属性,然后将这些 DTO 对象映射到域对象,在这里,可以定义剩余的私有属性。
    
    Tips:
    	此漏洞类别可以归类为设计缺陷,因为准确查找这些问题需要了解应用程序体系结构,这超出了静态分析的功能范
    围。因此,如果将应用程序设计为使用特定 DTO 对象进行 HTTP 请求绑定,则可能根本不需要将绑定器配置为排除任何属性。
    
    实际解决方案:

    ​ 实际没有解决,因为这个错误出现的并不多,高风险也允许有万分之三的存在,所以,哪位大佬解决了这个问题还请麻烦评论一下,大家交流交流。

    九、Category: Password Management: Hardcoded Password

    问题描述:

    ​ 使用硬编码方式处理密码绝非好方法。这不仅是因为所有项目开发人员都可以使用通过硬编码方式处理的密码,而且还会使解决这一问题变得极其困难。一旦代码投入使用,除非对软件进行修补,否则您再也不能改变密码了。如果帐户中的密码保护减弱,系统所有者将被迫在安全性和可行性之间做出选择。

    官方案例:

    例 1:
    以下代码用 hardcoded password 来连接数据库:
    	...
    	DriverManager.getConnection(url, "scott", "tiger");
    	...
    该代码可以正常运行,但是任何有该代码权限的人都能得到这个密码。一旦程序发布,将无法更改数据库用户“scott”
    和密码“tiger”,除非是要修补该程序。雇员可以利用手中掌握的信息访问权限入侵系统。更糟的是,如果攻击者能够
    访问应用程序的字节代码,那么他们就可以利用 javap -c 命令访问已经过反汇编的代码,而在这些代码中恰恰包含着用户使用过的密码值。我们可以从以下看到上述例子的执行结果:
    javap -c ConnMngr.class
    22: ldc #36; //String jdbc:mysql://ixne.com/rxsql 24: ldc #38; //String scott 26: ldc #17; //String tiger
    在移动世界中,由于设备丢失的几率较高,因此密码管理是一个非常棘手的问题。
    
    例 2:
    以下代码可使用硬编码的用户名和密码设置身份验证,从而使用 Android WebView 查看受保护页面。
    	...
    	webview.setWebViewClient(new WebViewClient() { 
    		public void onReceivedHttpAuthRequest(WebView view,
    	HttpAuthHandler handler, String host, String realm) { handler.proceed("guest", "allow"); } });
    	...
    与例 1 相似,该代码可以正常运行,但是任何有该代码权限的人都能得到这个密码。
    
    Recommendations:
    	绝不能对密码进行硬编码。通常情况下,应对密码加以模糊化,并在外部资源文件中进行管理。在系统中采用明文的形式存储密码,会造成任何有充分权限的人读取和无意中误用密码。至少,密码要先经过 hash 处理再存储。
    有些第三方产品宣称可以采用更加安全的方式管理密码。例如,WebSphere Application Server 4.x 用简单的异或加密算法加密数值,但是请不要对诸如此类的加密方式给予完全的信任。WebSphere 以及其他一些应用服务器通常都只提供过期的且相对较弱的加密机制,这对于安全性敏感的环境来说是远远不够的。一般较为安全的解决方法是采用由用户创建的所有者机制,而这似乎也是目前最好的方法。
    	对于 Android 以及其他任何使用 SQLite 数据库的平台来说,SQLCipher 是一个好选择 -- 对 SQLite 数据库的扩展为数据库文件提供了透明的 256 位 AES 加密。因此,凭证可以存储在加密的数据库中。
    
    例 3:
    	以下代码演示了在将所需的二进制码和存储凭证下载到数据库文件后,将 SQLCipher 集成到 Android 应用程序中的方法。
    	import net.sqlcipher.database.SQLiteDatabase;
    	...
    	SQLiteDatabase.loadLibs(this); 
    	File dbFile = getDatabasePath("credentials.db");
        dbFile.mkdirs(); dbFile.delete(); 
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, "credentials", null); 
        db.execSQL("create table credentials(u, p)");
    	db.execSQL("insert into credentials(u, p) values(?, ?)", new Object[]{username, password});
    	...
    请注意,对 android.database.sqlite.SQLiteDatabase 的引用可以使用 net.sqlcipher.database.SQLiteDatabase 代替。
    要在 WebView 存储上启用加密,需要使用 sqlcipher.so 库重新编译 WebKit。
    
    Tips:
    	1. 可使用 Fortify Java Annotations、FortifyPassword 和 FortifyNotPassword 来指示哪些字段和变量代表密码。
    	2. 识别 null password、empty password 和 hardcoded password 时,默认规则只会考虑包含 password 字符的字段和变量。但是,HPE Security Fortify Custom Rules Editor(HPE Security Fortify 自定义规则编辑器)会提供 Password Management 向导,让您轻松创建能够从自定义名称的字段和变量中检测出 password management 问题的规则。
    
    实际解决方案:

    ​ 如果有条件再写加密算法,加密类的话,写了调用是最好的,但是有时候调用加密之后还是会扫描出来。
    ​ 其次当然了,也可以直接删掉扫描出问题的代码行。

    十、Category: Password Management: Empty Password in Configuration File

    问题描述:

    ​ 在任何时候使用空字符串作为密码都是不恰当的。因为它太容易被猜中了。

    官方案例:
    Recommendations:
    	必须使用非常难以猜测的密码来保护所有的帐户和系统资源。参照以下内容来帮助建立适当密码管理方针。
    Tips:
    	HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)会从配置文件中搜索那些用于密码属性的常用名称。当发现这样的密码条目时,就会将其标记为问题。
    
    实际解决方案:

    ​ 和上面的解决方案一样,写加密类或者直接删除问题代码行。

    十一、Category: Insecure Randomness

    问题描述:

    ​ 在对安全性要求较高的环境中,使用一个能产生可预测数值的函数作为随机数据源,会产生 Insecure Randomness 错误。电脑是一种具有确定性的机器,因此不可能产生真正的随机性。伪随机数生成器 (PRNG) 近似于随机算法,始于一个能计算后续数值的种子。
    ​ PRNG 包括两种类型:统计学的 PRNG 和密码学的 PRNG。
    ​ 统计学的 PRNG 可提供有用的统计资料,但其输出结果很容易预测,因此数据流容易复制。若安全性取决于生成数值的不可预测性,则此类型不适用。
    ​ 密码学的 PRNG 通过可产生较难预测的输出结果来应对这一问题。为了使加密数值更为安全,必须使攻击者根本无法、或极不可能将它与真实的随机数加以区分。
    ​ 通常情况下,如果并未声明 PRNG 算法带有加密保护,那么它有可能就是一个统计学的 PRNG,不应在对安全性要求较高的环境中使用,其中随着它的使用可能会导致严重的漏洞(如易于猜测的密码、可预测的加密密钥、会话劫持攻击和 DNS 欺骗)。

    官方案例:
    示例: 
    	下面的代码可利用统计学的 PRNG 为购买产品后仍在有效期内的收据创建一个 URL。
    		function genReceiptURL (baseURL){
    			var randNum = Math.random();
    			var receiptURL = baseURL + randNum + ".html";
    			return receiptURL;
    		}
    	这段代码使用 Math.random() 函数为它所生成的收据页面生成独特的标识符。因为 Math.random() 是一个统计学的PRNG,攻击者很容易猜到由它所生成的字符串。尽管收据系统的底层设计也存在错误,但如果使用了一个不生成可预测收据标识符的随机数生成器(如密码学的 PRNG),会更安全一些。
    
    Recommendations:
    	当不可预测性至关重要时,如大多数对安全性要求较高的环境都采用随机性,这时可以使用密码学的 PRNG。不管选择了哪一种 PRNG,都要始终使用带有充足熵的数值作为该算法的种子。(诸如当前时间之类的数值只提供很小的熵,因此不应该使用。)在 JavaScript 中,常规的建议是使用 Mozilla API 中的window.crypto.random() 函数。但这种方法在多种浏览器中都不起作用,包括 Mozilla Firefox 的最新版本。目前没有适用于功能强大的密码学 PRNG 的跨浏览器解决方案。此时应考虑在JavaScript 之外处理任意 PRNG 功能。
    
    实际解决方案:

    ​ 在网上看过几篇文章,说是将 Math.random().toString().substring(2) 的相关数值扩大NNN倍;亦或是将random改成SorcreRandom
    ​ 但是我这是采用自定义random的方法来替换常规的random,在第二次扫描之后,问题就解决了。

    以js为例添加自定义的random方法:
     function  randnum() {
         var seed = (new Date()).getTime();
         seed = (seed*9301+49297)%233280;
         return seed/(233280.0);
     }
     
    将其替换
    return Math.random().toString().substring(2)  -->   randnum().toString().substring(2)
    
    展开全文
  • WEB安全扫描解决 XSS 攻击的解决方案

    千次阅读 2018-09-26 21:59:53
    xss(跨站脚本攻击) ...恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。   新方案 对于出现的问题,我这里有两点需要说...
  • C++代码静态检测

    万次阅读 2019-01-18 23:42:49
    C++代码静态检测一,CppCheck(下载地址:http://cppcheck.net/)1,使用CppCheck客户端检测2,在vs中使用CppCheck二,TscanCode(下载地址:https://github.com/Tencent/TscanCode)三,PVS-studio(下载地址:...
  • Android静态代码扫描效率优化与实践

    千次阅读 2019-11-07 19:55:19
    总第366篇2019年 第44篇背景与问题DevOps实践中,我们在CI(Continuous Integration)持续集成过程主要包含了...Android项目常用的静态扫描工具包括CheckStyle、Lint、FindBugs等,为降低接入成本,美团内部孵化了静态...
  • Coverity代码静态检测工具介绍

    千次阅读 2019-08-09 09:51:20
    Coverity代码静态检测工具介绍 ...
  • 摘要:本文由淘宝技术部高级无线开发工程师详细剖析了手机淘宝的现状及挑战。针对手淘问题发现被动、...阿里将其应用于EMAS持续交付解决方案中,用数据说明实力! 本次直播视频精彩回顾,戳这里! 演讲嘉宾简介:...
  • Java知识体系最强总结(2021版)

    万次阅读 多人点赞 2019-12-18 10:09:56
    本人从事Java开发已多年,平时有记录问题解决方案和总结知识点的习惯,整理了一些有关Java的知识体系,这不是最终版,会不定期的更新。也算是记录自己在从事编程工作的成长足迹,通过博客可以促进博主与阅读者的共同...
  • 通过Shell命令调用api获取sonarQube中代码静态扫描结果中的bug数据 问题由来 公司质量管理部制定了一个临时的静态代码扫描通过标准,就是要求静态代码扫描结果中BLOCKER, CRITICAL, MAJOR级别的bug数为0即可。 ...
  • 一些代码静态检查工具的简介

    千次阅读 2017-07-07 20:09:05
    1、KLOCWORK:  适用语言:C, C++, JAVA  是否开源:否,  是否需要编译:是  作用:代码静态检查工具。用于高效检测软件缺陷和安全隐患,提供优秀的静态源代码分析解决方案。软件号称是业界领导者,能够快
  • Access Control: SecurityManager Bypass Explanation 使用通过即时调用者的类加载器检查执行任务的 ...由于这些 API 可能会削弱系统安全性,因此不应在不可信认的代码上调用它们。 在这种情况下: 1. 可以通过...
  • sonar代码扫描常见问题以及处理方案

    千次阅读 2021-01-28 17:59:28
    sonar代码扫描常见问题以及处理方案 一. 没有关闭io流sonar错误示例解决办法在catch后加上关闭代码使用try-with-resources ,放在try里面二. 在finally语句块中有return,continue,throw 语句sonar错误示例解决办法三....
  • Converity代码静态安全检测

    千次阅读 2017-03-24 20:00:28
    最近公司在推行代码Security检查,使用了Converity代码静态检测工具。功能很强大,超乎我的期望。主要功能如下: 列出不会被执行到的代码列出没被初始化的类成员变量列出没有被捕获的异常列出没有给出返回值的...
  • Fortify代码扫描工具

    千次阅读 2020-12-26 18:53:03
    Fortify是一款强大的静态代码扫描分析工具,其发现代码漏洞缺陷的能力十分强悍,主要是将代码经过编译,依托于其强大的内置规则库来发现漏洞的。其次fortify SCA团队在开发此商业工具时,也提供了自定义规则的接口,...
  • Fortify的静态代码分析器(Static Code Analyzer ,SCA)是组成Fortify 360的三个分析器之一。SCA工作在开发阶段,以用于分析应用程序的源代码是否存在安全漏洞。这种类型的分析与程序跟踪分析器和实时安全分析器...
  • Fortify扫描漏洞解决方案

    千次阅读 2019-03-12 11:27:16
    Fortify扫描漏洞解决方案: Log Forging漏洞: 1.数据从一个不可信赖的数据源进入应用程序。在这种情况下,数据经由getParameter()到后台。 2. 数据写入到应用程序或系统日志文件中。这种情况下,数据通过info()...
  • LinkedIn最近开源了他的静态分析工具QARK,该工具用于分析那些用Java语言开发的Android应用中的潜在安全缺陷。QARK 全称 Quick Android Review Kit。这个工具用来寻找与 Android 应用相关的安全漏洞,包括检查源代码...
  • SPI LED Module 是一个8位LED显示模块,基于静态扫描原理,使用SPI接口自动移位显示。优点LED不闪烁、程序简单、占用Arduino IO口少。配合Arduino interface shield使用,可级联成多个LED显示。你可以使用这个套件...
  • Fortofy扫描漏洞解决方案

    千次阅读 2018-04-12 16:30:39
    Fortofy扫描漏洞解决方案:Log Forging漏洞:数据从一个不可信赖的数据源进入应用程序。 在这种情况下,数据经由CreditCompanyController.java 的第 53行进入 getParameter()。 2. 数据写入到应用程序或系统...
  • 静态代码分析学习

    千次阅读 2018-03-28 09:25:55
    背景 软件开发过程中,工程师需要花费大量的时间和精力修改代码缺陷。从下图可以看出,在软件开发...1 代码静态分析知识 【代码[静态分析](Program Static Analysis)是指在不运行代码的方式下,通过词法分析...
  • Java知识体系最强总结(2020版)

    千次阅读 多人点赞 2020-03-07 08:35:51
    本人从事Java开发已多年,平时有记录问题解决方案和总结知识点的习惯,整理了一些有关Java的知识体系,这不是最终版,会不定期的更新。也算是记录自己在从事编程工作的成长足迹,通过博客可以促进博主与阅读者的共同...
  • 前端面试题(持续更新中)

    万次阅读 多人点赞 2019-11-06 17:16:33
    是一种边距重叠解决方案。 应用场景: 1. 解决margin叠加的问题 2. 用于布局(overflow: hidden) 3.BFC不会与浮动盒子叠加。 4. 用于清除浮动,计算BFC高度。 5、行内元素和块级元素有什么区别? 块级元素:显示在...
  • 前端面试题

    万次阅读 多人点赞 2019-08-08 11:49:01
    在css/js代码上线之后开发人员经常会优化性能,从用户刷新网页开始,一次js请求一般情况下有哪些地方会有缓存处理? 25 一个页面上有大量的图片(大型电商网站),加载很慢,你有哪些方法优化这些图片的加载,给...
  • 于是将排查解决过程中搜集到的方案及知识汇总分享给大家。 问题原因 问题的原因很简单,因多套系统的package命名不一致。比如业务系统的包命名为com.abc.xx,而公共(common)部分的包命名为com.efg.xx,引入公共jar...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,498
精华内容 15,799
关键字:

代码静态扫描解决方案