纯属个人经验,这些年我在产品线上接触过的AI生成代码案例,如果直接按“能用就行”的标准验收,十段里面至少有七八段是要回炉的。不是说AI写得不行,而是它写得“太平顺”——所有分支都按正常逻辑走,所有异常都返回200,所有外键都懒得校验。这种代码运行起来没毛病,但被盯上就是另外一回事了。
所以我一直坚持一个习惯:任何AI协助生成的代码,合并之前必须做一轮安全自查。完整安全评审没时间做,但5分钟的快速排查完全够用,前提是你知道该往哪儿看。下面这份清单,就是我在实践中反复迭代出来的版本,配合一段可以直接复制使用的审查Prompt,基本能覆盖绝大多数常见风险面。
1. 先从整体思路说起:为什么AI代码的安全问题都集中在几个固定位置
先说一个观察结论,这可能比清单本身更有用:AI生成的漏洞,绝大多数不是“它不知道安全知识”,而是“它在缺乏业务上下文的情况下,自动选择了最简单的实现路径”。什么意思?就是它宁可把逻辑写得看似完整但浅尝辄止,也不会主动去做那些需要业务数据才能完成的深度校验。这就是为什么AI代码的安全问题往往是同构的——它们集中在几个固定的、缺上下文就做不彻底的环节。
安全自查的核心思路,不是逐行读代码,而是围绕四个高风险环带快速扫描:
- 数据入口:凡是外部输入进入系统的位置,包括HTTP参数、请求体、文件上传、消息队列消息。
- 信任边界:登录态、Token、会话标识、权限判断这些“你是谁、你能干什么”的逻辑。
- 敏感操作:删除、更新、导出、支付、发送消息等业务敏感动作。
- 外部交互:数据库查询、命令执行、文件读写、第三方API调用。
只要你在这四个环带内做针对性检查,5分钟是够用的。反过来,如果你从头到尾把AI生成的每一行都精读一遍,那50分钟都打不住,而且极易漏掉真正的问题——人的注意力会在疲劳之后自动跳过风险区域。
2. 五步自查清单:从拿到代码到可以上线的5分钟快速通道
2.1 第一分钟:依赖与引入检查
AI生成的代码经常会引入第三方库。如果你发现它在import区域出现了一些不熟悉的库名,先停一下,花30秒确认两个问题:这个库是不是真的存在?它是不是满足最小引入原则?我遇到过某同事用AI生成图片处理模块,代码里引入了一个叫“image_utils_enhanced”的库,听起来人模人样,结果PyPI上一查根本不存在,属于典型的幻觉包名攻击诱饵。如果当时有人恶意注册了这个包名,装进项目里的就是别人的代码了。
这一分钟的检查动作很简单:把AI用到的外部库挨个搜一遍,确认能在官方源上查到,确认版本号是最新稳定版而不是什么奇怪的预览版。另外有一条红线——凡是让你去改requirements.txt或package.json加依赖的,必须逐一核对依赖的来源与协议,这个没有任何商量的余地。
2.2 第二分钟:凭据与敏感信息扫描
这个环节的检查命中率极高。AI在生成代码时,经常顺手在配置文件里打上一串看起来很真实的口令、Token或密钥。别不信,我见过AI在测试配置文件里生成一个格式完全正确的AWS Access Key ID,业务代码调它调得理所当然,要不是有一道安全扫描拦着,这个假密钥就以“明文硬编码凭据”的身份进入仓库了。
更隐蔽的问题是AI生成的代码会在日志里打印请求报文、数据库连接串,或者干脆把上游API返回的完整错误信息直接透传回前端。这一分钟的排查动作,建议用搜索代替人眼:在代码目录下搜索常见的密钥特征,比如“api_key”、“secret”、“token”、“password”、“BEGIN RSA PRIVATE KEY”这类关键词,逐个打开上下文确认没有敏感信息落盘。
2.3 第三分钟:输入处理与注入面排查
输入校验这一分钟是整个清单里最值的。AI生成的代码有一个典型的通病——只做前端或入口层的“格式校验”,不做“语义校验”。比如它能帮你生成一段“身份证号必须为18位”的正则,但不会帮你判断这个身份证号是否真实存在、是否属于当前操作人。这种表面校验在安全视角下和没校验差不了太多。
这一分钟重点排查两类典型的注入问题:
- 拼接型:SQL语句、命令行、XML拼接是不是用了字符串相加。如果看到
"select * from users where id = " + user_id这种代码,别犹豫,这属于AI的经典“省事”写法。同样要注意的是它生成的ORM代码是否正确用了参数化,而不是在某些边缘分支里退回原始拼接。 - 渲染型:返回值里有没有直接用
innerHTML、v-html或者把用户输入拼到HTML模板里。这类问题在AI生成的前端代码里高发。
2.4 第四分钟:鉴权与越权逻辑核对
越权漏洞(IDOR)是AI代码里出现频率最高的高危问题之一,但极难靠静态扫描发现。原因是AI生成代码时,根据你给的Prompt顺利成章地写了“按ID查询用户信息”的逻辑,但它并不知道这个查询前提应该是“当前登录用户”。这一分钟的检查动作分为两个小步:
第一步,看到任何一个“按ID操作”的接口,问自己一个问题:这个ID是当前用户可控的吗?如果可控,后续代码里有没有校验该ID对应的数据归属当前用户?没有跨权限判断逻辑的,一律标记为待修复项。
第二步,检查AI生成的鉴权装饰器或中间件是否真正全局生效。我见过AI生成一个@login_required装饰器优雅地挂在视图函数上,结果路由表里一半接口忘了加,那部分接口就成了裸奔状态。别信装饰器,去路由表里逐一确认。
2.5 第五分钟:异常处理与未知面扫描
绝大多数AI代码的最薄弱环节不是功能逻辑,而是异常分支。AI默认假设一切顺利,所以它的异常处理都是“打日志然后返回一个友好错误”这种程度。真正需要你特别留神的是异常信息本身——如果直接返回Exception对象的字符串内容,就可能在页面上渲染出堆栈信息、SQL片段、文件路径。这些信息对攻击者来说是免费的情报。
第五分钟的最后30秒,用一个重构之后代码库里的高亮工具或IDE的“未使用标记”扫一遍,看有没有明显一段代码是AI写来“营造完整性”但实际上从未被调用的。死代码本身不是漏洞,但它通常说明AI在某个地方做了无用功,而无用功的下游往往跟着逻辑缺失。
3. 可复制的安全审查Prompt:让AI成为你的初筛助手
3.1 Prompt完整模板
下面是经过多轮迭代之后,我的日常使用版Prompt模板。它不负责替你思考,只负责把AI从“什么都说两句”变成“只挑高危的说”。你可以直接复制的版本如下:
角色设定:你是资深应用安全工程师,正在执行一次代码上线前的快速安全评审。你不是代码生成器,不需要修复代码、解释功能或提供替代实现,只需要找出安全问题。 输入范围:我接下来会提供一段或多段代码。请仅基于提交的代码内容做评审,不要推测代码之外的信息,不要假设不存在的依赖、权限或环境。 输出格式:严格按照以下格式输出,不要添加任何额外描述: 【高风险】问题简述;对应代码行号;为什么这是风险 【中风险】问题简述;对应代码行号;为什么这是风险 【低风险】问题简述;对应代码行号;为什么这是风险 优先级排序:高风险在前。如果不存在某类风险,则输出“未发现XX级风险”。 重点关注清单: 1. 外部输入是否经过有效校验,是否存在直接拼接到SQL、命令行、HTML的情况 2. 是否在代码中硬编码任何密钥、Token、口令、连接字符串 3. 是否存在任何形式的越权访问,例如按ID查询数据但未校验数据归属 4. 异常信息是否可能泄露堆栈、路径、SQL片段等内部信息 5. 是否使用了不常见的第三方库,尤其是无法确认来源的库 6. 新生成的代码是否为内部可访问接口提供了足够认证与授权 禁止事项: - 不要输出“代码整体质量不错”这类总体评价 - 不要输出与安全无关的风格建议、性能建议、架构建议 - 不要修复代码或重写实现,只需要指出问题 - 不要建议引入额外依赖来解决安全问题在提供代码之前,先说明代码的语言和框架环境,比如“这是一段FastAPI的视图层代码”,然后附上代码块,一次控制在100~300行之间效果最好。代码太多时AI的注意力会分散,漏报率肉眼可见地上升。
3.2 Prompt每一段的用意拆解
这个Prompt看起来平淡,但每一段都在解决实际踩过的坑。
“角色设定”那一段如果不明确“你不是代码生成器”,你会发现AI输出里有三成篇幅都在热心帮你重写代码,而不是做评审。它越热衷于帮忙,你越难从它那里得到冷静的风险判断。
“输入范围”这一段解决的是幻觉问题。AI默认会脑补一套权限模型和外层防御逻辑,然后基于脑补结果把代码评审成“虽然看起来有问题但外部防御会兜住”的安全状态,这非常危险。必须明确告诉它你没有提供的信息不要猜,让它基于裸代码做评审。
“输出格式”这一段的价值在于:人眼扫描“风险结果”比扫描“风险论述”快一个量级。结构化输出加上行号定位,可以直接按图索骥。而且优先级排序避免了AI端出一堆“可能”“也许”“建议留意”的低质量信息,那些委婉表达在安全评审里就是添乱。
“重点关注清单”实质上是一个微型安全知识库。AI默认的漏洞知识偏向于教科书案例,而这份清单把企业应用里最高发的几类问题拧成要点,把AI的审查注意力集中到最有价值的方向上去。如果你所在团队有额外的关注点,比如容器权限、第三方API密钥轮换策略,可以在这个清单里加减,它会直接影响AI的判断侧重点。
“禁止事项”是全篇的灵魂。AI在未被禁止时,几乎必然在输出末尾加上“建议持续关注运行时安全”“建议加强日志审计”这类正确的废话,这些废话会让真实的问题被淹没。删掉这些噪声,它才能把精力放在你真正需要的东西上。
3.3 Prompt使用细节:什么情况该调整,什么情况不该
这套Prompt适用于请求体、视图函数、工具函数这类小体积代码的评审。它有明确的舒适区:一次只审一个文件或一个函数。一场代码评审如果塞进5个文件,AI对每个文件的投入必然是均匀摊开的——它不会像人一样懂得“这个文件更关键所以多花点精力”,结果就是所有文件里的高危问题都被“平均化”成了中等异常。
有一种情况需要给Prompt加一条特殊补充:评审变更代码时,你可以追加一句“仅审查新增或修改的代码行,忽略未修改的历史代码”。这句话能显著降低误报率,因为AI经常会把已有旧代码的问题也算到你头上。
还有一种情况是项目本身使用了特殊框架,比如内部自研的ORM或权限框架。这时最好在“重点关注清单”里补充一条“注意代码是否符合XX框架的规则”,否则AI既要理解框架又要判断安全,准确率会明显打折。
4. 实测里最常踩的坑:AI审查Prompt翻车记录
4.1 “invalid prompt”是怎么出现的
我在尝试调整Prompt时遇到过几次输出被拦的情况,提示就是类似“invalid prompt: your prompt was flagged as potentially violating our usage policy”。起初以为是触发了某种敏感词规则,排查后发现根源是Prompt里的示例代码写得过于“真实”了。当时为了测试越权问题,我在Prompt里直接写了一个带“password”字段的SQL查询示例,结果被模型服务方的策略过滤器判定为诱导生成不安全内容,把整个请求直接拦截了。
这个问题的解决办法不复杂:不要在审查Prompt里插入完整的“危险代码示例”,尤其是包含真实攻击载荷形态的那种。要测试审查能力,用纯粹的、移除敏感字段的简写代码或伪代码即可。你完全可以说“存在一个拼接SQL的字符串变量”,而不必真的写出"SELECT * FROM users WHERE password='xxx'"这种文本。
建议在服务端日志尚未确认前,谁也别轻易断言“这个AI审查能力不行”——很多时候不是AI不行,是提示词的内容触发了策略层面的审前拦截。排查这类问题时,先简化Prompt里的示例代码,分段提交,定位是哪一段导致拦截,比反复调整格式参数更高效。
4.2 AI审查的“误报与漏报”博弈
在实际用这套Prompt的第二周,我发现了一个有趣的现象:AI的误报率开始上升。它会把某些正常的写法标注为风险,比如把“Pydantic模型自动校验”当成缺失输入校验。原因不是Prompt失效了,而是我对它的输出格式期望和它的判断逻辑产生了偏差。它严格遵守了“只要存在外部输入,就标注高风险”的规则,但实际上在FastAPI这类框架里,框架级别的校验和序列化会在视图函数执行前完成拦截,代码本身没有漏洞。
这种误报的正确处理方式,不是删掉Prompt里的校验关注项,而是在代码上下文里补充框架信息解决。比如提供代码前加一句“视图函数之前已有Pydantic模型处理基础字段校验,请在JSON Body部分只做业务逻辑层的校验审查”,AI的误报会立刻大幅下降。
漏报的情况则更微妙。在某次评审一段递归函数时,AI完全没有发现它缺少权限传递校验。回头反思,发现递归把数据流转拉得太长,AI的注意力集中在单层逻辑上,对深层调用链的安全性做不了有效判断。这不是Prompt能完全解决的,毕竟模型本身的推理深度有限。我的对策是:凡是递归、循环回调、消息队列消费者这类代码,不依赖AI评审,直接人工走查。
4.3 审查清单和Prompt怎么配合使用
5分钟清单和审查Prompt不是二选一的关系,它们各管一段。快速清单适合人肉扫描,Prompt适合让AI做初筛。我个人的工作流是:AI生成代码之后,先把代码切成100~300行的块,提交给审查Prompt拿一份结构化风险列表,然后拿着风险列表和源码做人工复核,重点看列表里的高风险和中风险项。最后再用五分钟清单快速过一遍,确认没有AI漏掉的方向。
这套组合拳下来,比起以前直接人肉Review,最大的改变是注意力分配合理了。以前做安全评审,总担心自己扫漏了哪块;现在有了AI初筛和高风险点的强制校验习惯,精力可以集中在最值得人判断的地方。
5. 关于安全自查的几个额外体会
自查清单本质上是一层“习惯约束”,它的可靠性取决于你是不是真的执行,而不是执行得多么花哨。我见过有人把这份清单背得滚瓜烂熟,上线前却不翻开代码看一眼,到头来还是那句“应该没问题”在撑着一切。这行里从来没有“应该没问题”这回事,只有“查过了”和“没查过”两种状态。
另一个体会是Prompt的质量和你投入的时间成正比。起初我花了一个下午调Prompt,觉得浪费时间;后来一个接近0day的风险点被它用三秒钟揪了出来,那个下午的成本就值回来了。用AI做安全审查,本质上是在训练一个不知疲倦的初筛器,调教得越具体,它给你的回报越实在。
最后想说,AI生成代码的普及已经不可逆,安全自查这件事必须向前移,移到合并请求之前、提交到测试环境之前。5分钟看起来短,但落在正确环节上,可能省掉的是后面一整天排查线上问题的时间。这份清单和配套Prompt算是我个人实践沉淀下来的止损工具,希望能让你少踩几个我踩过的坑。