转载

Java 安全编码审计,第 2 部分:平台,表达式,数值运算及其他

本文是 Java 安全编码审计的第二部分,主要针对 Java 平台安全、表达式、数值类型运算、面向对象、输入输出、异常处理、运行环境等内容,谈论是否适合审计,如何审计并提供整改建议。

基于 CERT 安全编码标准审计 Java 代码

平台安全审计

平台相关安全问题大都严重,力争审计并修复好。

表 1. Java 平台安全编程标准审计方法
标准审计复杂度严重性整改代价审计方法
SEC06J 人工
SEC07-J 工具
SECother 高/中 N/A

审计重点 SEC02-J 不要基于非受信源进行安全检查

审计方法:

工具 Fortify ( Juliet Test Case CWE470_Unsafe_Reflection 验证)。

整改方法:

深度复制。换句话说,不仅仅用 clone(),而是新生成类实例复制。

审计重点 SEC07-J 自定义 classLoader 时调用基类的 getPermissions()

审计方法:

工具 Findbugs 和 Fortify 都可以实现。

整改方法:

自定义 classLoader 时调用基类的 getPermissions()。

审计重点 SEC08-J 定义基于原生方法的封装器

审计方法 :

Fortify 工具可以实现 (Juliet Test Cast CWE111_Unsafe_JNI 支持)。

整改方法:

定义基于原生方法的封装器

审计点 SEC01-J 不要在特权代码 Java.security.AccessController .doPrivileged() 中使用污染变量

Java.security.AccessController 类的 doPrivileged() 方法停止在调用链上对权限的进一步检查,所以要加强对这个方法的安全措施。

审计方法:

人工对污染数据的定义很困难,因为污染数据可以是传播过来的。

静态分析工具 Fortify 和 Findbugs 都不支持。

整改方法:

如果用到污染变量,要先净化,因为它对污染变量特别敏感。问题是有效的净化方法不容易写。

审计点 SEC03-J 不要用非受信的 classLoader 装载受信的代码

审计困难,不建议开展。

审计点 SEC04-J 使用安全管理器保护敏感操作

审计方法 :

要求开发者的协助才能判断敏感操作,因此不建议审计。

整改方法:

使用 Java 类库,形如:

sm = System.getSecurityManager();

sm.checkSecurityAccess();

审计点 SEC05-J 不要用反射来增加类的可访问性

审计方法:

要求开发者的协助才能判断敏感操作。

整改方法:

使用 Java 类库。

表达式

表 2. Java 表达式编程标准审计方法
标准审计复杂度严重性整改代价审计方法
EXP00-J 人工
EXP02-J 工具
EXP03-J 工具
EXPother 高/中 N/A

审计重点 EXP00-J 不要忽略方法的返回值

有两种漏洞,一种是文件删除,一种是字符串替代。

文件操作时对返回值检查,可以处理可能初产生的错误。

字符串替代审计是协助确保开发者达成字符串替代的意图。

审计方法:

人工抽查。

工具 Fortify 无法测出 (Juliet Test Case CWE252_Unchecked_Return_Value 验证 )。

e.g [^=].replace()

整改方法:

将 original.replace(), 替换为 original=original.replace();

审计重点 EXP02-J 使用 Arrays.equals() 比较数组

审计方法:

工具 Fortify 可以检查。

整改方法:

使用 Arrays.equals() 替代 arr1.equals(arr2)。

审计重点 EXP03-J 不要用 == 或者!= 比较两个基础数据类型的值

基础数据类型是小写开头的 boolean, byte, char, short, int。

封装数据类型是大写开头的 Boolean, Byte, Char, Short, Int。

审计方法:

工具 Fortify 可以检查。

数值类型和运算

CERT 罗列数值类标准 一共 15 条。问题大都不严重,数学运算场景下时重点审计,其他场景因为检验成本高,并不建议审计。

审计重点 NUM00-J 检验和避免整数溢出

同理 NUM04-J 不要使用浮点数进行精确计算。

审计方法:

工具审计 Fortify。

整改方法:

用工具自动检测出运算中可能有整数溢出比较简单,但是判断这个潜在溢出是否是错误,不容易界定。可以用 Integer.MAX_VALUE, Integer.MIN_VALUE 进行先验后验判断,可以用 BigInteger 等手段强化代码,但是成本高,并不建议执行。

审计重点 NUM02-J 确保除数不为 0

审计方法:

人工审计,Fortify 工具不支持。

文本抽查 / 和 % 数学运算符。

整改方法:

先判断非 0,再执行/或%。

面向对象

