SQL注入防护不能仅依赖内网隔离,必须采用参数化查询;mysqli_real_escape_string存在绕过风险,需严格匹配字符集;ORM的raw()方法、动态字段名等业务逻辑漏洞是高危点,须白名单校验与权限最小化。

数据库放内网隔离区,只是SQL注入防护的起点,不是终点。 单靠网络隔离无法阻止应用层注入——只要应用代码里拼接了用户输入,攻击者就能通过合法接口(比如登录、搜索)把恶意SQL送进去,内网数据库照常执行。
为什么mysqli_real_escape_string不能代替参数化查询
它只做字符转义,不改变SQL语法结构。遇到宽字节编码(如gbk)、多字节字符集边界、或MySQL旧版本的sql_mode宽松设置时,转义可能被绕过。更关键的是:它要求开发者手动调用、且必须匹配当前连接的字符集,漏一处就全盘失效。
实操建议:
- 永远优先用
prepare+bind_param(PHP)或PreparedStatement(Java) - 如果必须用转义,确认
mysqli_set_charset($conn, 'utf8mb4')已显式设置,且数据库连接、表、字段三处字符集完全一致 - 禁用
magic_quotes_gpc(已废弃,但遗留系统可能还开着)
ORM框架里raw()和whereRaw()是高危接口
这些方法直接把字符串塞进SQL,等同于手写拼接。常见错误是把用户传入的$_GET['sort']直接喂给orderByRaw(),结果变成ORDER BY id; DROP TABLE users;。
实操建议:
- 禁止在
raw()中插入任何未校验的用户输入 - 排序字段限制为白名单:
in_array($input, ['name', 'created_at', 'status']) - 数值类参数(如分页
limit)强制转(int)并设上限,别信is_numeric()
内网数据库的SELECT权限也要按最小原则切分
攻击者一旦拿到Web Shell或RCE,就能本地直连数据库。此时若应用账号拥有SELECT * FROM mysql.user权限,就能读取其他应用的密码哈希;若能SELECT LOAD_FILE('/etc/passwd'),说明secure_file_priv没关严。
实操建议:
- Web应用账号只授予具体库+具体表的
SELECT/INSERT/UPDATE,禁用GRANT OPTION - 关闭
local_infile,设secure_file_priv = ''(MySQL 8.0+) - 定期用
SELECT user, host, authentication_string FROM mysql.user审计高权限账号
真正难防的从来不是外网扫描器,而是业务逻辑里那些“看起来无害”的动态字段名、排序方向、JSON路径表达式——它们不会报错,却让预编译形同虚设。盯住数据流向,比盯IP段重要得多。

















