{
  "id": "ai-writes-insecure-sql",
  "type": "failures",
  "category": "failures",
  "locale": "zh",
  "url": "/zh/failures/ai-writes-insecure-sql",
  "title": "如何修复AI编写的不安全SQL",
  "description": "AI代理使用字符串插值而不是参数化语句构建SQL查询，从而将SQL注入漏洞引入生产数据库代码中。",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "PostgreSQL",
    "TypeScript"
  ],
  "tags": [
    "sql",
    "security",
    "postgres"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "代理使用模板字面量构建SQL查询，创建了经典的SQL注入向量，这些向量在测试中看似正常，但在生产环境中可被利用。\n\n## 症状\n\n用户控制的值直接插值到SQL字符串中。\n\n```ts\n// WRONG — SQL injection vulnerability\nasync function getUserByEmail(email: string) {\n  const result = await db.query(\n    `SELECT * FROM users WHERE email = '${email}'`\n    //                                  ^^^^^^^ attacker-controlled\n  );\n  return result.rows[0];\n}\n\n// Attacker input: ' OR '1'='1\n// Resulting query: SELECT * FROM users WHERE email = '' OR '1'='1'\n// Returns every row in the table.\n```\n\n## 发生原因\n\n模板字面量是JavaScript中最自然的字符串构建工具，模型训练时使用的许多教程示例都使用它们来构建SQL语句而不进行参数化。代理也没有模拟恶意输入——它假设传递的是格式良好的数据。\n\n## 如何识别\n\n- 包含 `${...}` 插值的SQL字符串。\n- 接受用户输入并将其传递给原始查询助手但没有单独参数数组的查询函数。\n- 使用单个参数调用 `db.query(sql)` 而不是 `db.query(sql, [params])`。\n- `LIKE '%${term}%'` 模式。\n\n## 如何修复\n\n始终使用参数化查询。数据库驱动处理转义；您的代码从不接触引号。\n\n```ts\n// CORRECT — parameterized query (node-postgres / pg)\nasync function getUserByEmail(email: string) {\n  const result = await db.query(\n    \"SELECT id, name, email FROM users WHERE email = $1\",\n    [email]  // second argument: params array\n  );\n  return result.rows[0] ?? null;\n}\n\n// CORRECT — with Postgres.js (template tag)\nasync function searchUsers(term: string) {\n  return sql`SELECT id, name FROM users WHERE name ILIKE ${\"%\" + term + \"%\"}`;\n  // postgres.js automatically parameterizes template expressions\n}\n```\n\n```txt\n[ ] No ${...} inside raw SQL strings — use $1/$2 placeholders instead\n[ ] Every db.query() call passes user input via the params array, not the SQL string\n[ ] Use an ORM (Prisma, Drizzle) or query builder for complex queries\n[ ] LIKE wildcards are appended in the param value, not concatenated into the SQL\n[ ] Run sqlfluff or a SQL linter in CI to catch interpolated strings\n```\n\n## 修复提示\n\n```txt title=\"Fix Prompt\"\nThis SQL query uses string interpolation with user-supplied values, which is a\nSQL injection vulnerability. Rewrite every raw query to use parameterized\nstatements ($1, $2 placeholders for pg, or the tagged template literal form for\npostgres.js). Never interpolate variables directly into SQL strings. If the\nquery is complex, migrate it to Prisma or Drizzle ORM instead.\n```\n\n## 测试\n\n```bash\n# Detect template literal interpolation inside SQL-looking strings\ngrep -rn 'query(`\\|sql`\\|execute(`' --include=\"*.ts\" --include=\"*.tsx\" . \\\n  | grep '\\${' \\\n  | grep -v \"node_modules\" \\\n  && echo \"FAIL: interpolated SQL found\" || echo \"OK\"\n```"
}