注入:当输入变成了代码
注入是最古老、至今仍最常见的严重漏洞。它发生在用户提供的数据被当成指令而不是数据来处理的时候。对大多数应用来说,有两种类型值得关注。
SQL injection(SQL 注入)是用户输入混进了数据库查询。经典的脆弱写法:
// VULNERABLE: 用户的输入被直接粘进了查询里
app.get("/user", (req, res) => {
const name = req.query.name;
db.query(`SELECT * FROM users WHERE name = '${name}'`);
});
// 如果有人传入 name = '; DROP TABLE users; --
// 你的查询就变成了一条删表的命令。
修复办法是用参数化查询(也叫预编译语句)。你把查询和数据分开发送,这样数据库就永远不会把其中一个错当成另一个:
// SAFE: 值作为参数传入,永远不作为代码
app.get("/user", (req, res) => {
const name = req.query.name;
db.query("SELECT * FROM users WHERE name = ?", [name]);
});
规则是:永远不要用字符串拼接来构造查询。 如果你在 diff 里看到反引号或 + 把 SQL 拼在某个变量周围,停下来,要求改成参数化的版本。
XSS(cross-site scripting,跨站脚本) 是同一个思路在浏览器里的版本。如果你拿用户输入、把它当作原始 HTML 塞进页面,攻击者就能注入一个 <script> 标签,让它在你其他用户的浏览器里运行——比如窃取他们的会话。修复办法是转义:把用户内容当作文本来渲染,而不是 HTML。像 React 这样的现代框架默认会转义,这很棒——直到 AI 为了“让它能跑”伸手去用 dangerouslySetInnerHTML 或 innerHTML 这种逃生口。这些会绕过保护。在 diff 里看到任何这类调用,都要当成需要质疑的东西。
每个注入漏洞背后统一的原则是:让数据和代码保持分离。 每当用户输入要进入一个查询、一条命令、一个模板或 HTML 时,总得有东西去转义它或参数化它。