SQL注入防护必须使用参数化查询,过滤仅适用于非执行场景;通用过滤库无法应对注释绕过、多语句、十六进制编码等攻击,且存在规则覆盖不全、性能损耗等问题。

SQL注入风险不是靠“过滤”能兜住的
直接说结论:别用通用过滤库清理SQL输入——它解决不了根本问题,反而给你一种虚假的安全感。真正的防护必须落在参数化查询上,过滤只是辅助手段,且只应在展示层或日志脱敏等非执行场景出现。
为什么 sqlparse 或 bleach 不能防SQL注入
这类库设计目标是解析SQL语法或清洗HTML,不是拦截恶意SQL片段。它们无法理解你最终拼接进哪条语句、哪个位置、是否在引号内、是否被转义函数包裹。常见错误现象:SELECT * FROM users WHERE name = 'admin' -- ' 这种注释绕过,1; DROP TABLE users 这种多语句,0x7573657273 十六进制编码,全都能绕过基于字符串匹配的“过滤”。
- 使用场景错位:把本该在数据库驱动层做的参数绑定,挪到应用层做文本替换
- 参数差异大:不同数据库对注释、空格、大小写、Unicode空白符容忍度不同,过滤规则很难全覆盖
- 性能影响:每次请求都做正则扫描或AST解析,尤其对长文本字段(如富文本内容)开销明显
真正该用的方案:语言原生参数化 + 输入白名单校验
以Python的psycopg2或sqlite3为例,所有用户输入必须走execute()的占位符机制;其他语言同理:mysqli::prepare()、pg_query_params()、JDBC的PreparedStatement。这不是可选项,是唯一正确路径。
- 字符串拼接
WHERE id = ' + user_input必须改成WHERE id = %s+execute(sql, [user_input]) - 动态表名/列名不能参数化?那就用白名单校验:
if user_table not in ['users', 'orders']:直接拒绝 - 数字类型强制转
int()或float(),失败就报错,不尝试“过滤掉字母” - 邮箱、手机号等格式字段,用
re.match()做严格正则匹配,而不是“删掉所有非数字字符”
如果真要过滤(比如存入日志或前端展示),只做最小必要脱敏
仅当数据不参与SQL执行,但又要避免XSS或干扰日志分析时,才考虑轻量清洗。例如记录用户搜索关键词到ELK里,防止<script></script>污染Kibana界面。
- 用
html.escape()处理可能输出到HTML的地方,不是用strip_tags() - 日志中敏感字段(如密码、token)统一替换成
'[REDACTED]',而不是“过滤特殊字符” - 绝对不要对输入做
replace("';", "")或strip("'\"")这类操作——攻击者早就有几十种绕过方式
复杂点在于:很多人混淆了“输入校验”“输出编码”“存储格式约束”三件事。一个字段在入库前校验长度和格式,在查询时参数化,在返回JSON时做HTML转义,在写入日志时做敏感词遮蔽——每一步职责不同,混在一起就容易漏。

















