防御sql注入唯一有效方式是参数化查询或orm参数化,因连接层无安全白名单机制;需配合最小权限、输入校验与waf作为辅助防线。

SQL注入防御不能靠数据库连接白名单
数据库连接层没有“安全白名单”这个机制。所谓“对数据库连接开启安全白名单”,是混淆了网络层访问控制(如防火墙、云厂商安全组)和应用层输入防护的概念。真正能防SQL注入的,只有在代码里做参数化查询或严格校验输入——连接本身不解析SQL,它只负责把字符串原样发给数据库。
为什么mysql_connect或pg_connect加白名单没用
这些函数建立的是TCP连接,底层只是连上IP+端口。即使你限制了只有10.0.1.5能连3306,只要这个IP上的应用存在拼接SQL的漏洞,攻击者照样能通过该应用发起恶意查询。连接白名单防的是“谁连我”,不是“它发什么”。
- 数据库监听地址设为
127.0.0.1比开放0.0.0.0更安全,但仅限于隔离网络暴露面 -
iptables或云安全组规则可封掉非业务IP,但这属于基础设施加固,和SQL注入无关 - MySQL的
GRANT ... FROM 'host'控制的是账号登录来源,仍无法阻止已授权账号执行恶意SQL
必须用prepare + bind_param或ORM参数化
这是唯一被验证有效的防御方式:让SQL结构和数据彻底分离。数据库驱动会把参数值当纯数据处理,不参与语法解析。
错误写法:$sql = "SELECT * FROM users WHERE id = " . $_GET['id'];
正确写法(PHP + MySQLi):
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");<br>$stmt->bind_param("i", $_GET['id']);<br>$stmt->execute();
- 所有用户输入,包括URL参数、表单字段、HTTP头,都必须走参数绑定
- 不要试图用
addslashes或mysql_real_escape_string——它们有绕过场景,且PHP已废弃后者 - ORM如Laravel Eloquent、Django ORM默认使用参数化,但手动拼接
raw()或whereRaw()时会失效
额外防线:最小权限 + 输入校验 + WAF兜底
参数化是核心,其他是辅助。生产环境建议叠加三层:
- 数据库账号只授予必要权限,比如Web应用账号禁用
DROP、CREATE、LOAD_FILE - 对已知格式字段做前置校验:ID用
is_numeric或正则^\d+$,邮箱用filter_var($e, FILTER_VALIDATE_EMAIL) - WAF(如ModSecurity规则集)可拦截典型SQL注入特征,但不能替代代码层防护——它可能漏报、误杀,且绕过手段成熟
最常被忽略的一点:存储过程里如果用了EXECUTE IMMEDIATE或CONCAT拼接输入,照样中招。参数化必须贯穿到最底层执行环节。










