ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

构建稳定的AI代码安全审计Skill:从规则库到Agent实践

构建稳定的AI代码安全审计Skill:从规则库到Agent实践 前阵子有朋友问我你那个 security-audit-skill 到底怎么写的为什么我自己折腾了一个让 AI 做代码安全审计结果不是漏报就是误报最后还得人工全部重看一遍这个问题其实问到点子上了。我自己也经历过这个阶段一开始觉得让大模型做安全审计就是写一段长提示词的事比如请你帮我检查这个仓库有没有安全漏洞重点关注注入和越权。听起来没毛病但实际跑下来完全不是那么回事今天它能查出几处硬编码密钥明天同一个提示词换一个仓库输出就变成了建议加强输入验证这种正确但没用的废话。后来我花了几周时间把安全审计的方法论、检查项、报告模板全部固化成了一个 skill效果才算真正稳定下来。这篇文章就把我设计 security-audit-skill 的完整思路写出来包括能力边界怎么划、规则库怎么组织、执行流程长什么样、实际踩了哪些坑以及后面怎么把这个 skill 接进 agent 流水线里。适合正在研究 AI skill 编写、想给自己团队配一个代码审计助手的同学参考。1. 为什么安全审计要单独做成 Skill而不是对话里多塞几句提示词先说一个现象很多做 AI 编程工具的人对 skill 的理解还停留在结构化提示词这个层面。确实skill 底层有一部分是指令文本但如果只是把提示词换个文件包装一下那你遇到的稳定性问题一个都不会少。1.1 大模型做安全审计的知识漂移问题我在实际使用中的最大感受是安全审计是一件特别依赖确定性流程的事情。人类安全审计员是怎么工作的先看项目结构识别语言栈锁定高风险模块然后逐项对照检查清单——SQL 注入、XSS、敏感信息泄漏、越权、依赖漏洞每一项都有固定的判断逻辑和验证方法。整个流程是结构化的不是看到啥算啥。但大模型天生是联想式的推理你给它一段代码它会基于训练时见过的类似模式做判断。这带来一个致命问题模型状态不稳定。上下文里多了一段无关日志它可能就从审计模式漂移成代码讲解模式。上下文过长时它甚至会忘掉你最初给的审计要求开始专注于最后看到的那个文件。普通提示词扛不住这种漂移因为它的指令是一次性的没有层次、没有状态流转、没有强制检查点。而 skill 不一样它不是一个段落而是一整套指令 知识库 流程定义 模板的组合。模型在 skill 的约束下相当于被装进了一条有轨道的审计流程里。1.2 Skill 是把审计方法论固化的最小单元我理解的 skill本质上是把某个专业领域的方法论编码成 AI 能稳定执行的操作规程。你去看现在主流编程助手里的 skill 结构基本都有几个共同部分一个主指令文件比如 SKILL.md描述这个 skill 的触发条件、执行步骤、输出要求一个知识库目录存放规则、检查项、CWE/OWASP 参考信息一个脚本目录放一些可执行的辅助脚本比如生成扫描计划、统计扫描覆盖率若干示例或模板让模型知道最终产出长什么样。security-audit-skill 也是这个套路。核心思路是不指望模型懂安全审计而是把安全审计拆成模型能按步骤执行的子任务每一步都有明确输入和输出。举个例子人类审计员知道检查 SQL 注入要先找到所有数据库查询语句、再判断参数是否拼接、最终是否经过过滤和参数化。这套判断链如果写在提示词里可能就一两句话带过模型执行两次就变形了。而在 skill 里我会把这条检查链拆成独立步骤要求模型对每个数据库操作代码逐个判断并把判断依据填入固定表格。一旦模型跳过某个步骤输出格式校验就能发现。1.3 和普通提示词、MCP 工具的区别还有一个很容易混淆的点skill、提示词、MCPModel Context Protocol工具这三者不是一回事我在早期也花了不少时间才分清楚。提示词是给模型的一段文字指令没有结构化没有外部依赖Skill是围绕某个能力域的完整封装包含结构化的执行逻辑、知识引用和输出模板强调按规程办事MCP 工具是给模型提供手脚比如调用扫描器 API、读文件、执行命令它解决的是模型能做什么动作的问题而不是模型应该按什么流程思考的问题。实际做安全审计时三者往往是组合使用的skill 负责定义审计流程和规则MCP 工具负责帮模型把仓库拉下来、把文件列表读出来、把依赖版本查询一遍提示词只承担非常临时的交互说明。把审计逻辑完全压在提示词上是最脆弱的做法。2. 设计 security-audit-skill 时的能力边界哪些该管哪些不该碰给 skill 划能力边界是我觉得比写规则本身更重要的一件事。新手容易犯的毛病是贪大求全什么漏洞都想查什么代码都想分析最后 skill 变得又笨又慢输出一堆无法验证的疑似问题。2.1 第一版只做 SAST 级代码扫描不做运行时验证我的第一版 security-audit-skill 只做一件事静态应用安全测试SAST。也就是不执行代码、不发起请求、不动态调试纯粹通过读源码来发现问题。为什么不把动态扫描也做进来原因有两个一是动态验证需要跑环境、发流量、分析响应这完全超出静态 skill 的职责范围硬塞进来会让流程不可控二是静态审计本身就是安全测试的第一步能把这一步做扎实已经能覆盖大部分常见问题。举个例子检查 SSRF服务端请求伪造漏洞。静态阶段我能做的是找到所有发请求的代码看 URL 是否由用户可控是否有协议限制或域名白名单。至于这个 URL 实际能不能打到内网需要运行时验证。在 security-audit-skill 里我的指令明确写着只标注疑似 SSRF不确认内网可达性同时建议后续用什么工具去做动态验证。这个不碰运行时的边界极大降低了 skill 的复杂度。模型不需要理解运行环境不需要判断网络策略只需要做代码层面的模式匹配和逻辑推理准确率明显提升。2.2 静态规则覆盖的漏洞清单能力边界确定后下一步是明确规则覆盖范围。我给自己定的第一版清单包含以下几个类别都是静态代码分析中识别率较高、误报可解释性较强的类型类别典型问题判断要点注入类SQL 注入、命令注入、模板注入用户输入是否拼接进执行语句输出类XSS、敏感信息泄漏未转义输出、日志打印密钥文件类路径遍历、任意文件读取路径拼接是否含用户可控片段网络类SSRF、HTTP 明文调用请求 URL 是否可被外部操控凭据类硬编码密钥、弱口令、token 泄漏代码中是否出现 secret 模式加密类弱哈希算法、弱加密算法、不安全随机数是否使用 MD5/SHA1/DES/rand认证类越权、缺少权限校验接口是否缺少访问控制依赖类已知漏洞组件、过期依赖依赖锁定文件版本比对这个清单不是一次性定的很多类别是后面迭代补进来的。比如不安全随机数就是我在审一个重置密码模块时发现的问题——模型第一次没当回事因为代码里用的是标准库的 random行为上能跑但在安全语义下它是可预测的。后来我把这个判断逻辑写进了规则库。2.3 规则之外的辅助检查依赖、配置、凭据代码扫描之外我还给 skill 加了三项辅助检查它们不直接分析业务代码但经常能在审计里发现大问题依赖安全检查。读取依赖锁定文件比对已知漏洞库。这一项我不要求 skill 内置完整漏洞库而是要求模型聚焦哪些依赖版本明显过旧哪些依赖已被官方标记为不再维护输出给人工复核。配置文件检查。看是否存在调试模式未关闭、默认管理员密码未修改、CORS 配置为*、数据库连接串无加密等情况。凭据泄漏扫描。搜索.env、config目录和代码中是否有被提交的密钥文件以及密钥字符串是否落进日志、测试代码、前端 bundle。这里有一个细节不只是搜索password、secret之类的关键词还要识别常见第三方平台密钥的格式特征比如带特定前缀的 token这种模式匹配规则需要不断积累。边界划清楚之后skill 的主干就出来了静态代码审计 依赖与配置辅助检查 证据化报告。其他一律不做或者只在报告里给出后续建议。3. 规则库是灵魂自定义审计规则的字段设计与组织方式有了边界还得有内容。security-audit-skill 真正值钱的不是那段通用指令而是沉淀下来的审计规则库。没有规则库的 skill就像没有菜谱的厨师能力全凭感觉。3.1 每条规则必须具备的六个字段我这套规则格式是迭代了几轮才稳定下来的。一开始很简单就问题类型 检测关键词结果误报高得没法看。后来我参考了商业 SAST 工具的规则格式重新设计成六个字段rule_id规则的唯一编号方便审计报告中引用和定位severity严重级别按 critical / high / medium / low 分级category所属漏洞类别对应 CWE 或 OWASP 分类描述与影响用几句话说明漏洞原理和可能被利用的方式检测逻辑告诉模型看到什么样的代码模式才需要怀疑这个漏洞这是降低误报的关键验证方法检测出疑似问题后如何进一步确认包括追踪变量来源、查看上下文调用关系、判断数据流是否真正到达风险点修复建议给出针对该场景的修复方向。字段看起来多但每条规则写下来也就几十行维护成本完全可以接受。3.2 用 YAML 写规则让 AI 按结构执行规则格式我用的是 YAML而不是自然语言段落。原因很简单YAML 有清晰的键值结构模型读取时不容易丢字段也方便脚本做校验和统计覆盖情况。我拿硬编码密钥检测这条规则举例它在我的 skill 规则库里长这样rule_id: SC-CRED-001 severity: critical category: CWE-798 description: 源代码中存在硬编码的密钥、口令或访问令牌。 detect_logic: - 查找赋值语句右侧为字符串字面量的敏感变量名password, secret, api_key, token - 查找符合第三方平台密钥格式特征的长字符串 - 排除测试目录、示例目录中明确标注为 mock/fake 的字符串 verify_method: - 追踪该变量是否流向登录认证、接口鉴权、第三方 API 调用等关键路径 - 检查是否通过环境变量或密钥管理服务注入 - 若仅为前端展示占位符则降级为 low 或忽略 fix_suggestion: 将密钥迁移到环境变量或密钥管理服务禁止写入代码仓库注意几条规则里我都加了排除测试目录和验证流向的逻辑。这是从反复的误报中总结出来的如果只写检测是否出现密钥模型会把example_password 123456这种测试代码也报成 critical那报告就废了。3.3 规则的组织层级文件级 / 函数级 / 全局级规则库不能是平铺的列表否则模型扫描时会乱。我把规则按作用层级分为三类文件级规则关注整个文件的配置和内容。比如检查application.yml中是否关闭了调试模式、是否配置了宽松的 CORS、依赖声明里是否出现高危版本。函数级规则关注具体函数内部的实现模式。比如 SQL 语句拼接、未经验证的eval/exec调用、文件路径直接拼接用户输入。这类规则是审计的主要工作量所在要求模型进入每个关键函数逐行判断。全局级规则需要跨文件追踪数据流。比如用户输入参数从 HTTP 入口开始经过哪些处理函数最终是否到达了敏感操作。这类规则最难写也最容易漏报我的处理方式是不要求模型做全链路数据流分析而是要求它对入口函数和敏感操作点分别打标再通过交叉比对判断是否存在未校验路径。这个分层扫描的设计对模型非常友好。它不需要一次性理解整个项目而是先把所有文件分类然后按类别套用对应的规则集。结构清晰上下文消耗也更可控。4. 一次完整审计的执行流程从仓库信息收集到证据链报告规则库是静态的怎么让模型按一个稳定的流程把规则跑起来才是 skill 设计的核心难点。我把执行流程拆成了四个阶段每一步都做了输入输出约束。4.1 阶段一仓库信息收集与攻击面粗扫描审计不是从看第一行代码开始的而是从了解这个系统是什么开始。skill 的第一条指令是要求模型先做信息收集扫描目录结构识别项目类型、语言栈、前后端模块划分读取构建文件和依赖清单记录关键依赖及版本定位配置目录、路由目录、入口文件、鉴权模块粗略记录哪些目录看起来包含敏感逻辑比如auth/、admin/、payment/。这个阶段结束时模型要输出一个项目概览表包含语言栈、关键入口、高风险模块预判。这个概览不追求精确但它决定了后续审计的优先级排序。我踩过的一个坑是不给这个阶段足够的权重。早期版本一上来就扫描代码结果模型被一堆低价值文件比如样式文件和配置文件带偏了注意力把关键漏洞漏了。后来我把信息收集设为强制阶段并要求模型输出表格后才允许进入下一步。4.2 阶段二按规则逐项进行深度检查信息收集完成后skill 会生成一份本次审计检查清单也就是从规则库里挑选适用于当前项目的规则子集。挑选逻辑很简单语言栈不同适用的规则不同。一个 JavaScript 项目就不需要重点查反序列化漏洞一个 Python 项目则需要额外注意pickle.loads一类的危险调用。然后在检查阶段我要求模型逐文件扫描而不是全仓库笼统分析。每个文件内部按文件级规则 → 函数级规则的顺序执行遇到需要跨文件验证的先标记为待交叉验证不立即下结论。这一步里最重要的约束是每个发现必须附带文件路径、行号、代码片段这三样证据禁止输出没有坐标的模糊结论。4.3 阶段三交叉验证与误报标注交叉验证阶段是我后续迭代加上的也是把误报率压下去的关键。模型在检查阶段会产生一批候发现其中必然混有误报。比如识别到了一个变量名里有password的普通字符串但它实际上只是一个表单字段名并没有参与任何认证逻辑再比如项目里的测试代码故意构造了恶意输入来测试防护结果被模型当成真实漏洞报出来。针对这种情况skill 要求模型对每个候发现做一轮自我质疑该发现是否位于测试、示例、Mock 目录被标记的危险输入源头是否真正来自用户/外部请求数据流路径上是否有过滤、编码、白名单等防护措施是否有现有统一封装让看起来危险的调用实际被安全处理。经过这一轮质疑发现被分为三个状态确认、疑似、需人工复核。只有确认项才进入最终报告的高优先级位置疑似和需复核则单独列出。4.4 阶段四产出带证据链的审计报告最后是输出环节。security-audit-skill 对报告格式有严格要求我直接在 SKILL.md 里固化了模板不允许模型自由发挥。报告结构很固定风险总览、统计数据、按严重级别排序的漏洞明细、每个漏洞的完整证据链、修复建议、以及限定说明。每个漏洞明细部分是这样的格式规则编号与漏洞类型严重级别与置信度文件路径与行号漏洞代码片段与上下文说明攻击场景简述修复建议。为什么对格式这么较真因为类似的 skill 在社区有不少但很多跑出来的报告像聊天记录审计员还得自己重新整理。格式化输出不是表面功夫它是让 AI 审计结果能进入人工复核流程的前提。没有固定格式模型每次输出的字段都可能缺斤短两下游处理脚本根本无法落地。5. 真实踩坑记录误报、漏报、上下文爆炸这三座大山这一节写我实际维护这个 skill 过程中遇到的最棘手的问题。网上的教程基本不会讲这些但它们决定了你的 skill 是能演示还是能生产用。5.1 最头疼的误报问题先说误报。初版 skill 跑在一个真实项目上产出了 14 个 critical 级别问题。我心里还想着效果不错啊结果人工一复核真正成立的只有 3 个误报率接近 80%。问题出在哪规则里的detect_logic描述太宽泛。模型看到代码里出现exec就条件反射式地报命令注入完全没看exec的参数是不是硬编码字符串、有没有经过白名单校验。看到SELECT字符串拼接就报 SQL 注入但它没确认拼接部分是否由用户可控、数据库操作是否最终进入了参数化查询封装。后来我把规则的detect_logic全部重写了一遍核心改动是从看到危险函数就报改成先确认危险数据是否可控再确认是否到达风险点。同时在 prompt 里反复强调一个原则误报不是小事它会让审计报告整体失去可信度宁可少报三个也不要乱报一个。5.2 AI 容易一本正经地漏报漏报比误报更隐蔽也更难察觉。误报至少还在报告里人眼能筛掉漏报是根本不出现你可能永远不知道自己漏了什么。我遇到的一种典型漏报模式是跟随主体错位。模型扫到一个文件列表它在前几个文件里认真做了分析到了后半段输出开始变得敷衍出现此文件未发现明显安全问题的结论但实际文件里就有一处明显的硬编码密钥。我把这种输出拉出来看发现模型不是没有能力而是上下文处理不过来了——它一次处理太多文件后面的文件已经得不到足够的注意力。针对这个问题我做了两个改动一是在阶段二强制一个一个来每个文件独立分析当前文件分析完、填写完结果表再进入下一个文件二是给 skill 加了一个扫描计划脚本如果检测到文件数量超过阈值就自动生成分批计划比如先扫业务代码目录再扫配置文件目录而不是让模型一口气全吞。5.3 上下文窗口不够用时怎么办说到上下文这是所有做大仓库审计的 skill 都会撞上的墙。模型上下文窗口就那么大一个大仓库动辄几千个文件不可能全塞进去。我采取的办法是按需加载 摘要前置。具体流程是第一阶段信息收集时只读目录树和关键构建文件不读全文根据目录树和依赖清单选出高风险模块优先加载这些模块的文件内容低价值目录比如 assets、docs 的非安全文档直接跳过只在报告中说明本次审计未覆盖遇到超大文件要求模型只读取并分析其中的关键函数区段而不是整份代码。另外我还在 SKILL.md 里写了一条非常明确的指令禁止为了凑上下文而把结论先行变成结论空洞。有些模型在上下文不足时会生成大量建议加强安全意识之类的套话这层水分必须挤掉。宁可少看一个低优先级文件也要把已加载文件的分析做扎实。5.4 输出格式紊乱的治理最后一个坑是输出格式。大模型有个特点给定模板它能照着执行两次第三次就可能开始自由发挥。尤其是在审计很多文件以后它可能把风险总览写成大段叙述把漏洞明细写成了聊天形式。我治理的方式很朴素在 skill 的主指令里把每一个阶段的输出模板都写死并且阶段衔接处用检查点来约束——不完成某一格式的输出不允许进入下一阶段。比如阶段二结束时必须输出一个待验证问题清单表格字段固定模型不能跳过。如果模型输出的格式不符合要求我会在下一轮让它重新格式化后再继续。这个办法治标也治本。治标是因为格式约束让模型少了很多创造性空间治本是因为这些检查点实际上起到了分心隔离的效果每完成一个阶段就刷新一次模型的工作状态减少上下文漂移。6. 从 Skill 到 Agent安全审计流水线的下一步演进说到现在security-audit-skill 本身已经能作为一个独立工具使用了。但我在实际工作中发现单独跑一个审计 skill 只是第一步真正价值更大的是把它接入到更大的 agent 流水线里。6.1 Skill 和 Agent 的本质区别这个主题我在标题里就带了因为看到太多人把 skill 和 agent 混为一谈。我的理解是Skill 是能力包它封装了怎么做一件事的完整方法论但它本身不决定什么时候做、做完以后干什么。Agent 是调度中枢它负责接收目标、拆解任务、按需调用不同的 skill、汇总结果、决定下一步动作。用安全审计来举例security-audit-skill 知道怎么审计一个仓库而一个代码安全巡检 Agent知道的是新代码合并时触发审计、把审计结果发给对应的开发者、如果发现 critical 级别问题则阻止合并、更新规则库以应对新的漏洞模式。这些编排逻辑不在 skill 里而在 agent 里。6.2 在 Agent 中组合多个 Skill 的审计流水线单靠一个安全审计 skill覆盖面还是有限。实践下来我建议在 agent 里组合三类 skill 形成完整流水线security-audit-skill负责业务代码的静态审计产出结构化报告。dependency-review-skill负责依赖与供应链安全检查锁定文件、比对漏洞情报、输出组件升级建议。这个 skill 可以共享 security-audit-skill 的规则库格式但它的扫描目标更聚焦。config-compliance-skill负责配置基线检查比如 Kubernetes YAML 是否允许特权容器、云服务资源配置是否过宽、环境变量中是否存在危险默认值。agent 的流程编排大致是先由 dependency-review-skill 扫一批依赖风险再调用 security-audit-skill 扫业务代码最后用 config-compliance-skill 检查部署配置三个 skill 的输出统一汇总到一份综合报告里按严重级别合并去重。组合的价值在于单一 skill 能发现问题agent 能把这些发现组织成一次完整的风险评估并推动后续处理动作。6.3 后续演进规则库更新与反馈闭环最后聊聊这个 skill 未来的演进方向。我认为规则库必须要有反馈闭环否则会慢慢变成一潭死水。我现在维护这套 skill 的方式很简单每次人工复核发现误报就把这条案例补充到规则库的反例部分每次发现漏报就新增或修正一条检测规则。规则库会随着使用变厚误报率会随迭代降低。虽然这个动作完全靠手工但当团队里有两三个人一起维护时这个共享规则库的价值会越来越大。还有一个方向是把审计结果结构化存储下来比如生成 JSON 报告方便后续接入漏洞管理平台。目前 security-audit-skill 的输出已经具备规则编号、严重级别、文件行号和复现步骤理论上整条链路打通起来并不难。我自己在实际使用中最大的体会是做这种专业向的 skill一定要把减少模型自由发挥的空间当成第一原则。安全审计不是创意写作它需要的是稳定、可复现、能追溯的流程。每一次让模型自由发挥都可能在真实项目里制造一次漏报或误报。说到底skill 的价值不是让 AI 变得更聪明而是让 AI 在一个明确的轨道上稳定地输出专业结果——这份稳定才是它比一段提示词值钱的地方。
返回列表