真正有效的防线只有一条:彻底避免把用户输入塞进SQL字符串里,必须用参数化查询;同时需结合最小权限控制和及时更新数据库补丁,三者缺一不可。

SQL注入根本防不住,除非不拼接字符串
所有靠“过滤关键词”“替换单引号”的方案都失效了——mysql_real_escape_string 在宽字节、多编码场景下早被绕过;正则匹配 ' OR 1=1 -- 这类 payload 更是徒劳。真正有效的防线只有一条:彻底避免把用户输入塞进 SQL 字符串里。
实操上必须用参数化查询:PreparedStatement(Java)、pg.query + 参数数组(Node.js/pg)、cursor.execute(Python/psycopg2)——这些接口底层会把值当数据传,不参与 SQL 解析。
- 别写
"SELECT * FROM users WHERE name = '" + req.query.name + "'",哪怕加了trim()或replace(/'/g, "''") - PostgreSQL 中
pg.format()不是参数化,它只是字符串格式化,仍可注入 - ORM 如 Django 的
filter(name=request.GET['name'])是安全的,但手写extra(where=[...])或原生raw()就可能破防
数据库补丁更新不是“修漏洞”,而是堵住已知逃逸路径
SQL 注入本身是应用层缺陷,但数据库引擎补丁能封死攻击者利用注入后进一步提权或读取系统文件的通道。比如 PostgreSQL 12.2 修复了 CREATE FUNCTION 中的权限绕过,MySQL 5.7.32 修复了 LOAD DATA LOCAL INFILE 的任意文件读取——这些都不是注入本身,却是注入成功后的“放大器”。
不更新补丁的真实风险不是“被注入”,而是“注入后直接拿到 /etc/passwd”。
- MySQL 5.6 已停止维护,
mysql_upgrade升级后仍需手动检查sql_mode是否含STRICT_TRANS_TABLES,否则宽字节注入仍可能生效 - PostgreSQL 启用
log_statement = 'mod'并配合pg_stat_statements,能快速发现带异常参数的动态查询,比等补丁更早暴露问题 - Docker 环境中,镜像标签如
postgres:14是滚动更新,但实际拉取的可能是数月前的构建;应固定为postgres:14.10并监控 CVE 推送
连字符、空格、注释符——这些看似“无害”的字符才是检测盲区
WAF 和 IDS 常放过 %00(空字节)、%a0(NO-BREAK SPACE)、/**/(MySQL 注释)这类变体,因为它们不匹配传统正则规则。而现代注入工具(如 sqlmap)默认启用这些编码绕过策略。
与其在请求入口拦这些,不如在数据库侧做最小权限约束:
- 应用账号禁止
FILE权限(MySQL)、pg_read_server_files角色(PostgreSQL) - 禁用
xp_cmdshell(SQL Server)、CREATE TRUSTED PROCEDURE(Oracle)等高危功能 - PostgreSQL 中,
search_path设为public以外的 schema,并移除对pg_catalog的 public 访问
参数化查询 + 最小权限 + 补丁更新,三者缺一不可
只做参数化,但数据库账号有 SUPERUSER 权限,注入后照样执行 COPY 导出敏感表;只锁权限但用 mysql_query("SELECT * FROM $table") 拼表名,补丁再新也白搭。
最常被忽略的是:表名、列名、排序字段(ORDER BY user_input)无法参数化,必须走白名单校验。比如 sort_field 只允许 ['id', 'created_at', 'status'],硬编码比任何正则都可靠。

















