给 SonarQube 加 WAF 后,我给自己挖了个坑

缘起

前段时间升级了一下公司的 SonarQube,版本升到了 2026 年 7 月版。

这个 SonarQube 是通过 AWS 的公网 ALB 对外提供服务的。既然放在公网,虽然不是谁都能登录,但让它就这么裸奔着总觉得不太合适,于是我在 ALB 前面加了一层 AWS WAF。

WAF 里启用了几组 AWS Managed Rules,包括:

  • AWSManagedRulesCommonRuleSet
  • AWSManagedRulesKnownBadInputsRuleSet
  • AWSManagedRulesSQLiRuleSet
  • AWSManagedRulesAmazonIpReputationList

另外还加了一个按 IP 限速的规则。

SonarScanner 上传的数据比较大,所以当时还专门对 SizeRestrictions_BODY、CrossSiteScripting_BODY 和 SQLi_BODY 做了放行或者只计数处理。

过了几天,我又给 SonarQube 加了一层 IP 白名单:只有办公室出口 IP 和 GitHub Actions 的公网 IP 才能访问。

为了不影响同一个 ALB 后面将来可能挂的其他服务,这个白名单不是直接加在 ALB 的 Security Group 上,而是在 WAF 里根据 HTTP Host 匹配,只限制 SonarQube 的域名。

看起来考虑得还挺周全。

然后,坑就挖好了。

问题

今天开发同事问我,最近 SonarQube 做过什么改动。

我说主要有两个:

  1. SonarQube 升级到了 2026 年 7 月版;
  2. 公网 ALB 前面加了 WAF,并且给 SonarQube 加了 IP 白名单。

开发同事用 AI 查了一下,发现问题可能不在 SonarQube 升级本身,而是在刚加上的 WAF。

AWS 的 AWSManagedRulesCommonRuleSet 里有一条规则:

1
SizeRestrictions_QUERYSTRING

这条规则会检查 URL 的 query string。只要 query string 超过 2048 字节,默认动作就是 Block。

而 SonarQube 的 Web API 确实可能产生超过 2048 字节的 URL。SonarQube 官方文档在介绍反向代理配置时也专门提醒:

You may need to increase the max URL length since SonarQube requests can have URLs longer than 2048.

也就是说,这不是什么攻击,也不是什么奇怪的异常请求,只是 SonarQube 正常工作时可能产生的长 query string。

但是 WAF 不知道这些。

它只知道:超过 2048 字节,拦。

为什么一开始没想到这里

其实在创建 WAF 的时候,我已经考虑到了 SonarScanner 上传大文件的问题:

1
2
3
4
5
6
7
rule_action_override {
name = "SizeRestrictions_BODY"

action_to_use {
allow {}
}
}

也考虑到了二进制或 multipart 数据可能误触发 XSS 和 SQL Injection 检查。

但是我只注意了 request body,没有注意 query string。

而且 IP 白名单本身工作完全正常:办公室能访问,GitHub Actions 也能访问,不在白名单里的 IP 会被拒绝。

这很容易让人以为 WAF 已经调好了。

实际上,IP 白名单只是 WAF 里的其中一条规则。请求通过白名单以后,后面还有 AWS Managed Rules。白名单允许这个 IP 访问,并不代表后面的规则不会再把这个请求拦下来。

所以这次真正误杀 SonarQube 的不是 IP 白名单,而是加 WAF 时一并启用的 AWSManagedRulesCommonRuleSet。

解决方法

AWS Managed Rules 里的 2048 字节阈值不能直接修改。

最后采用的办法,是把 SizeRestrictions_QUERYSTRING 的动作覆盖成 count:

1
2
3
4
5
6
7
rule_action_override {
name = "SizeRestrictions_QUERYSTRING"

action_to_use {
count {}
}
}

这样,超过 2048 字节的 query string 仍然会被 WAF 记录下来,也可以在 CloudWatch 指标和 WAF 日志里看到,但是不会再直接阻断请求。

Terraform plan 的结果是:

1
Plan: 0 to add, 1 to change, 0 to destroy.

只是原地更新 Web ACL,没有新建或者销毁资源。

apply 完成后,SonarQube 的长 query string 请求不再被 WAF 拦截。

2048 和 8192

排查过程中还看到过“SonarQube 的 query string 最大可能达到 8192 字节”的说法。

不过查了官方文档以后,至少目前没有找到 SonarQube 官方对 8192 字节这个上限的说明。

AWS WAF 官方文档里明确写的是:

  • SizeRestrictions_QUERYSTRING:query string 超过 2048 字节时匹配;
  • SizeRestrictions_BODY:request body 超过 8192 字节时匹配。

所以,8192 很可能是和 request body 的限制混在了一起。

SonarQube 官方只明确提醒,它产生的 URL 可能超过 2048 字节,并建议根据实际情况提高反向代理允许的 URL 长度。官方给 IIS 的示例甚至把 maxQueryString 设置成了 32768。

因此这次没有另外增加一个“超过 8192 字节才阻断”的自定义规则,而是直接把托管规则里的 SizeRestrictions_QUERYSTRING 改成 count。

多说几句

这次问题有几个值得记下来的地方。

第一,给现有应用前面加 WAF,不是把 AWS Managed Rules 全部打开就完事了。

Managed Rules 提供的是一套通用基线,不可能知道每个应用的正常请求长什么样。像 SonarQube 这种有大 body、长 URL 和复杂 Web API 的应用,上 WAF 后一定要结合真实流量调整规则。

第二,白名单和其他 WAF 规则是两回事。

请求来自白名单 IP,只表示它通过了白名单这道检查。后面的大小限制、XSS、SQL Injection 和 IP Reputation 规则仍然可能继续处理并阻断它。

第三,WAF 最好先用 count 观察一段时间,再逐步切换到 block。

尤其是给已经运行了一段时间的应用补 WAF。如果直接把所有托管规则都设成阻断,表面上是安全性提高了,实际上很可能先把正常业务打死。

最后,还有一个很现实的问题:一次同时做了 SonarQube 升级和 WAF 调整,出问题后第一反应很容易怀疑升级。

结果软件版本没背锅,真正的原因是我自己刚加上的那层“安全防护”。

安全规则最大的敌人,有时不是攻击者,而是正常业务。

参考资料