如果你的Web应用出现了莫名其妙的问题,你可以怀疑是否被WAF设备拦截

有些时候,你的Web应用明明正常,但是突然有一天,它出现了莫名其妙的问题,无法定位到是哪一行代码错误,而且检查服务器配置也没有问题。这种情况下,可能是WAF安全设备在作怪。

本文用几个案例,讲一讲这种“莫名其妙”问题的现象如何,以及如何定位到WAF。

问题现象

  • 业务操作A从某时起突然开始提示“操作失败”,且更换账号、清理缓存、更换浏览器等方案皆无效,用户反馈显示所有人都无法成功办理业务。
  • 业务操作B从差不多的时间开始出故障,点击按钮没有反应,更换账号或浏览器同样无效。
  • 业务操作C也没有反应,后台抓包显示发生HTTP 418错误,而且有一个错误页面。跟开发人员确认,页面不是他们系统的。
  • 其他业务未反馈异常。
  • 询问实施与运维人员,确认近期没有碰过生产应用、生产数据库或其他相关服务器。

系统部署架构

业务系统采用了既经典又传统的Web应用部署架构:

  Internet     |                    机房
               |
用户 ← CDN ← 网关 ← 负载均衡 ← 应用服务器(集群) ← 数据库服务器
               |                     ↑
               |                 Redis服务器
               |

根据架构图,询问各环节是否靠谱:

  • 用户经常“出故障”,然而本次已确认是系统问题,不是用户环境或操作问题。
  • CDN由一家比较靠谱的第三方公司提供,出故障机率不大。
  • 负载均衡设备由一家比较靠谱的厂商生产,没有故障记录。
  • Redis服务器、数据库服务器偶尔出故障,但本次业务问题应该与数据库无关(猜测,需要进一步确认)。
  • 应用服务器集群,每个节点部署的应用完全相同。应用服务器有故障记录,而且有些不靠谱的开发人员会把bug带到生产系统。

检查过程

因故障发生前后未对生产系统服务器进行操作,首先排除自己人操作导致故障的可能性。

接下来排除集群节点故障,因为集群各节点应用相同,且只有特定功能失败,其他功能正常,不会是节点卡死导致的。

然后又排除了CDN的原因——通过修改自己电脑的hosts文件绕过了CDN,直接与服务器原始IP连接,问题依旧。

向开发人员确认两业务的代码:操作A是Ajax请求,操作B是普通的form表单提交。

下面分别分析A与B两个业务的情况。

业务A

使用浏览器控制台观察请求,发现业务A在Networks页面现象如下:

a-1

其他请求正常,说明应用服务器没挂,唯独这个个别业务可能有bug。需要登录服务器检查应用后台日志。

登录到服务器,对每个节点的应用日志进行检查,未发现报错。重复操作几遍之后再检查日志,仍然未发现报错。我们怀疑程序未打错误日志。

然而再仔细观察浏览器抓包的内容:

a-2

从图中可以发现,在Header里面未找到“Response Headers”,没有返回内容,“General”中也没有找到“Status Code”,说明甚至没有HTTP状态码。看来连接可能是被“掐断”了。

再用curl测试一下。本地调用的话是:

$ curl -v -X POST -H "Content-Type: application/json" -b "JSESSIONID=从服务器拷过来的cookie" -d "从浏览器抓到的AJAX提交的内容" http://www.xxx.com/business/submit
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to www.xxx.com (xxx.xxx.xxx.xxx) port 80 (#0)
> POST /business/submit HTTP/1.1
> Host: www.xxx.com
> User-Agent: curl/7.58.0
> Accept: */*
> Cookie: JSESSIONID=xxxxxxx
> Content-Type: application/json
> Content-Length: xxx
>
* upload completely sent off: xxx out of xxx bytes
* Empty reply from server
* Connection #0 to host xxx.xxx.xxx.xxx left intact
curl: (52) Empty reply from server
* Closing connection 0

未返回任何数据。

但登录到服务器里面操作就能返回HTTP 200 OK,还能看到JSON串,说明应用能正常响应:

# curl -v -X POST -H "Content-Type: application/json" -b "JSESSIONID=从服务器拷过来的cookie" -d "从浏览器抓到的AJAX提交的内容" http://127.0.0.1:8080/business/submit
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 port 8080 (#0)
> POST /business/submit HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Cookie: JSESSIONID=xxxxxxx
> Content-Type: application/json
> Content-Length: xxx
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache
< Connection: keep-alive
< Content-Length: xxx
< Content-Type: application/json
< Date: xxx, xx xxx xxxx xx:xx:xx GMT
<
一个乱七八糟的JSON串

* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0

将127.0.0.1:1080换成负载均衡地址,重复操作几遍,仍然返回200 OK,说明负载均衡设备应该没有问题。另外对JSON串的内容进行检查,发现提示“办理“成功”或“办理失败!请勿重复办理!”,说明数据库也是正常的,否则不可能校验出“重复办理”。

业务B

在进入B业务页面时,浏览器控制台显示oper.js有JavaScript语法错误:

oper.js

直接用浏览器查看oper.js内容,发现JavaScript脚本并未完整加载。分别在本地和服务器上使用curl命令获取oper.js内容:

在本地操作:
$ curl http://www.xxx.com/static/js/oper.js
curl: (18) transfer closed with outstanding read data remaining
一堆不完整的js代码

登录到服务器:
# curl http://127.0.0.1:8080/static/js/oper.js
一堆完整的js代码

发现:在我们电脑上执行curl,脚本还没加载完整,网络连接就被“掐断”了;在服务器上执行curl(域名分别使用127.0.0.1:8080与负载均衡地址),脚本内容能完整加载。

业务C

跟开发人员确认,他们的系统不会返回HTTP 418错误,而且错误页面与他们系统设计风格不符。说明错误页面不是该系统产生,而是由其他系统产生,例如CDN,但CDN表示这个错误页面也不是他们产生的。

结论

根据上面一系列现象判断,我们确认现有系统与设备未发生故障,可能某个环节调整了安全策略,导致正常请求被错误拦截。

和甲方负责人员沟通之后,得知机房近期部署了WAF,但甲方既未通知各相关单位,也未派人观察WAF拦截记录。我们直接和WAF负责人员沟通,维护白名单之后问题解决。

类似事件

到了另一个项目,该项目使用HTTPS网站。证书即将过期时,登录到nginx服务器替换新证书,浏览器验证时却发现证书有效期未更新,仍为原证书的有效期。

仔细检查服务器,发现证书文件已正确替换、nginx配置正确,而且服务器内只有一个nginx实例,并已正确重启。

但是在测试时意外发现,在服务器上使用curl -vIk https://127.0.0.1查看证书有效期已经更新,但是使用curl -vIk https://xxx.xxx.xxx.xxx(外网地址)时证书有效期未更新,说明服务器和外网之间应该有某类设备。

经确认,该项目服务器与外网之间也是有一个WAF,联系相应部门在WAF上配置新证书,证书有效期问题就解决了。