关于 auditing-code-for-vulnerabilities
auditing-code-for-vulnerabilities 是面向代码审计人员的源代码漏洞分析方法,适用于审查仓库、变更和扫描器报告。它通过建立安全上下文、追踪攻击面、验证可利用性和寻找同类问题,区别于仅依赖模式匹配的扫描。
主要功能
- 01先建立资产与信任边界模型
- 02映射并排序输入入口
- 03沿数据路径追踪漏洞类别
- 04验证可达性、控制与影响
- 05确认漏洞后搜索变体
- 06记录审计覆盖范围
适用场景
- 01审计服务或代码库中的安全缺陷
- 02审查提交或拉取请求引入的风险
- 03验证扫描器告警是否属实
- 04为客户安全审计制定计划
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trilwu/ secskills --skill auditing-code-for-vulnerabilities安装后在 Claude Code 中输入/auditing-code-for-vulnerabilities确认已加载。
附带文件references/bug-class-checklist.md
详细介绍
审计代码中的漏洞
在源代码中发现真实漏洞,与运行扫描器是两项不同的工作。扫描器会匹配模式;审计人员则会构建代码应该保证什么的模型,然后寻找保证失效的路径。这项技能介绍的正是第二项工作的方法。
适用场景
- 审计代码库、服务或库中的安全缺陷
- 审查差异、分支或拉取请求中引入的漏洞
- 在大型代码库中排查某一特定类别的漏洞
- 验证扫描器报告或已报告的漏洞是否真实存在
- 为客户项目制定审计计划和覆盖范围报告
不适用场景
- 对运行中的应用进行黑盒测试——使用
testing-web-applications或testing-apis - 专门针对 PHP 代码库(类型混淆、
unserialize/phar POP 链、php://包装器)——使用本方法以及auditing-php-applications - 排查植入的 WebShell 或后门,而非漏洞——使用
hunting-web-backdoors - 仅有二进制文件的目标——使用
analyzing-binaries - 依赖和构建流水线风险——使用
auditing-supply-chain - 密码学构造审查——使用
reviewing-cryptography - 撰写已完成发现的问题报告——使用
reporting-security-findings - 使用构建者/批评者分工和迭代,在整个目标范围内开展多 Agent 漏洞发现活动——使用
orchestrating-vulnerability-research,它会将本技能分派为每个切片的排查者
工作循环
审计分为四轮,而不是一轮。不要跳到第 3 轮。
1. Context → what does this system protect, and from whom?
2. Attack surface → where does untrusted input enter, and what does it reach?
3. Hunt → trace specific bug classes along those paths
4. Verify → prove exploitability before you write a word
第 1 轮:阅读代码前先建立上下文
不要随意打开文件。先花时间回答以下问题:
| 问题 | 查看位置 |
|---|---|
| 有哪些与安全相关的资产? | README、文档、数据模型、数据库架构 |
| 有哪些参与者和信任层级? | 身份验证中间件、角色枚举、租户模型 |
| 声明了哪些不变量? | 测试、断言、包含“must”“never”“invariant”的注释 |
| 此处已修复过哪些问题? | 使用 git log --grep 搜索安全关键词、CVE 文件、SECURITY.md |
| 哪些内容不在范围内? | 项目简报、供应商代码、生成文件 |
# Prior security work is the cheapest source of bug leads
git log --oneline --grep='security\|CVE\|vuln\|injection\|auth bypass\|overflow' -i | head -40
# Stated invariants often mark where the author was nervous
rg -n --stats 'MUST NOT|must never|SECURITY|XXX|HACK|TODO.*(auth|secur|valid)' -i
# Where does privilege actually get checked?
rg -n 'is_admin|require_role|authorize|has_permission|@login_required|checkAccess'
开始排查前,先写一份简短的目标模型:资产、参与者、信任边界,以及违反后影响最大的三个不变量。之后的所有工作,都是在寻找这些不变量的反例。
第 2 轮:绘制攻击面
列出所有入口点,然后按优先级排序。入口点在未经验证前能触达多深,决定了它的重要程度。
# HTTP/RPC routes
rg -n '@(app|router)\.(get|post|put|delete|patch)|app\.(get|post)\(|@RequestMapping|http\.HandleFunc'
# Deserialization, template rendering, and dynamic execution sinks
rg -n 'pickle\.loads|yaml\.load\(|Marshal|unserialize|ObjectInputStream|eval\(|new Function|exec\(|Runtime\.getRuntime'
# Command, SQL, and path sinks
rg -n 'os\.system|subprocess.*shell\s*=\s*True|child_process\.exec\(|execSync|Statement\.execute|\.raw\(|fmt\.Sprintf.*SELECT'
# Where authentication is decided rather than enforced
rg -n 'verify=False|InsecureSkipVerify|jwt\.decode\(.*verify.*False|algorithms=\[.*none'
按以下顺序排列入口点:无需身份验证即可访问 > 低权限层级可访问 > 仅管理员可访问。然后沿着优先级最高的入口点向内追踪。深度胜过广度——完整追踪一条路径,胜过找到二十条 grep 匹配结果。
grep 匹配结果是候选项,不是发现的问题。 上述命令只是低成本地广撒网;每个匹配项在完成追踪之前都是尚未解决的线索。保留候选集——以 (file:line, bug class, entry point) 形式建立工作清单——并逐一给出明确结论:已追踪且安全、已确认或需要 PoC。先低成本扩大搜索范围,再针对每个候选项投入高成本推理——顺序绝不可反过来。常见的失败并非缺少 grep 模式,而是列出五十个候选项,只粗略查看五个,就宣称整个代码库没有问题。对于大型代码库,应将项目自身的惯例纳入搜索范围——例如 ORM 的原始查询绕过方式、身份验证装饰器的名称、模板调用方式——因为通用模式容易漏掉产出最高的危险点。
第 3 轮:沿追踪路径排查漏洞类别
对于每条有希望的路径,从源头到汇点追踪污点,并思考代码作了哪些假设。以下是高收益漏洞类别,按其进入生产环境的频率大致排序:
授权,而非身份验证。 大多数真实的数据泄露源于缺少对象级检查,而不是登录机制失效。对于每个接收 ID 的处理程序,都要问:对象是否限定在调用者的租户或用户范围内,还是仅按 ID 查询?检查查询,而不是装饰器。
信任边界混淆。 数据在一层经过验证,却在另一层重新解析。留意跨越序列化边界的值——例如经过验证的字符串又被解析为 URL、路径、模板或查询。
状态与并发。 检查后使用的间隙、非原子余额更新、实际上并不唯一的幂等键,以及会重放副作用的重试路径。查找没有锁或事务保护的先读后写操作。
注入到次级解释器。 SQL、shell、LDAP、XPath、模板引擎、日志格式和正则表达式。问题从来不是“是否有过滤”,而是“过滤器与解释器是否采用相同的语法”。
内存安全(C/C++/不安全 Rust/CGo)。边界检查前进行长度运算、使用受攻击者影响的大小调用 memcpy、循环边界差一错误、有符号与无符号转换,以及错误路径上的释放后使用。
错误与清理路径。 通常有人审查正常路径,却不会审查 except、catch、defer 和 goto fail 分支。要专门审查这些路径。
机密信息与密码学误用。 硬编码密钥、对令牌进行非恒定时间比较、由 Math.random/rand() 生成可预测 ID、缺少签名验证。深入的密码学审查应归入 reviewing-cryptography。
第 4 轮:变体分析
漏洞是一个模式,不是单一事件。确认漏洞后,立即搜索其同类问题——同一作者犯下的相同错误、复制的代码块,或相邻路由中缺失的相同检查。
# You found one unscoped lookup. Find every other one.
rg -n 'find_by_id|findOne\(\{ *_id|get_object_or_404' -A3 | rg -v 'tenant|owner|user_id'
变体分析能让审计产生不成比例的价值。应明确为其安排时间——每确认一项发现,大约投入同等时间搜索变体。
报告前验证
无法演示的发现只是一个假设。将其写入报告前,请回答以下四个问题:
- 可达性——指出具体入口点和所需的调用者权限。“攻击者无需身份验证即可访问
POST /api/export。” - 可控性——说明攻击者可以控制危险值的哪一部分。
- 影响——说明哪些内容会被破坏:第 1 轮中的哪项不变量,以及攻击者能获得什么。
- 没有缓解控制措施——检查是否存在 WAF 规则、框架默认设置、中间件、DB 约束,或已经执行清理的调用方。
如果工作范围包含概念验证,请编写能够证明汇点受控的最小示例,而不是武器化的漏洞利用。
重新验证,剔除误报
上述四项检查用于确认发现;这一轮则试图推翻它。在每个已确认的候选项进入报告前都执行此步骤——报告的可信度取决于其中最严重的误报,而非最有力的真实发现。
-
问题是否已经修复? 你正在查看的代码树可能没有包含修复,或者修复可能位于尚未拉取的分支上。提交报告前,请确认存在漏洞的代码确实会随产品发布。
git log -S'<dangerous token>' --oneline -- <file> # when this line changed, and toward what git log --oneline <checkout>..origin/main -- <file> # a fix on main you are not reading git blame -L <line>,<line> <file> # the commit that introduced it, for context -
从对立角度重新推导。 论证相反的情况:假设代码是安全的,然后查找使其安全的控制措施——中间件、DB 约束,或已执行清理的调用方。经受住真正反证尝试的发现,才是你能够辩护的发现。
-
确认汇点仍然接收到你的值。 重新追踪最后一跳。重构常会在源头和汇点之间加入验证器或编码器,初次阅读时很容易忽略。
剔除未通过此处检查的候选项,并在覆盖范围备注中说明。无法重新验证的候选项只是给自己的备忘,不是发现。
应当摒弃的自我说服
这些想法会让审计沦为走形式。每一种都不对。
- “上游应该已经验证过了。” 那就去查阅上游代码。对调用方未经验证的假设,是最常见的漏报原因。
- “框架会处理这个问题。” 框架只会处理默认路径。检查版本,检查配置,确认这次调用是否使用了安全 API。
- “这个输入是内部的。” 今天是内部的。追踪它的来源;队列消费者或管理员导入通常都能从外部触发。
- “只有经过身份验证的用户才能利用这个漏洞。” 这是评估严重性时的一个因素,不是忽略问题的理由。
- “这看起来是有意为之。” 意图不是防护措施。记录这一意图,并保留该发现。
- “扫描器没有标记它。” 扫描器只能找到规则覆盖的问题。
- “我已经确认了,所以它是真的。” 你只是在一次代码检出中、根据自己的第一印象确认了它。在将其写入报告之前,请针对实际发布的内容重新验证,并认真尝试推翻这一结论。
- “我已经审查了足够多的文件。” 覆盖范围应根据第 2 轮中梳理的攻击面衡量,而不是根据文件数量衡量。
工具辅助,而非工具替代
静态分析用于扩大覆盖范围,以及在你了解某个模式后搜索变体。确认漏洞后编写规则,让它找出其余问题。
# Semgrep: broad pass, then a rule you write for your specific finding
semgrep --config=auto --severity=ERROR --json -o semgrep.json .
semgrep --config=./rules/my-variant-rule.yaml .
# CodeQL for dataflow questions grep cannot answer
codeql database create db --language=<lang> && codeql database analyze db --format=sarif-latest -o out.sarif
# Language-specific
bandit -r . -f json # Python
gosec -fmt=json ./... # Go
cargo audit && cargo geiger # Rust deps + unsafe surface
npm audit --json # JS deps
使用上述四个验证问题对每项工具发现进行分流。未经验证的扫描器输出报告比不报告更糟——它会消耗读者的信任,并掩盖真正的漏洞。
审计交付物
在审计过程中跟踪覆盖范围,并如实说明:
## Coverage
| Component | Files | Depth | Notes |
|-----------|-------|------------|--------------------------------|
| auth/ | 12 | Full trace | All routes traced to sinks |
| billing/ | 30 | Partial | Webhook handlers only |
| vendor/ | - | Excluded | Out of scope per brief |
## Findings
F1. [High] Tenant isolation bypass in GET /api/reports/:id — <impact> — <repro>
说明你没有覆盖哪些内容。声称实现了实际上并未达到的全面覆盖,是你能产出的危害最大的交付物。
参考资料
references/bug-class-checklist.md— 各语言的漏洞搜寻清单reporting-security-findings— 严重性评分和报告撰写格式- OWASP 应用程序安全验证标准(ASVS),用于基于要求的审查
- CWE Top 25 和 CWE 层级结构,用于分类