参数化查询是唯一安全的通用解法,它由数据库驱动在协议层完成类型绑定与字符转义,将用户输入作为纯值处理,彻底隔离单引号、分号、--、/*等注入载体,避免字符串拼接导致的语法错误与SQL注入。

参数化查询是唯一安全的通用解法
手动处理单引号永远有漏网之鱼,而参数化查询由数据库驱动在协议层完成类型绑定与字符转义,不依赖字符串拼接逻辑。你传进去的是值,不是 SQL 片段,单引号、分号、--、/* 全部自动隔离。
常见错误现象:用 userInput.replace("'", "''") 拼接后看似能查 O'Reilly,但遇到 O'Reilly'; DROP TABLE users-- 就直接执行注入语句。
- Java JDBC:
SELECT * FROM users WHERE name = ?+stmt.setString(1, userInput) - Python psycopg2:
cursor.execute("SELECT * FROM users WHERE name = %s", [userInput]) - PHP PDO:
$stmt->execute(['name' => userInput])(占位符用:name或?均可) - C# ADO.NET:
cmd.Parameters.AddWithValue("@name", userInput),SQL Server 建议显式指定SqlDbType.NVarChar避免隐式转换失败
必须拼字符串时,只允许用双单引号 '' 转义
这是 SQL 标准定义的唯一跨库兼容写法,PostgreSQL、SQL Server、Oracle、SQLite、MySQL(ANSI 模式下)全部支持。\' 是 MySQL 扩展,E'' 依赖 standard_conforming_strings 配置,在生产环境极易因部署差异出错。
使用场景仅限:日志语句、静态配置、DDL 脚本生成等完全可控、无用户输入的上下文。
- 正确示例:
WHERE title = 'O''Reilly & Associates'(&不需转,只有单引号要双写) - 错误示例:
WHERE title = 'O\'Reilly'(PostgreSQL 直接报错;MySQL 在 STRICT 模式下失效) - 多引号场景:
'John''s "quote" isn''t right'(所有单引号都成对,双引号无需处理)
LIKE 模糊查询中单引号的位置很关键
LIKE 的模式字符串本身仍是 SQL 字符串字面量,所以通配符周围的单引号也必须遵守双写规则。常见错误是只转义值,却忘了 LIKE 的引号边界。
错误写法:WHERE name LIKE '%O'Reilly%' → 解析器在第一个 ' 就结束字符串,后面 O'Reilly% 变成非法 token。
- 正确写法:
WHERE name LIKE '%O''Reilly%' - 更安全写法(推荐):
WHERE name LIKE ?+ 绑定"%O'Reilly%"(驱动自动处理整个字符串) - 注意:
ESCAPE子句只影响%和_的解释,对单引号转义无效
ORM 并不自动防住所有单引号风险
Sequelize、Prisma、TypeORM 等对模型字段查询默认参数化,但一旦调用 query()、$queryRawUnsafe 或动态拼接 ORDER BY、表名、列名,就退出安全区。这些地方无法用参数化,单引号问题会以更隐蔽的方式复现。
典型漏洞点:sequelize.query(`SELECT * FROM users WHERE region = '${req.query.region}'`) —— 用户传 region=' OR 1=1-- 即可绕过条件。
- 修复方式:改用
replacements或bind机制,如sequelize.query("SELECT * FROM users WHERE region = :region", { replacements: { region: req.query.region } }) - 标识符(表名/列名)不能参数化,必须白名单校验或用数据库特定转义:PostgreSQL 用双引号
"user_name",SQL Server 用方括号[user name] - JSON 字段里再嵌套 SQL 片段?别这么干。这种嵌套层级下,
replace("'", "''")极易漏掉某一层的引号
真正麻烦的不是怎么写两个单引号,而是你不确定那串“输入”是否已经被某个中间件、日志框架或监控 SDK 预处理过——这种边界模糊的场景下,只有参数化查询能守住底线。










