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 为例,应使用驱动的占位符并单独传值,不要用字符串格式化生成查询。
工程检查清单
- ✓所有用户控制的数据值都通过驱动 API 绑定;不要格式化进 SQL 文本。
- ✓动态表名、列名和排序方向必须从应用固定白名单中选择。
- ✓若暂时无法移除旧式转义,应使用驱动提供的上下文相关 API,并核对连接字符集和 SQL 模式。
- ✓使用引号、非 ASCII 文本和无效标识符测试查询构造;不要发送破坏性语句。
常见问题
转义有时仍是合适工具吗?
对于无法绑定的标识符或 SQL 片段,只能使用驱动支持的引用 API,并配合应用固定白名单。不要对任意数据值使用通用转义。
驱动的转义函数有维护就安全吗?
它在文档说明的上下文中可能有帮助,但仍依赖数据库、连接设置、引用上下文及每个调用点的正确使用。处理数据值时,参数化查询仍是更安全的默认方案。