位置:首页 > SQL > 如何避免把用户输入直接拼接到 SQL 语句

如何避免把用户输入直接拼接到 SQL 语句

时间:2026-08-25  |  作者:宇宙开黑者  |  阅读:0

唯一可靠的方式是彻底放弃字符串拼接,改用数据库驱动原生支持的参数化查询(Prepared Statements),必须禁用模拟预处理、严格校验动态表名列名白名单,并确保类型绑定正确。

如何避免用户输入直接拼接到SQL语句

唯一可靠的方式是彻底放弃字符串拼接,改用数据库驱动原生支持的参数化查询(Prepared Statements)。任何过滤、转义、正则黑名单,都挡不住真实攻击。

为什么 mysql_real_escape_stringaddslashes 不能防注入

它们仅仅是给单引号加上反斜杠,然而攻击者却能够绕过这种方式:采用十六进制编码(0x61646d696e)、利用注释符 /**/ 拆开关键字、通过双写 seleselectct 来触发解析器误判。更为关键的是,这类函数对于数字型参数完全不起作用——输入 1 OR 1=1 不包含单引号,却依然能够生效。

实际场景中,只要看到代码里出现 "WHERE id = " + user_id'username=' + name,就已处于高危状态。

PDO::prepare()mysqli_prepare() 的正确用法

必须确保底层真正启用预编译,而不是模拟预处理(emulated prepares)。PHP 默认开启模拟模式,它会把参数拼回 SQL 字符串再发给 MySQL,等于白做。

  • 显式关闭模拟:$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false)
  • 确认连接使用的是原生预处理:执行 SELECT @@session.prepared_stmt_count,有值增长说明生效
  • 参数只能用于值(WHERE name = ),不能用于表名、列名、排序字段——这些必须走白名单校验

Python cursor.execute() 常见错用

很多人以为传入元组就安全了,但错在用错了占位符或类型不匹配:

  • %s 占位符时,必须传元组或列表:cursor.execute("SELECT * FROM t WHERE id = %s", (user_id,)) —— 注意末尾逗号,否则 (user_id) 是 int 不是 tuple
  • 绝不能用 f-string 或 .format() 拼接:f"WHERE id = {user_id}""WHERE id = {}".format(user_id) 都是高危
  • PostgreSQL 的 psycopg2%s,不是 ;SQLite 两者都支持,但保持统一更稳妥

动态字段(表名、ORDER BY)怎么处理

这些无法参数化,硬塞 会报错,然后有人退回去拼字符串——这是最危险的临界点。

可行方案只有白名单校验:

  • 定义允许的列名集合:allowed_sort_fields = {"created_at", "name", "status"}
  • 校验输入是否严格属于该集合:if sort_field not in allowed_sort_fields: raise ValueError("Invalid sort field")
  • 表名同理,不能靠“过滤掉 ;”或“只允许字母”,而要穷举合法值

真正的难点不在语法,而在业务变化时忘记同步更新白名单——这比拼接字符串更隐蔽,也更难审计。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多