在当今数字化时代,Java 应用程序的安全性至关重要。代码审计作为保障应用安全的关键环节,能够帮助我们提前发现潜在的安全漏洞。本文将详细介绍 Java 代码审计的思路方法以及常见基础漏洞的审计要点,为安全从业者和开发人员提供参考。
一、代码审计思路和方法
进行 Java 代码审计时,可采用以下四种主要思路和方法,它们各有优劣,适用于不同场景:
-
逆向追踪(回溯变量)
- 方法:从敏感函数的参数入手,回溯变量的来源和传递过程,判断其是否可控且未经过严格过滤。例如,可依据 SQL 语句的特征来分析是否存在注入漏洞。
- 优点:通过搜索敏感关键字,能快速、高效地挖掘到定向漏洞。
- 缺点:由于未通读代码,对程序整体框架了解不深入,可能遗漏逻辑漏洞。
-
正向追踪(跟踪变量)
- 方法:先找出接收外部参数的文件,然后跟踪变量的传递路径,观察其是否传入高危函数或存在代码逻辑漏洞。
- 优点:挖掘范围比逆向追踪更全面。
- 缺点:效率可能不如逆向追踪。
-
直接挖掘功能点漏洞
- 方法:根据经验判断应用中哪些功能点可能存在漏洞,然后直接阅读该部分代码。
-
通读全文
- 方法:通读整个应用程序的代码,以了解其业务逻辑和整体架构。建议先从程序入口文件(如
index
文件)、函数集文件(如functions
、common
)、配置文件(如config
)和安全过滤文件(如filter
、safe
、check
)入手。 - 优点:能更好地了解程序架构和业务逻辑,挖掘出更多有价值的逻辑漏洞。
- 缺点:耗时较多,对于大型程序来说工作量较大。
- 方法:通读整个应用程序的代码,以了解其业务逻辑和整体架构。建议先从程序入口文件(如
二、基础漏洞审计
1. SQL 注入
- 漏洞原理:程序未对用户输入进行正确检查,直接以拼接方式将输入带入 SQL 语句中,导致攻击者可构造恶意 SQL 语句影响数据库操作。
- SQL 语句执行方式:
- JDBC:通过
java.sql.Statement
对象进行直接拼接,容易产生注入;而java.sql.PreparedStatement
使用预编译,相对安全。 - MyBatis:使用
$
符号进行拼接会造成注入,而#
符号使用预编译,基本安全。 - Hibernate 也可能存在相关注入风险,需具体分析。
- JDBC:通过
- 审计关键词:
Statement
、createStatement
、PrepareStatement
、select
、update
、insert
。 - 常见注入类型:
- 参数动态拼接。
- 预编译错误。
ORDER BY
注入:ORDER BY
无法预编译,需要使用拼接方式,易出现漏洞。LIKE
模糊查询注入。IN
查询注入:在IN
语句中使用#
会导致整个参数作为一个整体拼接进去,容易出现注入。
- 审计方法:通过关键词搜索,找到关键字后查看周围是否安全调用外部变量或是否有过滤。若没有,则追踪调用栈查找引用位置。
- 二次注入:在注册、登录等位置注入包含特殊字符的 SQL 语句,在后续查询用户信息的过程中可能导致二次注入。
2. 文件上传
- 漏洞原理:程序员对文件上传的控制不足或处理有缺陷,导致用户可以上传可执行的动态脚本文件。问题的关键在于服务器如何处理和解释上传的文件。
- 审计关键词:
org.apache.commons.fileupload
、java.io.File
、MultipartFile
、RequestMethod
。 - 审计方法:
- 关注上传表单代码段。
- 关注文件上传后的后缀校验、文件类型黑白名单、Content-Type 黑白名单,以及(如果为图片)图片库检测。
- 检查是否存在以下常见问题:未做任何过滤、仅在前端进行过滤、只判断
Content-Type
、后缀过滤不全、读取后缀方式错误(例如,不安全的后缀获取方式为fileName.indexOf(".")
)。 - 后端需要对文件类型、大小、文件名和内容进行验证。
3. XSS (跨站脚本攻击)
- 漏洞原理:通过注入恶意代码(通常是 JavaScript)到网页中,当用户访问该页面时,恶意代码被执行,可能导致用户信息泄露等问题。
- 漏洞分类:
- 反射型 XSS:非持久化,注入的恶意代码通过服务器的响应返回到前端页面并被执行。
- 存储型 XSS:持久化,注入的恶意代码存储在服务器中,用户访问页面时触发。
- DOM 型 XSS:通过修改页面的 DOM 节点触发。
- 审计关键词:
request.getParameter
、response.getWriter().print()
、response.getWriter().writer()
、<%=
、${
、<c:out
、<c:if
、<c:forEach
、ModelAndView
、ModelMap
。 - 修复手段:采用全局过滤器、安全应用程序接口(如 ESAPI / 谷歌的 xssProtect)、HTML 实体编码等方式进行防御。
4. 目录穿越
- 漏洞原理:没有校验文件名中是否包含
../
等特殊字符及对用户访问文件进行限制,导致攻击者可访问未授权的目录和文件。 - 审计关键词:
Path
、java.io.File
、FileInputStream
、file.read
。 - 修复手段:进行文件名过滤,检测路径是否合法。
5. URL 重定向
- 审计关键词:
return
、redirect
、url
、jump
、goto
、target
、link
、new ModelAndView
、redirectAttribute
、response.setHeader
。 - 修复手段:控制跳转域名,将跳转值写为固定值;设置只能根据路径跳转。
6. 命令执行
- 漏洞原理:代码未对用户可控参数做过滤,导致恶意命令被拼接到正常命令中执行,可能对服务器造成严重破坏。
- 执行命令方法:
java.lang.ProcessBuilder.start()
。java.lang.Runtime.getRuntime.exec()
:其 String 型重载在执行时会使用StringTokenizer
函数按空格、制表符等分割命令,可能导致无法执行;而数组型重载可以直接调用ProcessBuilder
执行,相对安全。
- 审计关键词:
getParameter
、ProcessBuilder
、start()
、Runtime.getRuntime.exec()
。 - 修复手段:避免直接使用
getRuntime
,并过滤输入的恶意字符。
7. XXE (XML 外部实体注入)
- 漏洞原理:应用程序在解析 XML 输入时,未禁止外部实体的加载,导致攻击者可加载恶意外部文件,获取敏感信息等。
- 审计关键词:
XMLReader
、SAXBuilder
、SAXReader
、SAXParserFactory
、Digester
、DocumentBuilderFactory
。 - 审计方法:
- 检查 XML 解释器的配置,看是否禁用了 XML 外部实体。
- 查看代码中是否使用了上述关键词中列出的 XML 解析接口或类。
- 修复手段:禁用 DTD / 禁用外部实体。
8. SSRF (服务端请求伪造)
- 漏洞原理:攻击者伪造服务器发出请求,向内部网络或其他受保护资源发起请求,而服务器未进行过滤和限制,可能导致内部信息泄露等问题。
- 审计关键词:
HttpURLConnection.getInputStream
、URLConnection.getInputStream
、HttpClient.execute
、URL.openStream
、ImageIO.read
。 - 审计方法:
- 快速找到 HTTP 请求操作函数。
- 测试是否可以访问内部地址(如
http://127.0.0.1
或内网 IP)或使用不同的 URL 协议(如file
协议)。
- 修复手段:
- 控制跳转域名,最好在源码中写为固定值。
- 设置只能根据路径跳转。
9. 反序列化
- 漏洞原理:应用程序在反序列化过程中,未对恶意数据进行验证和过滤,导致攻击者可以劫持字节流,植入恶意代码并被执行。
- 必要条件:存在利用链(exploit chain)和触发点(trigger point)。
- 审计关键词:
ObjectInputStream.readObject
、XMLDecoder.readObject
、yaml.load
、XStream.fromXML
、ObjectMapper.readValue
、JSON.parseObject
。 - 审计方法:
- 检查代码中是否使用了上述关键词。
- 关注流行的 Java 框架(如 Apache Struts、Apache Commons Collections、Jackson)中存在的反序列化漏洞。
10. CSRF (跨站请求伪造)
- 漏洞原理:攻击者盗用用户的身份,以用户的名义发起恶意请求,而服务器没有进行身份识别,可能导致用户数据被篡改等问题。
- 审计方法:检查是否对
Referer
、Token
等参数进行了严格的检测。
11. 逻辑漏洞
- 漏洞原理:在软件设计过程中存在错误或疏忽,导致未正确处理输入、验证用户身份、授权访问等,从而引发安全风险。
- 审计方法:逻辑漏洞的产生不在于代码本身,而在于业务逻辑的缺陷,因此需要深入理解业务逻辑,才能进行测试。
- 常见漏洞类型:
- 验证码漏洞:验证码爆破、回显、未与手机绑定、修改返回包绕过、转发、固定验证码等。
- 支付漏洞:任意金额修改、负数购买、越权支付、修改运费、优惠券修改、四舍五入漏洞、整数溢出等。
- 并发漏洞(条件竞争):后台未对操作加锁,导致并发操作产生安全问题,例如点赞或领取优惠券等。
- 越权漏洞:攻击者修改用户身份识别参数(如
id
、uid
、name
等),进行不属于自己的操作,例如删除评论、查看他人信息等。
12. SpEL 表达式注入
- 漏洞原理:程序未对用户输入进行验证,直接通过解析引擎解析 SpEL 表达式,可能导致恶意代码执行。
- 审计关键词:
SpelExpressionParser
、parseExpression
、expression.getValue()
。 - 修复手段:使用
SimpleEvaluationContext
。
通过以上对 Java 代码审计思路方法和常见漏洞的梳理,希望能帮助大家在实际工作中更好地进行代码审计,提升 Java 应用程序的安全性。在实际审计过程中,还需结合具体业务场景和代码细节,进行全面、深入的分析。