典型绕过手法包括空格替代(%09、/**/)、单引号绕过(char(39)、0x27)、关键词混淆(大小写、内联注释)、大小写差异利用及编码变形;单纯过滤特殊符号无效,因数据库支持多样的字符串构造与解析逻辑;唯一可靠防御是preparedstatement预编译。

SQL注入绕过 WHERE 条件的典型手法有哪些
攻击者常利用空格、注释符、编码变形绕过基础过滤,比如把 1 OR 1=1 改成 1%0aOR%0a1%3D1(换行+URL编码),或用 /**/ 替代空格,使正则匹配失效。更隐蔽的是用内联注释 /*!50000SELECT*/ 触发MySQL特定版本解析,绕过只检测 SELECT 字面量的规则。
- 空格被过滤时,用
%09(Tab)、%0a(LF)、/**/、+替代 - 单引号被拦截,尝试用
CHAR(39)、0x27或双引号(若后端未统一转义) - 关键词如
UNION被关键词黑名单拦截,可用大小写混写UnIoN、内联注释/*!UNION*/、或嵌套括号(UNION) - 某些WAF对
information_schema敏感,但放行INFORMATION_SCHEMA(大小写差异)或别名schema
为什么单纯过滤特殊符号根本防不住SQL注入
靠正则删掉 '、--、/* 这类“危险字符”是无效的——现代数据库支持多种字符串拼接、编码、函数调用方式,不依赖单引号也能构造有效payload。例如 PostgreSQL 中 CHR(39)||'admin'||CHR(39) 可动态生成带引号的字符串;MySQL 的 CONCAT(0x27,'admin',0x27) 同理。过滤层在应用层,而SQL解析在数据库层,二者语义不一致,必然存在gap。
- 过滤发生在请求解析后,但数据库会先做编码解码、变量展开、预处理语句绑定,顺序错位
- 不同数据库对空白符、注释、大小写的处理逻辑不同,一套正则无法覆盖 MySQL/PostgreSQL/SQL Server 全部行为
- 前端JS校验、Nginx
mod_security规则、甚至WAF都可能被绕过,因为它们看不到参数实际进入SQL时的上下文
PreparedStatement 是唯一可靠的防御手段
所有语言的标准数据库驱动都提供预编译接口,它把SQL结构和数据彻底分离:查询模板由数据库服务端预解析并缓存执行计划,参数值以二进制协议传入,绝不会参与SQL语法分析。这意味着哪怕你传入 admin' OR '1'='1,数据库也只把它当一个字符串字面量,不可能改变原有WHERE逻辑。
- Java 必须用
Connection.prepareStatement()+setString(),禁用Statement.execute()拼接 - Python 的
sqlite3、psycopg2、pymysql都要求使用%s占位符(不是字符串格式化!),如cursor.execute("SELECT * FROM user WHERE name = %s", [name]) - PHP 的
PDO::prepare()和mysqli_prepare()同理,严禁用mysql_real_escape_string()(已废弃且不安全) - Node.js 的
pg、mysql2库默认启用预处理,但需确认配置项prepare: true已开启(尤其连接池场景)
深度策略该拦什么、不该拦什么
如果必须加WAF或网关层规则(比如遗留系统无法改代码),重点应放在**异常流量模式识别**,而非字符黑名单。例如连续5次响应体含 mysql_fetch 错误、同一IP在1秒内发起10+含 UNION SELECT 的请求、或URL参数长度突增到2KB以上且含大量嵌套括号——这些比拦 ; 有用得多。
- 允许
SELECT、WHERE等关键词出现,但对连续出现多个SQL子句(如UNION SELECT ... FROM ... WHERE)做频率限速 - 记录并告警非常规编码组合:如参数中同时含
%25(%编码)和0x十六进制字面量 - 禁止
information_schema.tables这类元数据表名出现在非管理员接口,但不要拦schema字样(可能是业务字段名) - 对
ORDER BY后的参数必须强制类型转换为整数,防止注入1, (SELECT password FROM user)
真正难处理的是那些需要动态拼接SQL的场景,比如多条件搜索、权限字段白名单控制。这时候没有银弹——要么引入表达式AST解析器做安全重写,要么把拼接逻辑收归DBA审核的存储过程中,而不是丢给应用层自由发挥。











