手动过滤关键字总被绕过,因攻击者通过大小写混写、注释分割、url编码、空格替换、双写嵌套等方式使union/select等不以原始形态出现;参数化查询才是唯一可靠防御,因其在协议层分离sql结构与数据。

手动过滤关键字为什么总被绕过
因为攻击者根本不在意你拦了哪些词。只要输入能进数据库执行,他们就有几十种方式让SELECT、UNION、OR 1=1这些“关键词”不以原始形态出现。
常见绕过现象包括:
- 大小写混写:
SeLeCt、uNiOn逃过简单in判断 - 注释符干扰:
/**/UNION/**/SELECT把关键词切开,正则难匹配 - URL编码:
%55NION(U被编码)、%27(单引号)绕过字符串检测 - 空格替换:
UNION%09SELECT用制表符替代空格 - 双写或嵌套:
UNIunionON、SELSELECTECT让过滤器误判为“非关键词”
这类过滤本质是“黑名单对抗”,而黑名单永远追不上攻击者的新手法。它既不能覆盖所有数据库方言(MySQL/PostgreSQL/SQL Server语法差异大),也无法处理多字节编码、宽字节注入等底层问题。
数据库驱动的转义函数真能用吗
可以,但仅限于**字符串值上下文**,且必须由驱动原生支持、在参数绑定前调用。比如 PHP 的 mysqli_real_escape_string()、Python 的 pg_quote_literal()、Java 的 StringEscapeUtils.escapeSql()(Apache Commons,已弃用)。
但要注意三点:
- 它们只对单引号、反斜杠、
\0等做转义,对数字型参数、布尔型参数、列名/表名完全无效 - 必须配合正确的字符集使用,否则宽字节注入(如 GBK)会让转义失效
- 一旦拼接进 SQL 字符串,就等于放弃参数化查询的语义隔离优势——仍是拼接,只是“看起来更安全”
示例(危险!):
$id = mysqli_real_escape_string($conn, $_GET['id']); $sql = "SELECT * FROM users WHERE id = $id"; // 没加引号!数字型参数没被包裹,转义白做
这行代码仍可被 id=1 OR 1=1 直接击穿。
一款AI开发辅助工具,主要用于从 AI 编程会话日志(Clawdbot、Claude Code、Codex)中提取对话记录。该功能用于在用户要求导出提示词历史、会话日志或 `.jsonl` 格式的会话文件时使用,适合需要提升相关任务效率的用户。
为什么参数化查询比转义函数更可靠
参数化查询(PreparedStatement、cursor.execute(sql, params)、pg_query_params())不依赖字符串处理逻辑,而是靠数据库协议层将数据与结构彻底分离。
关键区别在于:
- 驱动不把参数当“字符串”去转义,而是作为独立二进制数据发送给数据库服务端
- 数据库在预编译阶段就锁定 SQL 结构,运行时只填入数据,不会重新解析语法
- 即使参数里含
' OR 1=1 --,数据库也只当它是某个字段的值,绝不会执行
实操建议:
- 所有用户输入参与 WHERE、HAVING、VALUES 的位置,一律用
?或命名占位符 - 不要试图“先转义再拼接”,那是给自己造幻觉
- ORM 中调用
.raw()、.execute()、text()等接口时,必须手动补上参数化逻辑,ORM 不会帮你兜底
动态 SQL 片段(表名、排序字段)该怎么处理
这部分无法参数化,SELECT * FROM ? 在任何数据库里都是语法错误。硬拼就是高危操作,必须用白名单控制。
典型错误写法:
ORDER BY {user_input}
正确做法:
- 定义允许的列名数组:
['created_at', 'updated_at', 'score'] - 显式校验方向:
if order_dir not in ['ASC', 'DESC']: raise ValueError("Invalid order direction") - 用映射表代替自由输入:
table_map = {'users': 'users_v2', 'orders': 'orders_archive'},查不到直接 400 - 绝对不要做“替换非法字符”或“删掉分号”,那只会破坏正常业务,还挡不住
orders; DROP TABLE users--
最易被忽略的一点:白名单检查必须放在参数化之前,且不能有任何 fallback 或默认值兜底——模糊容忍就是漏洞入口。










