真正防住资源耗尽型sql注入必须关闭pdo::attr_emulate_prepares=>false以启用服务端预处理,配合max_execution_time限制查询时长、动态标识符白名单校验及最小权限授权。

单纯靠“过滤关键词”或“加个 sleep()”挡不住资源耗尽型 SQL 注入,真正有效的防线是服务端预处理 + 资源限制 + 权限收敛,三者缺一不可。
为什么 PDO::ATTR_EMULATE_PREPARES = true 会让 CPU 和连接数暴增
开启模拟预处理时,PHP 在本地拼接 SQL 并转义,每条查询都走完整解析流程,且无法复用执行计划。攻击者只要发一堆 SELECT SLEEP(10) 或 SELECT * FROM huge_table ORDER BY RAND(),就能让 MySQL 线程卡死、连接池打满。
- 必须显式关闭:
PDO::ATTR_EMULATE_PREPARES => false,强制走 MySQL 服务端预处理 - 验证是否生效:执行
$pdo->prepare("SELECT ?"),若抛出SQLSTATE[HY000]: General error,说明服务端真在编译;若静默成功,大概率还在模拟 - MySQL 5.7+ 默认支持,但若
max_prepared_stmt_count = 0或权限不足,会自动退化为模拟模式,需检查SHOW VARIABLES LIKE 'max_prepared_stmt_count'
如何限制单条查询的 CPU 时间和扫描行数
MySQL 本身不提供 CPU 时间配额,但可通过 max_execution_time(MySQL 5.7.8+)和 optimizer_search_depth 控制查询暴力程度,再配合 sql_mode 阻断危险行为。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
- 对连接级生效:执行
SET SESSION max_execution_time = 2000(单位毫秒),超时自动中断查询 - 禁止全表扫描:在 my.cnf 中设置
sql_mode = STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,ERROR_FOR_DIVISION_BY_ZERO,并确保optimizer_search_depth不为 0 - 慎用
SQL_BIG_RESULT提示——它会强制使用临时表,容易撑爆内存,除非你明确需要
哪些动态部分根本不能参数化?必须白名单硬控
ORDER BY、GROUP BY、表名、列名、LIMIT 的 offset/row_count,这些语法位置无法用 ? 或 :param 占位,拼进去就等于开门揖盗。
-
ORDER BY字段只能从白名单数组里取:$allowed_sorts = ['created_at', 'status', 'score']; if (!in_array($_GET['sort'], $allowed_sorts)) die('invalid sort'); -
LIMIT的两个参数可用intval(),但必须额外校验:$limit = min(max(intval($_GET['limit']), 1), 100); $offset = max(intval($_GET['offset']), 0); - 表名/列名绝不能靠正则过滤或
addslashes()——宽字节、反斜杠逃逸、多字节编码都能绕过,只允许映射到预定义常量或枚举值
为什么最小权限比 WAF 更关键
WAF 只能拦住已知特征的恶意 payload,但对 SELECT COUNT(*) FROM huge_log_table 这类合法却耗资源的语句完全无效。真正起效的是数据库账户层面的权限切割。
- 业务账号禁用
FILE、PROCESS、SUPER权限,连SHOW DATABASES都不该有 - 只授予具体表的
SELECT、INSERT,不用GRANT ALL ON *.* - 对读写分离架构,从库账号甚至应禁用
UPDATE和DELETE,防止误操作或注入后篡改
最容易被忽略的点是:预处理没关模拟模式、max_execution_time 没设 session 级、动态字段没白名单——这三处任意一个失效,攻击者就能用一条看似正常的查询拖垮整个数据库。防资源耗尽,本质是把“能跑多慢”和“能扫多广”这两件事,在代码、驱动、配置三个层面全部锁死。










