php如何防止sql注入_php防止sql注入过滤手段【防护】

12次阅读

唯一防sql注入的方法是参数化查询,因字符串拼接无论加何种转义均可能被编码绕过;需分离SQL结构与用户数据,pdo更推荐且应禁用模拟预处理,动态标识符须用白名单校验。

php如何防止sql注入_php防止sql注入过滤手段【防护】

mysqli::prepare()PDO::prepare() 是唯一可靠方式

手写字符串拼接 SQL(哪怕加了 addslashes()mysql_real_escape_string())本质上无法覆盖所有编码绕过场景,php 官方早已废弃 mysql_* 系列函数。真正防注入只有一条路:参数化查询。

关键不是“过滤输入”,而是“分离代码与数据”——SQL 语句结构由开发者固定,用户输入只作为参数传入,数据库驱动层自动处理转义和类型绑定。

  • PDO 更推荐:支持多种数据库、默认强制模拟预处理(可关)、错误模式易调试
  • mysqli 原生预处理也安全,但需显式调用 bind_param(),类型字符(s/i/d/b)必须严格匹配
  • 不要混用:比如用 PDO::quote() 拼接 SQL,它只是加引号+转义,仍属字符串拼接,不防注入

为什么 htmlspecialchars()strip_tags() 对 SQL 注入完全无效

这两个函数作用于 HTML 输出上下文,用来防 xss;它们对 SQL 解析器毫无意义。数据库收到的是 http 请求体里的原始字符串,不是浏览器渲染后的 HTML。

常见误用:

立即学习PHP免费学习笔记(深入)”;

  • 把用户输入先过 htmlspecialchars() 再塞进 SQL —— 数据库看到的是 zuojiankuohaophpcnscriptyoujiankuohaophpcn 这种字面量,既没防注入,又污染了存储内容
  • 在 SQL 查询前用 strip_tags() 清洗 —— 同样只是删 HTML 标签,' OR 1=1 -- 这类 payload 完全不受影响
  • 以为“前端加了校验就安全”—— 前端 js 可被绕过,后端必须独立验证 + 参数化

动态表名/字段名不能参数化,必须白名单硬编码

PREPARE 语句只允许参数化「值」,不允许参数化「标识符」(如表名、列名、ORDER BY 字段、LIMIT 偏移量)。试图用问号占位符替换表名会直接报错:

PDOException: SQLSTATE[HY093]: Invalid parameter number

正确做法是:提取用户意图(如排序字段),映射到服务端预设的白名单中。

  • 错误:$sql = "select * FROM ? WHERE status = ?"; → 语法错误
  • 错误:$table = $_GET['table']; $sql = "SELECT * FROM {$table}"; → 直接执行任意表
  • 正确:$allowed_tables = ['users', 'orders', 'products']; $table = in_array($_GET['table'], $allowed_tables) ? $_GET['table'] : 'users';
  • 同理,ORDER BY 字段必须限定为 ['id', 'created_at', 'name'] 等明确列表,不可拼接用户输入

使用 PDO 时别忽略 PDO::ATTR_EMULATE_PREPARES 设置

MySQLi 默认走真实预处理,PDO 默认开启模拟预处理(即 PHP 层做字符串插值),虽仍比手动拼接安全,但存在极少数边界绕过可能(尤其旧版 MySQL + 多字节编码)。生产环境应关闭模拟:

$pdo = new PDO($dsn, $user, $pass, [     PDO::ATTR_EMULATE_PREPARES => false,     PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]);

注意:关闭后若遇到 SQLSTATE[HY093] 错误,大概率是参数个数/顺序/绑定方式不对,不是配置问题。

另外,PDO::quote() 仅用于生成安全的字符串字面量(如动态构建 SQL 片段中的值),绝不能替代 prepare/bind 流程;且它不处理整数、NULL 等类型,需自行判断。

真正的防护不在过滤手段多寡,而在是否让数据库引擎彻底区分“语句结构”和“运行时数据”。其他所有“过滤”“替换”“截断”都是补丁,只有预处理是根治。

text=ZqhQzanResources