修复安全漏洞
你的工作不是“一次性修好所有问题”。而是按正确顺序修复、验证每个修复,并且不破坏已有的功能。用户往往是vibecoder:不要想当然地认为他们理解爆炸半径,请解释清楚。
黄金法则
在删除、取消部署或禁用在生产环境中的任何内容之前,切记必须先回答:“今天有什么在使用它?”(前端、作业、其他端点、仪表板)。在删除前务必查看。如果有合法使用,停止并通知用户,不要继续操作。
如何在每个关卡提问
在每个流程暂停以请求确认的点上(提议的顺序、认证方案、在应用任何不可逆操作之前),使用AskUserQuestion工具,而不是用自由文本提问。用户是vibecoder,更倾向于选择选项而非撰写回复。
每个关卡的提问模式:
- 简短标题(例如:“严重级别1”、“认证方案”、“应用差异”)。
- 典型选项:“批准并开始”(推荐,第一个)、“先调整”、“先审查/解释更多”。
- 每个实际决策只提一个问题。不要将用户可能希望分开批准的事项合并到一个问题中(例如,启动严重级别1和批准严重级别2方案是两个不同的问题)。
- 如果决策依赖于用户尚未看到的内容(例如,差异尚未显示),先展示该内容,然后再提问。
流程
1. 分类
按严重性和不可逆性对漏洞进行排序,而不仅仅是按严重性。一个破坏生产流程的“高级”漏洞比一个孤立的“严重”漏洞更棘手。将每个漏洞分类为:
- 孤立 + 可逆 → 可以直接修复。
- 涉及认证 / 改变公共契约 → 需要在编写代码之前制定方案。
- 删除或取消部署生产环境中的基础设施 → 需要在触碰任何内容之前确认“今天有什么在使用它”+ 备份 + 回滚方案。
呈现提议的顺序并暂停。在用户确认之前,不要继续修复。
2. 先单独处理严重/不可逆的漏洞
- 在删除/取消部署之前:列出今天调用它的内容。如果它确实是无主的,保留备份(受影响表的导出)和回滚方案。
- 只应用一个更改。不要与其他漏洞混在一起。
- 在继续之前验证(第4步)。
3. 修复认证 / 公共契约
- 首先制定文本方案:你要添加什么验证,今天有哪些合法客户端调用它,如何避免破坏它们。等待批准。
- 然后再编写代码。
4. 验证(每个修复必须执行)
重新执行发现漏洞的相同攻击/PoC,并显示“之前易受攻击 / 现在被拒绝”。没有复现,修复不算关闭。仅仅说“已经修复”是不够的。
5. 回归测试
确认合法流程仍然正常工作(例如,如果你向端点添加了认证,确保官方客户端仍然可以运行)。说明你测试了什么。
6. 防护措施
对于每个已确认的漏洞,添加一个测试,该测试可复现漏洞,并在漏洞再次出现时失败。如果无法测试,留下原因说明。
7. 批量快速修复
低风险的漏洞(头部信息、输入验证、查询修复)可以分组到一个提交中,但在应用之前列出每个修复及其差异。
8. 关闭
最终总结:哪些已修复并验证,哪些待处理及原因(例如,主网前的多签),以及哪些更改需要用户手动部署。
自检清单(不要跳过,不要等待用户要求)
用户是vibecoder,可能会批准一个修复而没有注意到它是装饰性的。在宣布关闭一个漏洞之前,自行运行这些检查,如果任何一项适用,即使没有人要求也要说出来。
- 认证 = 身份,而不是存在。 一个仅验证是否收到令牌的门控并不能关闭IDOR。确认令牌/会话已绑定到身份(钱包、user_id),并且你将此身份与请求的资源进行比较。如果“所有权”检查(403)没有基于真实的绑定,那么修复是虚假的。
- main = 生产环境。 如果你从未合并的分支部署了修复,那么生产环境运行的代码不在
main上:下一次从main部署将还原修复。通知用户需要合并,并将其标记为安全风险,而不是整洁性。 - 审计员也可能出错。 在应用修复之前,在实际代码中验证漏洞的假设(例如,“客户端发送令牌”可能是错误的)。基于错误假设的修复会破坏生产环境。
- 已接受的风险要记录,而不是沉默。 如果某些内容未修复(静态令牌、因破坏流程而撤销的轮换),将其记录为已接受的风险,而不是“已关闭”。
- 验证在一个端点,而不是所有端点。 如果一个字段在PATCH中验证但不在POST中(或反之),漏洞仍然存在。检查相同数据的所有输入路径。
- 删除会留下痕迹。 在取消部署/删除后,审计表或资源中是否有漏洞利用留下的数据;不要假设恢复端点就能清除已经造成的损害。
输出风格
- 在应用任何不可逆更改或部署更改之前显示差异。
- 没有第4步的复现,不要宣布“已解决”。
- 如果调查后发现漏洞是误报,说明原因,不要“修复”它。