FlawOpen

flawopen.com/SQL Injection/转义就够了吗?

转义引号能防止 SQL Injection 吗?

CWE-89: SQL InjectionReference page
简短回答

通常不能。转义函数依赖数据库、连接字符集、SQL 模式和具体的引号上下文。参数化查询通过驱动 API 将值与 SQL 语法分开。数据值应使用参数;无法绑定的动态标识符应从应用固定白名单中选择。

转义为何容易失效

连接字符集会影响转义

转义不是脱离上下文的文本替换。MySQL 文档指出,mysql_real_escape_string() 使用连接字符集,而 SET NAMES 不会更新该函数依赖的客户端库状态。字符集不一致会破坏对转义文本的假设。优先使用参数化 API;若旧代码暂时仍需转义,应通过驱动支持的函数配置字符集,并处理错误。

任何一次漏调用都会削弱防线

每个插值点都必须结合正确的驱动、字符集、SQL 模式和字符串字面量上下文进行转义。参数化 API 在查询调用处明确区分值与数据,避免手动拼接带引号的字符串。

转义引号无法覆盖所有 SQL 上下文

转义引号只处理特定字符串字面量中的字符。它不能安全构造表名、列名、关键字或排序方向,也不能保护未加引号的数字表达式。数据值应绑定参数;动态 SQL 结构应映射到应用固定选项。

VULNERABLE
// 用户输入被插入 SQL 语法
query = f"SELECT id FROM users WHERE name = '{name}'"
cursor.execute(query)
HARDENED
// 驱动将 name 绑定为数据值
cursor.execute(
    "SELECT id FROM users WHERE name = ?",
    (name,),
)

参数绑定改变了什么

参数化查询通过数据库 API 分别传递 SQL 结构和值。驱动可能使用原生预处理语句或其他安全实现,因此不要假定特定的协议传输顺序。关键是值作为数据处理,不会再被解析成 SQL 语法。以 Python DB-API 为例,应使用驱动的占位符并单独传值,不要用字符串格式化生成查询。

工程检查清单

常见问题

转义有时仍是合适工具吗?

对于无法绑定的标识符或 SQL 片段,只能使用驱动支持的引用 API,并配合应用固定白名单。不要对任意数据值使用通用转义。

驱动的转义函数有维护就安全吗?

它在文档说明的上下文中可能有帮助,但仍依赖数据库、连接设置、引用上下文及每个调用点的正确使用。处理数据值时,参数化查询仍是更安全的默认方案。

参考资料

另请参阅 SQL 注入(CWE-89):原理与各编程语言的防护方法 — flawopen.com 使用 ORM 足以防止 SQL 注入吗? Knex.js 能防止 SQL 注入吗? MyBatis ${} 与 #{}:防止 SQL Injection MySQLi 与 PDO:安全使用预处理语句 NHibernate 能防止 SQL 注入吗? MongoDB NoSQL 注入:安全查询过滤器