本文目录导读:

代码审计(Code Review)是识别安全漏洞的重要手段,主要目标是在开发阶段发现并修复安全隐患,而非等到生产环境被攻击,以下是系统性地排查漏洞的思路、方法论和常见漏洞点。
核心审计流程(方法论)
好的审计不是漫无目的地“看代码”,而是有策略地“找数据流”。
-
明确审计范围与目标
- 是白盒审计(有源码)还是黑盒/灰盒? 白盒能看逻辑,灰盒能看接口。
- 关注点: 是寻找特定类型漏洞(如SQL注入),还是全面安全评估?是审计核心业务模块(如支付、登录)还是第三方组件?
-
“数据流”分析法(最核心)
- 入口: 追踪所有用户可控的输入点。
- HTTP参数(GET/POST/Body/Header/Cookie)
- 文件上传
- 第三方回调、WebSocket消息
- 环境变量、命令行参数
- 传递: 数据如何经过过滤、编码、拼接、序列化?是否到达了“危险函数”?
- 出口/处理: 数据最终被如何处理?
- 数据库 -> SQL注入
- 操作系统命令 -> 命令注入
- 浏览器(HTML/JS) -> XSS
- 文件系统 -> 路径遍历/文件上传漏洞
- XML解析器 -> XXE
- 内部逻辑(如加密、校验) -> 逻辑漏洞
- 入口: 追踪所有用户可控的输入点。
-
“关键函数/关键字”搜索法(快速扫描)
- 用
grep、ripgrep或 IDE 的全局搜索功能,查找危险函数和敏感关键字。 - 输出类:
print、echo、innerHTML、response.write - 执行类:
eval、exec、system、shell_exec、Runtime.getRuntime()、subprocess.call - SQL类:
select…where、mysql_query、PDO::query(特别是未预处理的情况)、EntityManager.createQuery(JPQL拼接) - 文件类:
file_get_contents、include、require、InputStream、readfile、unlink - 反序列化类:
unserialize、readObject、pickle.loads、ObjectInputStream.readObject() - 弱密码/硬编码:
password= “123456”、key= “hardcoded_key”
- 用
常见漏洞类型排查清单(按优先级排序)
SQL注入(最常见、最严重、最经典)
- 排查点:
- 动态拼接SQL: 直接使用
“SELECT * FROM users WHERE id = ” + userId这种形式。 - 不安全的ORM用法: 某些ORM的
raw()、nativeQuery()方法,或拼接了用户输入的HQL/JPQL。 - 存储过程: 如果存储过程内部使用了
EXECUTE拼接字符串。
- 动态拼接SQL: 直接使用
- 修复检查:
- 是否全部使用了参数化查询/预编译(PreparedStatement、参数化查询、ORM的内置查询方法)?
- 对于
LIKE子句,是否对输入的 、 进行了转义?
跨站脚本(XSS)
- 排查点:
- 反射型: 用户输入(如
$_GET[‘name’])直接不经过滤,出现在echo或响应体中。 - 存储型: 用户提交的评论、文章标题等存入数据库,被其他用户读取时未转义。
- DOM型: 前端JavaScript直接操作DOM:
document.innerHTML = urlParam、eval(location.hash)、$('#output').html(userInput)。
- 反射型: 用户输入(如
- 修复检查:
- 是否在输出处进行了上下文相关编码(HTML实体编码、JS编码、URL编码)?而不是仅在输入端过滤(输入过滤不可靠,因为输出场景不同)。
- 是否设置了
Content-Security-Policy(CSP)头?
文件上传漏洞
- 排查点:
- 后缀名白名单: 仅允许
jpg,但攻击者可能上传.php、.jsp、.war、.exe。 - 内容验证: 是否只检查了
Content-Type(客户端可控)?应检查文件头(Magic Number)或使用图片二次处理库。 - 路径可控: 上传路径是否包含用户输入(如 导致目录穿越)?
- 执行权限: 上传目录是否有执行脚本的权限(Web服务器配置)?
- 后缀名白名单: 仅允许
- 修复检查:
- 文件重命名(使用随机字符串+白名单后缀)。
- 文件存储在Web根目录外或云存储(如OSS)。
- 使用专业的上传组件(如
apache-commons-fileupload)。
命令注入
- 排查点:
- 系统调用:
Runtime.getRuntime().exec(“ping ” + userInput)、system(“curl ” + url)。 ProcessBuilder参数被拼接。
- 系统调用:
- 修复检查:
- 优先避免调用系统命令。
- 必须调用时,使用数组形式传递参数(如
exec([“ping”, “-c”, “4”, userInput]))而不是拼接字符串。 - 严格白名单校验参数内容(只允许IP格式、域名格式)。
路径遍历(Path Traversal)
- 排查点:
- 文件读取:
readFile(“/var/data/” + fileName),fileName可为../../etc/passwd。 - 文件包含:
include(“/templates/” + tplName)。
- 文件读取:
- 修复检查:
- 正规化路径(
realpath()/Path.normalize())并检查是否在允许的基目录内(必须加前缀,如/var/data/)。 - 使用白名单或ID映射方式选择文件。
- 正规化路径(
反序列化漏洞
- 排查点:
- Java:
ObjectInputStream.readObject()、readResolve()、FastJson、Jackson的enableDefaultTyping()。 - PHP:
unserialize()。 - Python:
pickle.loads()、yaml.load()。 - .NET:
BinaryFormatter.Deserialize()、JavaScriptSerializer。
- Java:
- 修复检查:
- 避免对不可信数据反序列化。
- 使用白名单机制(如
ObjectInputFilter(Java)、safe_load()替代yaml.load())。 - 升级有漏洞的序列化库(如Jackson、FastJson、Log4j)。
逻辑漏洞(最难静态检测,需理解业务)
- 排查点:
- 权限绕过: 修改
userid、role参数是否能查看到其他用户数据(水平越权)?普通用户操作管理员接口(垂直越权)? - 验证码/重放: 优惠券、红包、抽奖、登录次数未做幂等性限制?Token未失效?
- 支付逻辑: 修改价格参数(负数、小数)、修改数量参数(整数溢出)、修改支付状态(未支付改为已支付)?
- 竞争条件: 多次并发请求(如提现、领奖、购买限量商品)是否导致库存扣减错误?
- 权限绕过: 修改
- 修复检查(需结合业务):
- 服务端校验: 所有敏感操作(修改、删除、查询)必须二次校验当前用户是否拥有权限(即:永远不要相信客户端传过来的
user_id作为权限依据)。 - 原子性操作: 库存、余额操作应使用数据库锁(乐观锁/悲观锁)或分布式锁。
- 服务端校验: 所有敏感操作(修改、删除、查询)必须二次校验当前用户是否拥有权限(即:永远不要相信客户端传过来的
不安全的依赖库/组件
- 排查点:
使用了过时的、已知漏洞的第三方库(如 Log4j <=2.14.1、Struts 2 某些版本、低版本 jQuery)。
- 修复检查:
- 使用工具检测:OWASP Dependency-Check、Snyk、Trivy、GitHub Dependabot。
- 建立依赖库版本管理策略,及时打补丁。
高效辅助工具(但不要完全依赖)
工具能帮你发现规律性的漏洞(如SQL注入、XSS),但逻辑漏洞、业务漏洞、权限漏洞仍需人工判断。
- 静态代码分析工具(SAST)
- 商业: Fortify、Checkmarx、Veracode。
- 开源: SonarQube(+安全插件)、Semgrep(可自定义规则)、CodeQL(GitHub出品,规则强大)。
- IDE/插件
- VS Code + Semgrep 插件。
- IntelliJ IDEA + Qodana。
- 依赖分析
- OWASP Dependency-Check(Maven/Gradle插件)。
npm audit、pip audit。
审计报告(输出什么?)
一个高质量的审计报告应包含:
- 漏洞详情: 漏洞名称、风险等级、所在文件/行号。
- 复现步骤(POC/Demo): 清晰说明如何触发,最好附带请求示例或代码片段。
- 影响分析: 攻击者能利用它做什么?(拿数据、提权、控制服务)
- 修复建议: 具体的代码修改建议(如:“将第58行拼接SQL改为PreparedStatement”)。
- 修复方案: 如果业务无法修改,可提供的临时缓解措施(如WAF规则、配置文件加固)。
关键原则总结
-
信任边界: 永远不要信任用户输入。
所有来自客户端、网络请求、第三方API、文件系统的数据,默认都是“恶意”的,直到被证明是安全的。
-
深度防御:
- 输入端做校验(格式、长度、类型)。
- 转换层(如数据库)做参数化。
- 输出层(如浏览器、系统命令)做编码。
-
最小权限:
- 给代码分配最小的数据库权限。
- 给文件系统分配最小的读写权限。
- 给用户分配最小的功能权限。
-
自动化辅助 + 人工分析。
- 工具负责扫描明显的、具有语法特征的漏洞。
- 人负责分析逻辑流、权限控制、业务上下文。
开始实战审计时的一个高效技巧: 从最近改动或核心业务接口入手,追踪一个用户输入的参数从 “入口”(HTTP请求) 到 “出口”(数据库、文件、浏览器、系统调用) 的完整路径,通过检查每一步是否被正确处理,能快速发现绝大多数漏洞。