java语言源代码漏洞测试规范
Java语言源代码漏洞测试规范
去年帮一个团队做代码审计,扫了三十万行Java代码,码漏结果Runtime.exec()的洞测调用出现了十七次,其中九次直接拼接了外部参数。试规这不是语言源代特例。漏洞测试规范要是码漏不把这类“经典错误”钉死在检查清单里,工具跑再多遍也是洞测白搭。
规范第一条:别信“这行代码没问题”的试规直觉。Java的语言源代String不可变、PreparedStatement防注入,码漏这些常识反而容易让人放松。洞测我见过一个支付模块,试规用了PreparedStatement,语言源代但表名和排序字段是码漏String.format拼进去的——照样注入。测试时得盯着动态拼接点,洞测不管它外面包了多少层“安全”的壳。
public List<User> findUsers(String sortField) {
String sql = "SELECT * FROM users ORDER BY " + sortField;
return jdbcTemplate.query(sql, new UserRowMapper());
}
反序列化这块更隐蔽。ObjectInputStream的readObject()一调用,攻击者就能构造恶意序列化流。规范里得强制要求:凡是实现Serializable的类,要么加readObject白名单校验,要么干脆用JSON替代。去年某金融系统被绕过的漏洞,就是readObject里没过滤InvokerTransformer这类 gadget 链。
路径遍历测试不能只看../。Java的File类在Windows下对..\和..%5c的解析差异,加上getCanonicalPath()偶尔的“宽容”,能让一个看似校验过的路径逃逸。测试用例里得塞进..%2f、....//、甚至/proc/self/cwd/../../etc/passwd这种组合拳。我习惯用Path.normalize()后对比前缀,但规范里必须写明:校验前先解码,解码后再规范化。
有个细节容易被忽略:
File.createTempFile()的默认权限是rw-r--r--,在共享环境下等于把临时文件暴露给同机其他用户。测试规范里该加一条“检查临时文件权限是否被显式收紧”。
XML解析器是另一个重灾区。DocumentBuilderFactory默认允许外部实体,XXE漏洞一抓一个准。规范得强制要求:setFeature("http://apache.org/xml/features/disallow-doctype-decl", true),或者直接换SAXParserFactory并禁用DTD。但别只写“禁用DTD”——有些业务确实需要DTD,那就得测试XMLConstants.ACCESS_EXTERNAL_DTD是否设为空字符串。
日志注入算冷门但真实存在。用户输入\n没过滤,直接写进日志,攻击者能伪造日志行。测试时构造一个带换行符的用户名,看日志文件里是不是多出了假的“登录成功”记录。规范里提一句“对日志输出中的CR/LF进行转义”,比长篇大论有用。
最后说个反直觉的:equals()和hashCode()不一致导致的HashMap污染,也能变成DoS。攻击者构造大量哈希碰撞的键,把HashMap退化成链表。Java 8之后红黑树缓解了,但自定义对象没正确实现hashCode()时,测试用例得能触发O(n²)的插入。规范建议:对Map的键类型做哈希分布抽样,或者直接用ConcurrentHashMap加随机化。
漏洞测试不是把工具跑一遍就完事。上面这些点,每个都得写成可执行的测试用例,还得配一个“如果失败,具体怎么修”的指引。否则开发看了报告只会说:“哦,又误报了吧。”
```








