参数化查询是防止SQL注入的唯一可靠解法,因其将SQL结构与用户输入严格分离,避免手动转义因数据库差异、驱动解析、边界情况等导致的失效与漏洞。

Insert语句里含单引号、反斜杠、百分号等字符,别手动拼接转义——直接用参数化查询,这是唯一可靠解法。
为什么手动转义INSERT字符串注定出问题
手动替换'为''、为\,看似简单,但实际极易漏掉边界情况:比如字符串末尾带反斜杠、嵌套引号、或客户端驱动二次解析(如 JDBC 吃掉一层)。更麻烦的是,不同数据库对双引号、NULL字节、换行符的处理不一致,靠人肉适配成本高且不可维护。
常见错误现象包括:SQL syntax error near 'Reilly'、插入后数据截断、甚至意外触发 SQL 注入(尤其当输入来自表单或 URL 参数时)。
- MySQL 客户端可能提前解析一次反斜杠,导致
'O'Reilly'传到服务端变成O'Reilly,仍报错 - PostgreSQL 对
''转义严格,但若字段类型是JSONB或含 Unicode 组合字符,双单引号可能不够 - SQL Server 在
ANSI_NULLS OFF模式下,空字符串和NULL行为还会干扰转义逻辑
参数化查询怎么写才真正生效
关键不是“用了?或@param”,而是**整个值必须作为独立参数传入,不能拼进 SQL 字符串里**。哪怕只有一处拼接,就等于放弃防护。
正确示例(Python + psycopg2):
cursor.execute("INSERT INTO users (name, bio) VALUES (%s, %s)", ("O'Reilly", "dev\ops
v2"))
错误示例(哪怕只拼一个字段):
name = "O'Reilly"
sql = f"INSERT INTO users (name) VALUES ('{name}')" # 危险!
- Java JDBC 必须用
PreparedStatement,且调用setString()等方法传参,不能用Statement执行拼接字符串 - Node.js 的
pg库需用$1, $2占位符,且client.query(sql, [val1, val2])传数组,而非模板字符串 - PHP PDO 要禁用
PDO::ATTR_EMULATE_PREPARES(设为false),否则会退化为客户端模拟,失去防注入能力
什么情况下还得补一层转义逻辑
参数化能解决 95% 的问题,但有两类场景需额外注意:
一是LIKE子句中的通配符(_、%)——参数化只保值不保语义,WHERE name LIKE ?传入'user_name'仍会匹配userXname。
二是某些 ORM 或低代码平台生成的 SQL 封装层,可能把参数“自动展开”成字符串再执行(比如 Django 的extra()或 SQLAlchemy 的text()未绑定参数)。
- 对
LIKE,应用层应预处理:pattern = input.replace('_', '_').replace('%', '%'),再传入WHERE name LIKE ? ESCAPE '' - 检查 ORM 日志输出的实际 SQL,确认
?是否被真实保留,而不是变成'user_name'再执行 - 避免在存储过程中用
CONCAT()拼接参数后再EXEC,这等于绕过参数化
特殊字符本身要不要在业务层做校验
参数化不等于放任输入。像NULL字节(
