必须禁用字符串拼接SQL,因其无法区分输入中的“数据”与“代码”,导致' OR '1'='1等恶意输入直接篡改查询逻辑;唯一可靠防御是全程使用参数化查询(如PreparedStatement、cursor.execute()带参数),辅以白名单校验动态对象和严格转义兜底。

最可靠的方式是彻底避免字符串拼接 SQL,改用参数化查询(PreparedStatement、cursor.execute() 带参数形式等)——这是唯一能从根源上切断 SQL 注入路径的做法。
为什么字符串拼接 SELECT * FROM users WHERE name = ' + input + ' 必须禁用
这种写法把用户输入直接嵌入 SQL 语法结构中,数据库无法区分“数据”和“代码”。只要输入里含 ' OR '1'='1 或 '; DROP TABLE users; --,整个查询逻辑就被篡改。哪怕加了 trim() 或 len() 也拦不住编码绕过、宽字节注入或注释符截断。
常见错误现象包括:
- 登录页输
' OR 1=1 --就免密进后台 - 商品列表 URL 加
?id=1 AND SLEEP(5)页面明显卡顿(盲注特征) - 错误信息里暴露
MySQL 5.7.32和完整 SQL 片段(帮攻击者试错)
cursor.execute() 和 PreparedStatement 的参数占位符怎么用才安全
关键不是“用了占位符”,而是“是否真正分离了语义与数据”。不同语言/驱动对占位符的实现有差异,容易踩坑:
- Python +
pymysql:必须用%s占位,且参数传元组或列表,cursor.execute("WHERE id = %s", [user_id])✅;若写成"WHERE id = " + str(user_id)❌ - Java + JDBC:必须用
?占位并调用setString()/setInt()方法,ps.setString(1, username)✅;若用String.format()拼 SQL ❌ - PHP +
PDO:必须设PDO::ATTR_EMULATE_PREPARES = false,否则 MySQL 驱动可能在客户端模拟预编译,导致; DROP类多语句仍可执行 - Node.js +
mysql2:用?占位,但数组参数会被展开为多个值(IN (?, ?)),不能用于表名/列名——这些必须走白名单校验
当参数化查询不可行时(比如动态表名、排序字段),该怎么补救
这类场景无法用占位符,但绝不等于可以放任字符串拼接。必须用严格白名单 + 显式校验:
- 表名/列名只能从预定义集合中取:
if order_by not in ['created_at', 'price', 'name']: raise ValueError - 数字型参数(如分页
limit)必须转int并范围检查:limit = max(1, min(100, int(request.args.get('limit', 20)))) - 完全无法白名单的极少数情况(如全文检索关键词),需用数据库内置转义函数,例如 PostgreSQL 的
pg_escape_string(),而不是手写正则删单引号 - 永远不要相信
mysql_real_escape_string()(已废弃)或自定义addslashes(),它们在多字节编码或特定上下文中会被绕过
真正难的不是写出第一版参数化查询,而是确保所有分支路径(包括异常处理、日志记录、缓存键生成)都没漏掉拼接点。一个 logger.info(f"Query: SELECT * FROM {table_name}") 就可能让整套防护失效。











