本文目录导读:

代码审计(或称代码安全审查)是一项系统性的工作,目的是通过阅读源代码来发现安全漏洞、逻辑缺陷和配置问题,要高效地排查漏洞,不能漫无目的地看代码,而需要结合自动化工具与人工审查,并围绕关键风险点进行。
以下是一个系统的代码审计排查指南,分为思路、工具、方法、常见漏洞类型四个部分。
核心审计思路(先宏观,后微观)
不要一上来就逐行读代码,建议按以下步骤进行:
- 理清架构与数据流:
- 理解应用是做什么的(电商、CMS、API服务等)。
- 识别核心功能模块:用户登录、文件上传、支付、搜索、API接口。
- 画数据流图:数据从哪里来(用户输入、数据库、第三方API)?经过了哪些处理?最终去哪了?
- 寻找“不可信数据入口”:
$_GET,$_POST,$_REQUEST,$_FILES,$_COOKIE(PHP)request.getParameter(),@RequestParam,@RequestBody(Java)request.form,request.args,request.json(Python Flask)- HTTP Headers, User-Agent, Referer, X-Forwarded-For
- 数据库读取的数据(二次注入或存储型XSS的可能来源)
- 文件读取的配置/模板内容
- 追踪数据流向:
- 从入口点开始,沿着函数调用栈,追踪这个数据最终:
- 是否拼接到了 SQL语句? → SQL注入
- 是否直接拼到了 HTML输出? → XSS
- 是否拼到了 操作系统命令? → 命令注入
- 是否拼到了 文件路径? → 路径遍历
- 是否有反序列化操作? → 反序列化漏洞
- 是否作为参数传给eval? → 代码执行
- 从入口点开始,沿着函数调用栈,追踪这个数据最终:
关键的审计方法(代码层面)
黑盒白盒结合 + 敏感函数搜索
-
敏感函数搜索(Grep大法):这是最快的方法,用关键词搜索特定语言中的危险函数。
- PHP:
eval,assert,system,exec,shell_exec,popen,include,require,unserialize,file_get_contents,mysql_query,preg_replace(/e模式) - Java:
Runtime.exec,ProcessBuilder,Class.forName,Method.invoke,URLClassLoader,ObjectInputStream.readObject,FileInputStream,PreparedStatement(但拼接了字符串) - Python:
eval,exec,os.system,subprocess.call,pickle.loads,yaml.load,__import__
找到这些函数后,往前回溯,看其参数是否可控。
- PHP:
-
框架特性审计(现代应用必查):
- ORM框架(MyBatis, Hibernate, JPA):检查
@Select或@Query注解中是否有 和 混淆。 是字符串拼接,必查SQL注入。 - Spring Boot:检查
@Value注解、配置文件(application.properties)、Actuator端点是否暴露。 - 模板引擎(Thymeleaf, Freemarker, Jinja2):检查模板中是否有 表达式注入或错误的循环引用。
- 路由与权限:检查
@RequestMapping/@GetMapping等注解,看是否缺少权限校验(如@PreAuthorize)。越权漏洞(IDOR)是审计重点。
- ORM框架(MyBatis, Hibernate, JPA):检查
逻辑漏洞审计(最难也最值钱)
- 身份认证:
- 密码重置Token是否可预测(时间戳+简单随机数)?
- 登录是否限制了失败次数?
- Session是否安全创建?注销后是否销毁?
- 访问控制(越权):
- 垂直越权:普通用户能否访问管理员的URL(如
/admin/deleteUser)?检查后端是否有统一的权限拦截,还是只在页面层做隐藏。 - 水平越权:用户A能否通过修改ID访问用户B的订单(如
/api/order?orderid=123)?检查后端是否用了Session中的用户ID来校验,而不是直接信任前端传入的参数。
- 垂直越权:普通用户能否访问管理员的URL(如
- 业务逻辑:
- 支付逻辑:金额是否在客户端计算且在服务器端未校验?能否篡改支付状态(如
success=true)?优惠券能否无限使用? - 密码修改:旧密码验证是否可绕过?修改他人密码是否只需提供用户名?
- 验证码:是否在服务端校验一次后即可被重复使用?是否存在于Session中,逻辑检查后未清空?
- 支付逻辑:金额是否在客户端计算且在服务器端未校验?能否篡改支付状态(如
危险配置与依赖审计
- 第三方依赖(Composer, Maven, npm):
- 使用
npm audit,pip audit,dependency-check等工具扫描package.json,pom.xml,composer.lock。 - 检查是否存在已知的高危CVE(如Log4Shell, Struts2 S2-045)。
- 使用
- 配置不当:
- 数据库连接密码硬编码在代码中。
- 调试模式开启,堆栈信息可能泄露SQL语句、路径、内部变量。
- 日志文件记录了敏感信息(明文密码、银行卡号)。
常用自动化工具(辅助而非替代)
| 语言/场景 | 推荐工具 | 特点 |
|---|---|---|
| 通用静态分析 | Semgrep | 规则驱动,可自定义规则,非常强大,支持多种语言。 |
| CodeQL | GitHub出品,功能最强,学习曲线陡峭;可查询复杂数据流。 | |
| SonarQube | 侧重代码质量、坏味道和常见安全漏洞,持续集成友好。 | |
| Fortify / Checkmarx | 商业级,误报率低,但价格昂贵,适合企业。 | |
| Python | Bandit | 专注于安全审计,规则简单,适合快速扫描Flask/Django代码。 |
| Java | FindBugs / SpotBugs | 经典插件,配合 find-sec-bugs 规则库检测安全漏洞。 |
| JavaScript | ESLint (+ eslint-plugin-security) | 检测不安全的JS模式(如 eval)。 |
| PHP | RIPS (开源版) | 专为PHP设计,数据流分析能力不错。 |
常见漏洞排查清单(Checklist)
审计时,对照以下清单快速检查:
| 漏洞类型 | 审计重点(看什么) | 经典红点 |
|---|---|---|
| SQL注入 | 原始SQL拼接、 在MyBatis中、LIKE查询、IN子句、ORDER BY 动态排序、存储过程 | 搜索 拼接,检查所有数据库查询的参数来源。 |
| XSS | 用户输入 -> HTML展示(echo, print, innerHTML, dangerouslySetInnerHTML) |
看输出前是否经过 htmlspecialchars() / escape() / encodeForHTML() 编码。 |
| CSRF | 所有改变状态的请求(POST, DELETE, PUT) | 检查是否有 CSRF Token 校验;SameSite Cookie 设置是否严格。 |
| 文件上传 | 文件类型校验逻辑、文件名拼接、文件存储路径 | 校验是否服务端校验MIME或文件头(不仅仅是后缀);文件名是否未处理特殊字符。 |
| SSRF | file_get_contents、curl_exec、HttpURLConnection |
攻击者能否控制URL指向内网(127.0.0.1, 10.x.x.x)?是否限制了协议(只允许http?)。 |
| SSRF | 要求传递URL参数的函数 | 特别关注是否提供了绕过对IP黑名单限制的可能性。 |
| 反序列化 | unserialize、readObject、pickle.loads、yaml.load、xml.dec.XMLPullParser |
是否存在 Magic Methods(如 __wakeup, __destruct)调用危险函数。 |
| 逻辑越权 | 所有涉及ID、订单号、用户ID的API接口 | 后端代码在操作前是否通过Session获取当前用户,并校验该用户拥有对参数的对象的权限。 |
| 命令注入 | exec, system, Runtime.getRuntime().exec(), ProcessBuilder, subprocess.check_output |
参数是否经过 escapeshellcmd / escapeshellarg 处理,且是否拼接到命令中。 |
高效审计实战流程(建议)
- 初步信息收集:获取技术栈(看
composer.json,pom.xml,requirements.txt, 框架路由文件)。 - 自动化快速扫描:
- 运行 Semgrep 或 SonarQube,获取一份高可疑度的报错列表(SQL注入、命令执行、文件包含)。
- 运行
npm audit或pip audit检查依赖。
- 人工重点突破:
- 优先看用户认证、登录、注册、密码重置模块(这是攻击者首选入口)。
- 其次看文件上传和API接口(逻辑漏洞高发区)。
- 针对自动化工具报出的“疑似”漏洞,人工三遍确认:
- 输入可控吗?(是用户输入还是固定值?)
- 有过滤吗?(黑名单还是白名单?如只过滤了 select,但没过滤 union select)
- 输出安全吗?(上下文是什么?数据库?HTML?JSON?)
- 逻辑链测试:模拟正常业务操作流程,思考“这个步骤如果被跳过、被篡改、被重放会发生什么?”
- 编写最终分析报告:包含漏洞位置、修复建议、复现步骤。
总结一句话
代码审计 = 找到每一个不可信的输入数据 + 找到每一个执行或存储这些数据的危险函数 + 判断中间路径的过滤/编码/校验是否有效。