让AI写代码这事儿,圈子里已经没人觉得新鲜了。但让AI去审代码里的安全问题,以前我是真不敢放手,总觉得模型虽然能说出个大概,可一到具体漏洞点、调用链、修复方案,就容易飘。直到我把一套完整的安全审计流程做成了一个Agent Skill——也就是security-audit-skill——实测跑了几轮之后,效果比我口头叮嘱AI去“检查一下安全”要好出太多。它不只是更规范,而是直接把我的审计经验、检查清单、报告模板全固化下来了,AI拿到这个Skill,基本就是个听话的安全审计助理。
这篇文章我会从原理讲到实操,完整拆解我是怎么设计这个security-audit-skill的,包括目录结构怎么写、SKILL.md的内容怎么组织、审计清单如何落到references里、怎么让Agent按固定流程输出安全审计报告。不管你是用Claude Code、Codex还是OpenCode,这套思路基本都通用。如果你也想让AI帮你做靠谱的代码安全审计,或者正在研究怎么写出高质量的Skill,这篇应该能给你不少可以直接照抄的东西。
1. 先搞清楚Skill是什么,为什么安全审计适合做成Skill
1.1 Skill不是提示词,是给Agent的岗位SOP
很多朋友一听说Skill,第一反应是“这不就是写一大段提示词吗”。我一开始也这么想,实际折腾完才发现,Skill和提示词的差别,类似于你临时叫一个新员工“帮我把系统检查一下”,和丢给他一本《安全审计岗位操作手册》的区别。
Skill本质上是一个目录,里面装着一个SKILL.md主文件,以及若干references参考文档、脚本、模板。SKILL.md负责定义这个能力包的触发场景、运行流程和输出要求,references则存放更细的检查清单、知识点、示例代码。AI在对话时如果判断当前任务和某个Skill匹配,就会读取这个Skill的内容,然后严格按里面的流程执行。
以Claude Code为例,Skill可以放在用户级目录(比如~/.claude/skills/)或项目级目录(比如.claude/skills/),Codex也有类似机制。放好之后,只要对话内容命中Skill描述里的场景,AI就会自动加载它。这就是为什么很多人在网上问“skill怎么下载运用”“skill注册检索”——它不是一个插件装完就完事,而是要放进正确的目录、并让描述文字足够精准,AI才会在需要时主动调用它。
提示:如果你只是把一大段提示词粘贴进对话里,那每次都要重复贴,而且AI很容易漏掉中间的细节。做成Skill之后,加载是自动的、内容是结构化的,执行路径也稳定得多。
1.2 安全审计为什么是最适合做成Skill的领域之一
安全审计这个任务有几个特点,决定了它特别适合被沉淀成Skill。
第一,审计流程是标准化的。不管审什么项目,基本都绕不开依赖检查、注入类漏洞、认证授权、敏感信息泄露、不安全配置这几大块。流程固定,就意味着可以写成明确的步骤,让AI按部就班执行。
第二,安全知识更新很快,而且细节特别多。你让AI凭训练知识去审计,它可能知道SQL注入是啥,但不一定知道当前项目里哪个框架的哪个API有这个风险。如果把这些“经验型知识”整理成检查清单放进references,等于给AI外挂了一个随时可以翻阅的专家笔记,它的表现会稳定很多。
第三,审计结果需要强证据支撑。好的安全审计报告必须带文件路径、行号、代码片段、风险等级、修复建议。这些东西用对话式提示词很难约束AI每次都给出,但Skill可以把输出模板定死,AI就会乖乖按模板生成。
我身边有朋友用Codex时总是抱怨“让它审计,报告写得跟作文一样,一点都不落地”,后来我把这个Skill分享给他,他跑完第一轮就说:“诶,它现在知道给出具体文件和修复代码了。”这就是Skill和闲聊式提示词最大的差距。
2. Security-Audit-Skill的整体设计思路
2.1 先想清楚审计目标与场景边界
动手写Skill之前,不能上来就列一堆检查项。我设计这个Skill时,先给自己定了两个边界。
一是审计对象:面向中小型代码仓库的初步安全审计,Web应用、API服务、后端代码优先,不碰那种大型分布式系统。为什么?因为一个Skill的指令空间是有限的,贪多嚼不烂,把Web端最常见的攻击面覆盖住,实际收益是最大的。
二是审计目标:不是做渗透测试,也不是绕过WAF,而是快速发现“开发者自己就能修”的常见安全问题,比如硬编码密钥、SQL注入写法、危险函数调用、越权接口、CORS配置过宽、日志泄露敏感信息等。目标明确了,Skill里的检查项才不会跑偏。
确定边界之后,我还定义了这个Skill的输入输出。输入很简单,就是一份代码目录路径;输出则是一份结构化的安全审计报告,包含风险总览、问题明细、修复建议。这个思路建议你在做任何一个Skill之前都先走一遍——先定义边界,再填充内容,不然写着写着就会发现Skill变成了一个什么都想管的大杂烩。
2.2 检查项按攻击面分类,而不是按语言分类
设计检查清单时,我踩过一次坑:一开始按编程语言来组织,分Python、JavaScript、Java各写一套,结果发现不同语言之间横向对比很麻烦,而且同一个漏洞模式在不同语言里反复出现。后来我改成了按攻击面分类,效果好了很多。
最终落地的清单大概分成了六类:
| 分类 | 典型检查项示例 |
|---|---|
| 注入类 | SQL注入、命令注入、路径穿越、XSS、SSRF |
| 认证授权 | 硬编码密钥/Token、Weak口令、越权接口(IDOR)、JWT实现缺陷 |
| 敏感信息 | 密钥提交到仓库、调试日志打印密码、错误信息泄露堆栈 |
| 配置类 | Debug模式开启、CORS配置为*、安全响应头缺失、HTTPS配置不当 |
| 依赖风险 | 存在已知CVE的依赖、lock文件缺失、依赖版本过期 |
| 数据保护 | 明文存储密码、日志记录敏感字段、接口返回多余敏感字段 |
这种分类方式的好处是,AI在审计时可以根据当前文件类型动态调整检查策略,但大的检查框架始终保持稳定。而且我可以在references里针对每个分类写一篇比较详细的“审计笔记”,AI扫描到对应代码时就会去查阅。
2.3 设计原则:渐进式审计,先理解再扫描
这是整个Skill设计里我觉得最关键的一点,也是我和很多AI编程工具磨合之后总结出来的心得:不要让AI一上来就逐行扫描,而是先让AI通读项目、建立全局认知,再分层深入。
我在Skill里把审计流程拆成了四个阶段:
第一阶段,通读与架构理解。让AI先浏览项目根目录、README、配置文件、入口文件,搞清楚这是什么技术栈、有哪些模块、数据怎么流动。没有这一步,AI很容易在细节里迷路。
第二阶段,按清单逐类静态扫描。在理解架构的基础上,按references里的检查清单逐项排查,把可疑代码位置全部标记出来。
第三阶段,调用链交叉验证。光有可疑点是不够的,比如一个输入框接收了用户输入、然后直接拼进SQL查询,那才是真正的SQL注入。AI需要顺着调用链确认数据流是否真的到达危险函数。这一步能过滤掉大量误报。
第四阶段,汇总输出报告。按模板把验证过的问题整理成报告,每个问题都标注风险等级、文件位置、证据代码、修复建议。
为什么不在第一阶段就扫描?因为AI的上下文窗口是有限的,如果它一开始就被各种零散的代码细节占满,后面反而没有余量去做交叉验证。先建立全局框架,再填充细节,再回头验证,这套流程和人工审计的思路其实是完全一致的。
3. 手把手构建:目录搭建与SKILL.md编写
3.1 Skill目录结构规划
建Skill的第一步,是把目录结构搭好。我现在的security-audit-skill大概长这样:
security-audit-skill/ ├── SKILL.md ├── scripts/ │ └── scan_hardcoded_secrets.py └── references/ ├── security-checklist.md ├── owasp-top10-notes.md ├── injection-patterns.md ├── auth-and-access-control.md └── report-template.md- SKILL.md:入口文件,定义触发场景、审计流程、指令序列。
- references/:放详细检查清单和知识点,AI审到某类问题时到这里查细节。
- scripts/:放一些可执行的辅助脚本,比如自动扫描硬编码密钥的正则工具。
这个结构不算复杂,但足够支撑一次完整的安全审计。目录结构确定之后,核心工作就是写SKILL.md,以及把references里的内容填充到能直接指导Action的颗粒度。
注意:Skill目录名不要带空格和特殊字符;SKILL.md这个名字是固定的,不要改成别的,否则AI可能识别不了。
3.2 编写SKILL.md主入口文件
SKILL.md是整个Skill的大脑。它前半部分是YAML格式的frontmatter,声明name和description;后半部分是Markdown正文,内容是给AI的具体操作指引。
description不能乱写,它决定了AI什么时候会加载这个Skill。我实测下来,把使用场景、触发条件、输出内容都写进description里,命中率会高很多。我的SKILL.md大概长这样:
--- name: security-audit description: 对指定代码目录执行系统性的安全审计。适用于Web应用、API服务、后端代码仓库的安全检查场景;当用户要求“审计代码安全”“检查漏洞”“寻找潜在风险”“做一次安全评估”时使用。审计完成后输出包含风险等级、文件位置、证据代码和修复建议的结构化报告。 --- # Security Audit Skill 你是一名资深应用安全工程师。接到审计任务后,必须严格按照以下流程执行,不得跳过任何阶段。 ## 审计目标 发现当前代码中可被实际利用或修复成本较低的常见安全问题,输出可执行的修复建议。 ## 审计流程 ### 阶段一:架构理解 1. 先读取项目根目录,识别技术栈、框架版本。 2. 阅读README、主配置文件、入口文件,理解模块划分和数据流。 3. 记录项目使用的关键依赖及其版本。 ### 阶段二:静态扫描 按 references/security-checklist.md 中的检查清单逐类排查。 对每一类检查项,定位可疑代码后,记录文件路径、行号和代码片段。 ### 阶段三:交叉验证 对阶段二标记的每个可疑点,追溯数据流。 确认用户可控数据或敏感数据是否真的能到达风险点。 仅保留验证通过的问题,删除误报。 ### 阶段四:报告输出 按照 references/report-template.md 的模板生成审计报告。 报告必须包含:风险总览表格、问题明细(含文件、行号、证据、风险等级、修复建议)、优先修复清单。这份SKILL.md“把流程定了,但把细节交给了references”。这样写的好处是,SKILL.md本身不会太臃肿,AI读起来也轻松。千万不要把几百个检查项全部塞进SKILL.md,那样AI反而抓不住重点。
3.3 编写references安全审计清单
references里的清单是这个Skill的“知识底座”。我在security-checklist.md里针对六类攻击面分别列了检查项,每一项的描述都包含四要素:问题描述、审计方法、证据要求、修复方案。
举三个我写过的例子:
SQL注入检查项:
- 问题描述:在SQL语句中直接拼接外部输入。
- 审计方法:搜索execute、query、raw SQL拼接等模式,重点检查动态拼接字符串的位置。
- 证据要求:记录拼接SQL的代码行、外部输入来源、数据库执行语句。
- 修复方案:使用参数化查询或ORM,禁止拼接。
硬编码密钥检查项:
- 问题描述:源码中出现AWS Key、JWT Secret、数据库密码等敏感凭据。
- 审计方法:正则匹配类似于AKIA[0-9A-Z]{16}、-----BEGIN PRIVATE KEY-----等模式,也搜索utils/config文件里可疑的常量字符串。
- 证据要求:标明哪个文件哪个常量、密钥可能用于什么服务。
- 修复方案:移入环境变量或密钥管理服务,并轮换已泄露的密钥。
越权接口检查项:
- 问题描述:接口仅依赖前端传参判断资源归属,后端缺少权限校验。
- 审计方法:查找以ID直接查数据的接口,检查是否校验当前用户与资源归属。
- 证据要求:列出接口路由、参数、后端处理函数。
- 修复方案:在后端增加资源归属校验,使用当前会话用户信息做鉴权。
这里有个经验:检查清单不要追求数量多,要追求“每条都能让AI落地执行”。宁可写30条能落地的,也不要写100条看起来专业但AI不知道该怎么查的。
3.4 编写辅助脚本和输出模板
审计过程中,有些重复性工作可以直接交给脚本。我在scripts里放了一个简单的Python脚本,用来扫描硬编码密钥:
import re import pathlib SECRET_PATTERNS = [ (r"AKIA[0-9A-Z]{16}", "AWS Access Key"), (r"-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----", "Private Key"), (r"sk-[A-Za-z0-9]{24,}", "API Secret Key"), (r"(?i)(password|passwd|secret|token)\s*=\s*[\"'][^\"']{8,}[\"']", "Hardcoded Credential"), ] def scan_files(root: str): root_path = pathlib.Path(root) findings = [] for file in root_path.rglob("*"): if not file.is_file(): continue if any(part.startswith(".") for part in file.parts): continue if file.suffix in {".py", ".js", ".ts", ".java", ".go", ".env", ".json", ".yaml", ".yml", ".conf"}: try: text = file.read_text(encoding="utf-8", errors="ignore") except Exception: continue for lineno, line in enumerate(text.splitlines(), 1): for pattern, label in SECRET_PATTERNS: if re.search(pattern, line): findings.append({"file": str(file), "line": lineno, "type": label, "code": line.strip()}) return findings if __name__ == "__main__": import sys root = sys.argv[1] if len(sys.argv) > 1 else "." for item in scan_files(root): print(f"{item['type']} | {item['file']}:{item['line']} | {item['code']}")这个脚本算不上多高级,但它的意义在于:AI发现敏感信息问题时,可以直接跑一遍脚本快速定位候选位置,然后再人工判断哪些是真泄露。这比AI凭空猜要高效得多。
输出模板在report-template.md里,我会让AI按固定格式生成本次审计报告,核心结构包括风险总览表、问题明细、优先修复清单。格式固定之后,报告的可读性和可追溯性都好了很多。
4. 实战记录与常见问题排查
4.1 实战:用Security-Audit-Skill审计一个FastAPI Demo项目
为了验证Skill的实际效果,我拿一个Spring Boot和FastAPI混合的Demo仓库跑了一轮。在Claude Code里输入“用security-audit审计当前项目”,Skill被自动加载,AI立刻按照流程执行。
审计结果出来之后,有两条发现让我印象很深。
一条是FastAPI项目里有个上传接口,文件名直接拼接到了存储路径上,形成了路径穿越漏洞。AI不仅标出了具体文件和行号,还画出了数据流,证明攻击者可以通过修改文件名参数访问到目录之外。这个判断没有误报。
另一条是某个util类里有一个硬编码的数据库密码,而AI在运行脚本扫描后确认了这条密码同时被多个模块引用。报告给出的修复建议是改为从环境变量读取,并提醒轮换旧密码。这个建议完全可以直接交给开发去改。
整个审计过程大概几分钟,比人工快出一个量级。虽然没有商业扫描器那么全,但作为代码提交前的快速安全体检,完全是可用状态。
4.2 常见问题速查与解决办法
我自己用下来,以及分享给朋友之后,遇到过一些共性问题,整理成速查表:
| 常见问题 | 现象 | 解决办法 |
|---|---|---|
| Skill不生效 | 让AI审计,但它没有按照Skill流程走 | 检查Skill是否放在正确目录,确认description里的触发词是否覆盖了你的提问方式 |
| 审计结果太泛 | 报告只说“存在SQL注入风险”,不给位置和证据 | 检查security-checklist里是否每条都写了审计方法和证据要求,模板里是否强制要求输出文件行号 |
| 误报太多 | 列了一堆问题,实际可用的没几个 | 检查阶段三的交叉验证是否被AI跳过了;适当减少检查项,突出重点 |
| 上下文不够用 | 审计到一半,AI忘了前面的内容 | 把检查清单拆成references,按需查阅;不要把所有内容一股脑塞进SKILL.md |
最让我头疼的是第一个问题。后来发现,不是Skill没写好,而是我在对话里的措辞没有命中description里的触发场景。把description改得更贴近用户真实说法,比如加上“帮我看看项目有哪些安全问题”“做一次安全评估”,触发率立刻上来了。
4.3 几条让Skill越用越强的经验
最后分享一个我自己特别受用的操作习惯:每次审计结束后,我会把Agent新发现的问题回填到references的检查清单里。
比如有一次审计时,Agent发现了一个我原本没写进清单的问题:S3的Bucket策略允许公共读写。我确认这是一个真实风险之后,就在security-checklist.md里新增了一条云存储权限配置检查项。下次再审计类似项目,AI就会自动检查这个点。Skill就像个知识库,会随着使用越来越接近你所在团队的实际业务场景。
还有一个小技巧:如果你觉得当前项目的技术栈比较特殊,可以在references里为这个项目单独写一个notes文件,比如golang-gin-notes.md,记录gin框架里容易出现的路由鉴权问题。AI在审计这个项目时,会优先查阅这份笔记,审计效果会比通用清单好很多。
注意:不要指望一个Skill同时覆盖十几个领域。一个Skill聚焦一个具体任务,比如“安全审计”就只做安全审计,不要让它再去顺便重构代码。Skill越聚焦,AI执行得越稳。
5. 写在最后:给Agent写SOP,比给Agent下指令更重要
我折腾security-audit-skill这段时间,最大的收获不是这个Skill本身,而是我重新理解了Agent Skill的定位。以前我总觉得AI够聪明就行,给它一个指令它自己会发挥;但实际做下来发现,真正稳定好用的方法,是把成熟经验编写成结构化的SOP,再让AI在这个框架里去发挥。
安全审计恰好是一个特别适合验证这个思路的领域——它有清晰的检查项、有成熟的漏洞分类、有固定的报告格式,所有这些都能被沉淀成一份Skill。如果你也想自己写一个Skill,我建议从类似的、流程比较固定的任务开始。比如代码评审、日志分析、配置检查,都比“让AI帮我写个方案”这种开放任务更容易落地。
最后再分享一个细节:我每次更新Skill之后,都会用同一个测试项目回归跑一遍,确保新加的内容没有破坏原有流程。这个习惯帮我避免过好几次把Skill越改越乱的尴尬,你如果也开始维护自己的Skill,强烈建议试试。