源码安全审计不是把代码丢进扫描器跑一遍就完事,扫描器只能发现已知漏洞模式,真正的风险往往藏在业务逻辑、权限校验缺失和第三方依赖的深层调用里。一次有效的审计必须从攻击面梳理开始,而不是从工具开始。先搞清楚系统对外暴露了什么:API接口、文件上传入口、后台管理地址、WebSocket端点、回调通知地址,把这些入口全部列成清单,再逐一追踪每个入口的数据流向和权限控制,才能避免“扫了一遍没高危”但实际被人直接越权拖库的情况。

审计前的资产与入口梳理

拿到源码后第一件事是确认技术栈和目录结构,记录框架版本、语言版本、依赖库清单。很多严重漏洞并不是自研代码写出来的,而是用了存在已知CVE的旧依赖。接下来把所有可访问的URL和入口点整理出来,包括前端路由和后端路由,尤其注意那些不需要登录就能访问的接口。对于每个入口,标记它是否需要鉴权、是否涉及数据库操作、是否接收文件上传、是否调用外部服务。这个清单就是你后续审计的主线,没有这份清单,审计很容易变成漫无目的地翻代码。

依赖与第三方组件风险排查

依赖检查要具体到版本号。比如Java项目里用了Fastjson,版本低于1.2.83就存在反序列化风险;Node.js项目里用了旧版lodash或minimist,可能有原型污染或命令注入问题。不能只看直接依赖,还要看间接依赖,很多漏洞藏在传递依赖里。审计时可以用依赖分析工具生成依赖树,但工具报出来的结果需要人工判断是否真的可利用,因为有些漏洞的触发条件在当前项目里并不成立。判断依据是:漏洞触发所需的代码路径是否在项目中被实际调用,以及攻击者能否控制输入到达该路径。

认证与会话管理审计

认证部分重点看登录接口是否对失败次数做限制,是否存在用户名枚举,密码重置流程是否可以被绕过。很多系统在重置密码时只校验了短信验证码或邮箱验证码,却没有校验验证码与目标账号的绑定关系,导致攻击者用自己的验证码重置他人密码。会话管理要检查Token生成是否使用足够随机的算法,Token有效期设置是否合理,退出登录时服务端是否真正使会话失效。还要注意Cookie属性:HttpOnly、Secure、SameSite是否设置正确,缺少这些属性会让会话容易被脚本读取或被跨站请求利用。

权限控制与越权漏洞排查

越权是业务系统里出现频率最高、危害最直接的漏洞类型。审计时要对每个接口做两件事:一是确认它是否做了登录校验,二是确认它是否做了对象归属校验。例如查看订单详情接口,URL里传了订单ID,后端只校验了用户已登录,却没有校验这个订单是否属于当前用户,这就是水平越权。再比如普通用户调用了只有管理员才能使用的接口,后端没有做角色判断,这就是垂直越权。审计方法很简单:逐个接口追问“如果我把URL里的ID改成别人的,会怎样?”“如果我用普通账号调用这个管理接口,会怎样?”把这两个问题问遍所有入口,越权问题基本无处遁形。

输入验证与注入类漏洞审计

SQL注入仍然是高危漏洞里的常客。审计时要关注所有拼接SQL的地方,包括使用字符串拼接、MyBatis里使用${}而不是#{}的地方。不要只看DAO层,很多拼接发生在Service层或Controller层。命令注入要关注系统命令执行函数,比如Java的Runtime.exec、PHP的system和shell_exec、Node.js的child_process.exec,追踪这些函数的参数是否包含用户可控输入。XSS要区分存储型和反射型,重点检查富文本编辑器、评论功能、用户资料展示等场景,输出时是否做了编码处理。文件上传漏洞则要检查是否校验了文件扩展名、MIME类型和文件内容,上传后的文件是否在可执行目录,访问路径是否可控。

文件操作与路径穿越审计

文件下载、文件读取、文件删除功能是路径穿越的高发区。审计时看文件路径是否由前端参数直接拼接而成,例如下载接口接收filename参数,后端直接拼成/upload/ + filename去读取文件,攻击者传入../../etc/passwd就能读取任意文件。修复方式是先对文件名做白名单校验,再用真实路径与预期目录做前缀比对。文件上传后的保存路径也要注意,如果保存的文件名使用了用户原始文件名,需要过滤掉路径分隔符和特殊字符,最好使用服务端生成的文件名存储。

业务逻辑漏洞审计

业务逻辑漏洞是扫描器最难发现的,因为它不依赖特定代码模式,而是依赖业务流程设计缺陷。常见的有:支付金额由前端传入,后端不重新计算;优惠券可重复使用;验证码在服务端不过期或可复用;注册时手机号与验证码不绑定;抽奖接口可被脚本高频调用;订单状态流转缺少校验,用户可以直接将订单改为已支付或已完成。审计业务逻辑需要先理解正常业务流程,再思考每一步是否可以被跳过、重放、并发或篡改。特别要关注涉及资金、积分、库存、权限变更的接口。

加密与敏感信息处理审计

检查密码存储是否使用了强哈希算法,比如bcrypt、scrypt或argon2,而不是MD5或SHA1。检查敏感信息是否在日志中被明文打印,很多系统在调试时打印了用户密码、Token、身份证号等信息,上线后没有清理。检查接口返回是否过度暴露字段,比如用户列表接口返回了密码哈希、内部ID、权限字段。还要检查传输层是否强制使用HTTPS,以及敏感数据在缓存、备份文件、临时文件中是否有残留。

漏洞修复与验证流程

审计完成后输出的报告不能只列漏洞名称和风险等级,要给出每个漏洞的触发路径、影响范围和具体修复建议。修复顺序按风险等级和可利用性排序,优先修复无需认证即可利用的漏洞,比如未授权访问、远程代码执行、SQL注入。修复时要避免只做前端校验或黑名单过滤,要从根源上解决问题。例如SQL注入修复要改用参数化查询,而不是过滤单引号;越权修复要在服务端做归属校验,而不是隐藏前端按钮。

修复完成后必须进行回归验证,验证方式不是重新跑一遍扫描器,而是按照审计报告里的触发路径手动复现,确认漏洞已经无法利用。同时要检查修复是否引入了新的问题,比如改了权限校验后是否影响了正常功能。对于无法立即修复的漏洞,要制定临时缓解措施,比如通过WAF规则拦截攻击流量、限制接口访问频率、暂时关闭高风险功能。整个修复过程要有记录,包括漏洞状态、修复人、修复时间和验证结果,形成闭环。

源码审计的自动化与人工结合

自动化工具能提高效率,但不能替代人工分析。可以把工具用在第一轮扫描,快速找出已知漏洞和可疑代码点,然后人工对扫描结果进行确认和深入分析。人工审计的重点是业务逻辑、权限控制、加密实现和跨模块的数据流追踪。审计时建议从入口点开始,沿着数据流一路追到数据库或文件系统,这样不容易漏掉跨层漏洞。对于大型项目,可以按模块拆分审计,每个模块单独梳理入口和数据流,最后再汇总分析模块间的交互风险。

源码安全审计的最终目的不是找漏洞,而是建立一套可持续的安全基线。每次审计后把常见问题整理成开发规范,把典型漏洞案例加入代码评审清单,把可复用的审计脚本沉淀下来,后续迭代时就能在开发阶段拦截大部分问题,而不是等到上线后再补救。