SQL注入的真正防线是全程使用参数化查询,所有外部输入必须仅出现在参数占位符位置;字符串拼接、ORM误用、存储过程动态拼接、结构化参数(如表名、排序字段)未白名单校验等均会导致漏洞。

SQL注入不是靠“拼字符串”发现的,而是靠“不走参数化查询”暴露的
只要代码里还用 + 或 fmt.Sprintf 拼接用户输入进 SQL,不管加多少正则过滤、黑名单、转义函数,都算没防住。真正的分水岭是:所有外部输入是否**全程只出现在参数占位符位置**。
常见错误现象:SELECT * FROM users WHERE name = ' + userInput + ';哪怕后面加了 strings.Replace(userInput, "'", "''", -1),攻击者仍可用 admin'-- 或 Unicode 绕过。
- 必须用语言原生支持的参数化接口:Go 用
db.Query(<code>SELECT * FROM u WHERE id = ?, userID),Python 用cursor.execute("SELECT * FROM u WHERE id = %s", (user_id,)) - 禁止把参数化当“装饰”:比如先拼出
"WHERE status = '" + status + "'"再传给Query—— 这根本没用 - ORM 不等于安全:Django 的
filter(name__contains=request.GET['q'])安全,但extra(where=[f"name LIKE '%{q}%'"])就直接裸奔
WHERE 条件动态拼接时,参数化依然可行,但得换写法
真实业务常要“按需加条件”,比如搜索页支持选填用户名、状态、时间范围。这时候不能退回到字符串拼接,而要用“参数化 + 动态占位符”组合。
使用场景:后端接收 name、status、start_time 等可选参数,生成带条件的查询。
- Go 示例:构建
whereParts切片和args切片同步增长:if name != "" { whereParts = append(whereParts, "name = ?"); args = append(args, name) } - 不要在 SQL 字符串里留空占位符(如
WHERE 1=1 AND ?),数据库无法优化这种伪条件,还可能干扰执行计划 - PostgreSQL 注意
NULL比较:用status = ?无法匹配NULL值,需额外加OR (status IS NULL AND ? IS NULL)并传两个参数
LIKE 模糊查询的通配符必须由代码控制,不能交给用户输
LIKE 是 SQL 注入高危区,因为用户输入的 %、_、\ 会直接影响语义。但更隐蔽的问题是:即使用了参数化,如果让用户直接输 %admin%,就等于把元字符放进了参数值,失去过滤意义。
正确做法是——通配符由服务端决定,用户只输“原始关键词”。
- Go 中应写:
db.Query("SELECT * FROM u WHERE name LIKE ?", "%"+keyword+"%"),而不是让前端传%keyword% - 若需前缀匹配(如 autocomplete),只加前缀:
name LIKE ?+keyword + "%";后缀同理 - PostgreSQL 的
ILIKE和 MySQL 的LOWER()包裹不影响参数化,但注意索引失效:加函数后普通 B-tree 索引大概率失效,需函数索引或全文索引配合
存储过程或视图里嵌套动态 SQL,照样会中招
有人觉得“我把 SQL 写进数据库里,应用层只调用 CALL search_user(?, ?) 就安全了”,错。只要存储过程中用了 CONCAT、EXECUTE IMMEDIATE 或 EXEC(@sql) 拼接用户参数,漏洞照旧。
使用场景:MySQL 存储过程根据传入字段名排序,或 PostgreSQL 函数动态指定表名。
- 排序字段名这类“非数据值”参数,无法用
?占位,必须白名单校验:IF sort_col NOT IN ('name', 'created_at', 'status') THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid sort column'; END IF; - 表名、列名、ORDER BY 方向(ASC/DESC)属于 SQL 结构,不属于数据,参数化无能为力,只能严格比对枚举值
- SQL Server 的
sp_executesql支持参数化,但前提是整个 SQL 字符串是静态拼的,且所有变量都声明为@param并传入——一旦拼进表名,就无效
最易被忽略的一点:日志里打印的 SQL 如果含脱敏失败的参数值,等于把绕过痕迹直接写进磁盘。别信“我只在 debug 环境打”,配置漏掉一行就全崩。

















