防止SQL注入最有效的方式是使用参数化查询,辅以输入校验;动态SQL元素(如表名、排序字段)须用白名单限制,避免拼接和魔改输入等无效防护。

防止SQL注入最有效的方式是避免拼接SQL语句,核心手段是使用参数化查询(预编译),辅以合理输入校验。单纯依赖过滤关键字或正则替换容易绕过,不可靠。
优先使用参数化查询
数据库驱动普遍支持占位符方式传参,SQL语句结构与数据完全分离,从根本上杜绝注入可能。
- MySQL(Python + pymysql):
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) - PostgreSQL(Python + psycopg2):
cursor.execute("SELECT * FROM users WHERE name = %s", (name,)) - Java JDBC:
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE email = ?"); stmt.setString(1, email); - Node.js(mysql2):
conn.execute("SELECT * FROM posts WHERE status = ?", [status]);
输入校验应作为辅助手段
校验不能替代参数化查询,但能提前拦截明显异常输入,提升系统健壮性与可观测性。
- 对数字型字段(如ID、年龄)直接转为整型,转换失败即拒绝请求
- 对用户名、邮箱等使用白名单正则(如
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$),不依赖黑名单过滤 - 限制长度(如手机号固定11位、密码最小8位)、禁止控制字符和Unicode零宽字符
- 避免在后端重复“修复”输入(如自动去除单引号),这会掩盖真实问题且可能引入新漏洞
特殊场景需额外注意
参数化查询无法覆盖所有SQL元素(如表名、列名、排序字段、LIMIT值),这些必须严格受限。
- 动态表名/列名:仅允许从预定义枚举中选取,例如
if order_col not in ['created_at', 'score']: raise ValueError - ORDER BY 字段:用白名单映射,不直接拼接用户输入
- LIMIT 参数:强制转为整数,并设合理上限(如
min(max(int(n), 0), 100)) - 存储过程调用:仍需对传入参数使用绑定变量,不可拼接内部SQL
避免常见误区
很多“防护”看似有效,实则形同虚设。
- 魔改输入(如把
'替换成'')只对特定数据库生效,且易被编码绕过 - 关键词过滤(如屏蔽
union、select)可被大小写、注释、编码、分块等方式绕过 - 前端JS校验可被跳过,后端必须重新验证
- ORM框架(如Django ORM、SQLAlchemy)默认使用参数化,但手动拼接
.extra()或text()时仍会引入风险










