本文是 Java 安全编码审计的第二部分,主要针对 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 类库。
标准 | 审计复杂度 | 严重性 | 整改代价 | 审计方法 |
---|---|---|---|---|
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 问题严重,建议审计,但是审计复杂度高,静态工具不能支持,人工审计。 其他条编码标准,问题不严重,发生可能性不大,但是审计复杂度高,不建议审计。
标准 | 审计复杂度 | 严重性 | 整改代价 | 审计方法 |
---|---|---|---|---|
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。
标准 | 审计复杂度 | 严重性 | 整改代价 | 审计方法 |
---|---|---|---|---|
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 替代。
和运行环境相关的安全问题都比较严重,可是大都涉及到业务逻辑安全域界定和代码部署,不适合静态代码分析的方式审计。
标准 | 审计复杂度 | 严重性 | 整改代价 | 审计方法 |
---|---|---|---|---|
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 常见缺陷列表 (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 工具可以提供大部分的审计和修复建议。
威胁等级 | 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 标准,地毯式排查。静态源代码审计,尽可能先使用自动化分析工具,当一个规则不能由工具审计的时候,或者怀疑工具漏报误报的时候,必须采用人工审计的方式。