WAF配置后还是能绕过SQL注入?预编译查询没用?

♫玉萱 阅读 4

我给API加了预编译查询和WAF规则,但测试时发现用OR 1=1 UNION SELECT这种payload还是能获取数据库表名。WAF规则用了黑名单过滤了单引号和分号,但用户输入“%df%”时居然返回了敏感数据…

尝试过在Express中间件用正则替换特殊字符,但POST数据里的JSON参数还是被解析成SQL语句了。数据库是MySQL,代码用的是:


app.use((req, res, next) => {
  const safeInput = req.body.input.replace(/[';]/g, '');
  req.safeData = safeInput;
  next();
});

现在搞不清楚到底是WAF没拦截,还是预编译没生效?有没有更好的防御组合方案?

我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
FSD-锦锦
问题应该出在几个地方,咱们一个一个来看。首先你提到WAF规则用了黑名单过滤单引号和分号,但这种简单的字符替换其实很容易被绕过。比如用户输入“%df%”这种情况,可能是利用了编码或者宽字节注入的技巧,MySQL在某些字符集下会把多字节字符解析成单引号,导致你的过滤失效。

其次,预编译查询理论上是防御SQL注入的最佳实践,但从你的描述来看,可能并没有真正用对。如果你只是简单地把用户输入拼接到SQL语句里,那预编译就完全没发挥作用。正确的做法是使用参数化查询,确保用户输入的内容永远不会被当成SQL代码执行。

再来看你的Express中间件逻辑,这里有几个问题。第一,你只对 req.body.input 做了简单的正则替换,但POST请求里的JSON数据可能有多个字段,每个字段都需要处理。第二,这种方式本质上还是黑名单过滤,而黑名单永远不可能穷尽所有攻击方式,攻击者总能找到新的绕过方法。

推荐的解决方案是这样:首先确保所有SQL查询都使用真正的预编译查询。以Node.js为例,假设你用的是 mysql2 模块,代码应该写成类似这样:

const mysql = require('mysql2/promise');

async function queryDatabase(input) {
const connection = await mysql.createConnection({host: 'localhost', user: 'root', database: 'test'});
const [rows] = await connection.execute('SELECT * FROM users WHERE username = ?', [input]);
return rows;
}


这里的关键点是问号占位符和参数数组,这样可以确保用户输入的内容始终作为数据处理,而不是SQL代码。

其次,WAF的规则需要升级。单纯依赖黑名单是不够的,建议引入白名单机制,限制用户输入只能包含特定的字符集。比如用户名字段通常只允许字母、数字和下划线,可以用正则 /^[a-zA-Z0-9_]+$/ 来校验。

另外,针对你提到的“%df%”返回敏感数据的问题,很可能是数据库的字符集配置有问题。检查一下MySQL的连接字符集,确保它设置为 utf8mb4,并且不要使用容易出问题的字符集比如 GBKlatin1

最后一点,也是最容易被忽略的,就是API的权限控制。即使SQL注入被成功利用了,如果数据库用户权限配置得当,比如只给查询权限而不给结构访问权限,攻击者也很难获取表名或者其他敏感信息。

总结一下,防御SQL注入的最佳实践应该是多层防护:正确使用预编译查询、严格校验输入、合理配置WAF规则、确保数据库字符集安全、以及最小化数据库用户权限。每一层都做好了,才能最大程度降低风险。
点赞 2
2026-02-18 18:05