CERT 一共 12 条编码标准。其中 OBJ05-J、OBJ09-J、OBJ11-J 问题严重,建议审计,但是审计复杂度高,静态工具不能支持,人工审计。 其他条编码标准,问题不严重,发生可能性不大,但是审计复杂度高,不建议审计。

表 3. Java 面向对象编程标准审计方法
标准审计复杂度严重性整改代价审计方法
OBJ05-J 人工
OBJ09-J 人工
OBJ11-J 人工
OBJother 高/中 N/A

审计重点 OBJ05-J 返回引用之前,防御性复制私有的可变类成员

人工审计,工具 Fortify 等不支持。

审计重点 OBJ11-J 小心处理构造函数抛出的异常

人工审计,工具 Fortify 等不支持。描述笼统,审计困难。

审计重点 OBJ09-J 比较类而不是类名称

严重性高修复成本低,所以建议重点审计。

审计方法:

人工审计,因为工具 Fortify 等不支持(Juliet Test Case CWE486_Compare_Classes_by_Name 验证)。

审计复杂度高,只能找出部分违规代码。

文本搜索 getClass().getName().equals(

整改方法:

直接用 getClass()==xx。

输入输出

CERT 一共 15 条文件相关编码标准,大部分问题不严重,发生可能性不大,但是审计复杂度高,不建议审计,比如 FIO06-J,FIO07-J,FIO09-J, FIO10-J, FIO11-J,FIO12。

可审计的是 FIO08-J 和 FIO13-J。

表 4. Java 输入输出编程标准审计方法
标准审计复杂度严重性整改代价审计方法
FIO08-J 人工
FIO13-J 工具
FIOother 高/中 N/A

审计重点 FIO08-J 对读取一个字符或者字节的方法,使用 int 类型的返回值

高风险严重问题,容易整改,所以重点审计。

审计方法:

工具 Fortify 可以捕获

整改方法:

确保用 int 返回值来和-1 比较

审计重点 FIO13-J 不要在受信边界之外记录敏感信息

审计方法:

工具检测 Fortify (Juliet Test Case CWE533_Info_Exposure_IDSver_Log 支持)。

整改方法:

不记录即可。

异常处理

Java 的异常捕获功能非常强大。异常行为,问题不严重,不容易出错。

基本原则是不要在异常处理中进行业务逻辑。不适合静态代码分析方法审计。

审计重点 ERR08-J 不要捕捉 NullPointerException 或任何它的基类

问题不严重,非常容易发生,审计容易整改难。考虑到现实软件工程中汗牛充栋的 NPE,建议可以不审计。如果要靶向该标准审计 Java 代码。

审计方法:

人工文本搜索 catch(NullPointerException *)

或者 catch(Exception *)

整改方法:

前者直接允许报告 NPE。

后者用具体化,不笼统的 xxExcpetion 替代。

运行环境

和运行环境相关的安全问题都比较严重,可是大都涉及到业务逻辑安全域界定和代码部署,不适合静态代码分析的方式审计。

表 5. Java 运行环境编程标准审计方法
标准审计复杂度严重性整改代价审计方法
ENV02-J 人工
ENV04-J 人工
ENVother 高/中 N/A

审计重点 ENV02-J 不要信任 System.getenv() 得到的环境变量的值

因为 System.getENV() 需要用和操作系统相关的关键字才能获得环境变量的值。当程序代码跨操作系统移植时,代码出错。比如不提倡使用 System.getE getenv NV(“UIDS”)。

“UIDS”是操作系统相关的关键字,不同操作系统会提供不同关键字,Linux 下是 UIDS,Windows 下 UIDSNAME。一旦跨平台移植,代码出问题。提倡使用 System.getProperty(“uIDS.name”)。“uIDS.name”是 JVM 保留的关键字,平台无关,使用安全。

审计方法:

文本搜索 System. Getenv。

整改方案:

使用 System.getProperty,注意相关参数的替换。

审计重点 ENV04-J 不要关闭字节码验证功能

问题严重,但是发生可能性不大,一般情况下,如果打包部署 EAR 应用包,应用服务器会进行相关检查。一个发生问题的场景是 Java 命令行执行,Java -noverify Hello。这条规则和 Java 代码无关,不适合静态代码审计。

审计方法:

人工审计,文本搜索软件项目工程下的可执行脚本文件,比如*.bat 文件,关键字形如:

Java –Xverify:none AppName

整改方法:

用 Java –Xverify:all AppName 替换 Java –Xverify:none AppName。

审计点 ENV00-J 只对特权代码签名

描述笼统晦涩,审计困难。

审计点 ENV01-J 把所有安全敏感的代码置于单独 jar 包,签名后封装

本规则和业务逻辑相关,需要开发者协助进行,对敏感代码 jar 包封缄。审计困难。

审计点 ENV03-J 不要赋予危险的权限组合

一般情况下,这条规则不适合静态代码审计。

审计点 ENV05-J 不要部署被远程监控的应用

一般情况下,这条规则不适合静态代码审计。

杂项,锁,并发, 方法,声明和初始化,线程,线程池,

CEW 还罗列了几个杂项,安全严重性低,审计复杂度高,整改代价大,不适合静态工具审计,不作为安全审计的重点。所以这些内容目前不在本文讨论范畴。


基于 CWE TOP 25 审计 Java 代码

CWE 常见缺陷列表 (Common Weakness Enumeration) 是 MITRE 公司 (一个非盈利机构) 的安全漏洞词典。该公司同时也是 CVE (Common Vulnerabilities and Exposures) 安全漏洞字典的提供者。国际影响力比较大。

CWE TOP25 罗列了危害性最大且频繁收到安全威胁的 25 类常见 Web 漏洞,Ref: http://cwe.mitre.org/top25/

CWE89,78,601,134,190,191 可以用 Juliet Test Case 检验支持 [7]。

CWEother,Juliet Test Case 不足以支持,可以参考 http://cwe.mitre.org/data/definitions/${ CWEother.}.html 提供的 Sample Code 检验。

Fortify 工具可以提供大部分的审计和修复建议。

表 6. 针对 CWE TOP 25 严重漏洞的 Java 代码审计方法
威胁等级CWE 号审计方法
1 CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') Fortity 工具审计
2 CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') Fortity 工具审计
3 CWE-120: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') Fortity 工具审计
4 CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') Fortity 工具审计
5 CWE-306: Missing Authentication for Critical Function Fortity 工具审计
6 CWE-862: Missing Authorization Fortity 工具审计
7 CWE-798: Use of Hard-coded Credentials Fortity 工具审计
8 CWE-311: Missing Encryption of Sensitive Data Fortity 工具审计
9 CWE-434: Unrestricted Upload of File with Dangerous Type Fortity 工具审计
10 CWE-807: Reliance on Untrusted Inputs in a Security Decision 审计复杂性大。
11 CWE-250: Execution with Unnecessary Privileges Fortity 工具审计
12 CWE-352: Cross-Site Request Forgery (CSRF) Fortity 工具审计
13 CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') 审计难度大,工具无法捕获
14 CWE-494: Download of Code Without Integrity Check Fortity 工具审计
15 CWE-863: Incorrect Authorization Fortity 工具审计
16 CWE-829: Inclusion of Functionality from Untrusted Control Sphere Fortity 工具审计
17 CWE-732: Incorrect Permission Assignment for Critical Resource Fortity 工具审计
18 CWE-676: Use of Potentially Dangerous Function Fortity 工具审计
19 CWE-327: Use of a Broken or Risky Cryptographic Algorithm Fortity 工具审计
20 CWE-131: Incorrect Calculation of Buffer Size Fortity 工具审计
21 CWE-307: Improper Restriction of Excessive Authentication Attempts Fortity 工具审计
22 CWE-601: URL Redirection to Untrusted Site ('Open Redirect') Fortity 工具审计
23 CWE-134: Uncontrolled Format String Fortity 工具审计
24 CWE-190: Integer Overflow or Wraparound Fortity 工具审计
25 CWE-759: Use of a One-Way Hash without a Salt Fortity 工具审计

总结

相对 C 语言来说,Java 语言编程比较安全。Java 没有显式的指针操作,对数组和字符串边界有自动检查机制。JVM 基于类 Java.lang.SecurityManager 设计了一个安全管理器,程序员可通过命令行操作它。但是,Java 语言和其他任何语言一样,有自身的脆弱性。所以对于 Java 代码的审计是必要的。

本文重点关注如何审计输入验证和系列化两块。Java 由于广泛应用于 Internet web 应用,更容易开放向攻击者。攻击者可以通过提交表单等方式,实施各种注入攻击。在 Java 程序中输入验证是 Web 防御的有效手段。因而对输入验证代码的审计非常关键。Java 序列化安全漏洞隐藏深,如果发生在基础平台影响大,所以需要重点审计。

安全审计专家审计 Java 编写的软件产品,或者 JEE 工程时,可以将本文作为桌面参考。

一般来说,审计可以进行两次,第一次给出整改方法和建议,第二次保证整改方法的实施。首先按照漏洞严重性,排查 CWE TOP 25 问题。然后,也可根据 CERT 标准,地毯式排查。静态源代码审计,尽可能先使用自动化分析工具,当一个规则不能由工具审计的时候,或者怀疑工具漏报误报的时候,必须采用人工审计的方式。


正文到此结束
Loading...