神策军 丨 与客户难题“对抗”的百余天
-
2020-11-11
神策数据
对于每一个客户的问题,不管大小,都需要跟进追踪到底,以获得最佳的答案。
一个问题一个坑,留了坑,前路必将荆棘丛生;填了坑,前路即是坦途。这是神策人的做事态度和行事准则,也帮助我树立了积极的人生观。
如果同行也正在经受类似问题的困扰,希望同行可以通过这篇文章,在排查过程中能够获得一点启发。
刷量?客户反馈异常 IP 发送大量重复数据
从今年 2 月份起,我陆续收到客户反馈:Web JS SDK 短时间内上报了大量重复的数据到神策分析,从而影响了客户的分析、决策和制定运营计划。对于这种情况我们通常称之为 “刷量”。什么是刷量?
图 1 刷量表现—按天
图 2 刷量表现—按分钟
用户所反馈的现象和刷量一致,有几个异常 IP 短时间内密集发送 Web 端重复数据入库。一个 IP 一天发送的重复数据甚至达到了几十万、上百万条,这显然不是一个正常用户产生的行为数据。
我们查看了刷量时的用户行为序列,如下图,可以看到事件是重复循环触发的,时间间隔在几十毫秒。
图 3 刷量发生时的用户行为序列
针对此问题的反馈,我们进行了快速排查及提供初步解决方案。 当时的排查思路:
一,会不会是客户代码 bug 导致重复触发事件?
通过协调运维帮忙查询事件日志,证明这些重复事件的 trackid(Web JS SDK 对发送数据生成的随机数,每条采集和发送的数据都会有自己独特的 trackid) 是一样的。从这个方面看,基本排除了是客户代码 bug 导致的,因为即使是代码重复触发的事件,trackid 是不会重复的。
二,会不会是别人恶意获取了神策的数据采集请求和数据体,使用工具或者脚本伪造请求,灌注脏数据到神策分析服务器?
Web JS SDK 采集的数据,默认使用 image 方式发送数据,GET 请求的数据接收地址和数据体都包含在请求的 URL 中,如图所示:
图 4 Web JS SDK 的数据发送
只要复制该 Request URL 直接在浏览器地址栏访问,或者使用脚本访问,就会有一条一模一样的数据入库。这也符合 trackid 重复的情况,而且可以集中产生大量的请求,实现灌注脏数据。在本地使用脚本模拟,也能够复现出来数据刷量的情况。
因此,推测的结论是:有人从集成了 Web JS SDK 的页面上,截取了发送的数据请求,并通过脚本灌注脏数据进入神策分析。
根据这一结论,给客户的方案是:将 Web JS SDK 的数据发送方式从 image 改成 ajax,这样请求就会从 GET 变成 POST,在一定程度上避免将整个请求暴露在 URL 上;将出现过刷量的 IP 增加到后端的防火墙黑名单中。
问题又现!存疑,再深入排查
但是,似乎并没有解决问题。在接下来的一个月,依旧陆续收到刷量的情况反馈。
我每天值班也都胆战心惊,只要有客户反馈刷量的情况,心里都咯噔一下:如果是有人恶意刷量,那么持续刷的动作有点说不通,是否有其他原因导致……问号一直悬在那里,始终无法说服自己。
我们内部组织 Web JS SDK 开发人员来集中开会讨论,当时判断刷量的可能原因:Web JS SDK 的 bug 导致?有人恶意攻击,灌注的脏数据导致?后端服务中的 bug 导致的重复读取数据导致?
为了验证我们的推测,申请了一个客户被刷量的环境查看,判断不是 Web JS SDK 和后端服务的 bug ,目前的方向也只剩下是有人恶意攻击了。Web JS SDK 在前端针对这种情况,没有什么方法阻止,寄希望于后端可以将这样的刷量数据去重。
由于 Web 端的数据入库后,默认使用服务器时间,而服务端的去重逻辑是根据 trackid、time 等一起去重。像这样的刷量数据入库,虽然 trackid 一致,但是 time 相差只有几十毫秒,因此无法去重。为了解决这一问题,可以在后端对于 Web 端数据的去重逻辑中将 time 字段去除,这样就可以对刷量数据进行去重了。
不过,这只是一个临时解决方案,且该方案只针对集群版有效,有一定的局限性。另外,这种方案属于出现了刷量后的被动处理。
“用力啊!”
对于此问题,书记也一直怀疑,每天见我必问:“有进展吗?用力啊!” 由于并没有找到确定的原因,我在网上查到 image 请求死循环的资料,如下图所示:
图 5 image 请求死循环资料
于是我查看 Web JS SDK 源码,发现已经对 image 请求的 onerror 做了 null 赋值处理,不会出现死循环现象。有点失落,这个方向也是不对的。
场景难以复现,多次转机出现却屡陷僵局
终于,转折出现了。当我与被刷量客户的技术做深入沟通时,有了一个重大发现。
4 月的某天,客户拿到注册用户的手机号,通过技术人员联系到这个用户。发现用户是正常操作,只是使用了某浏览器访问页面。当时,远程客户电脑进行操作,发现如果关闭浏览器后就会停止刷数据,浏览器处于开启状态就会一直刷数据。另外,发现用户的浏览器版本比较老,让其升级到最新的浏览器后,依然能够出现刷数据的情况。同时,在访问页面时页面会有卡顿情况,风扇也转的特别厉害。另外,同一时刻该用户对客户自己页面的一个水印图片访问量也很大,达到了 21.4 万次。但是,对神策的 sa.gif 访问量更多,达到 255.4 万次。
不过当客户的技术在自己电脑上模拟的时候,无法复现这个问题。查看当前稳定版的 M 浏览器内核为 Chrome 78.0.3904.108。通过这个发现,我去查看其他客户的最近刷量数据,均发现其 UA 是 Chrome 78.0.3904.108, 指向 M 浏览器。通过以上线索推论:刷量问题很可能不是人为恶意攻击,而是 M 浏览器访问针对 image 数据请求可能有无限循环的 bug 导致的。
按照客户提供的线索,我反向查看了下:以该内核版本为筛选条件,发现出现刷量的那天,有近 5300 个用户使用该内核版本访问了页面,而出现刷量的情况只有 1~2 个用户。从这方面看,刷量似乎不是必现的。本地使用多台电脑,测试 M 浏览器均没有能够复现,问题似乎又陷入了僵局。
再换一条路,基于 M 浏览器的问题判断,我当天我在浏览器社区留言,希望可以从浏览器的工作人员那里可以获得答复和帮助,如图所示。
图 6 留贴浏览器社区
工作人员通过 QQ 联系了我,并与我沟通了基本情况。由于他们没有受理过类似问题,希望我提供复现的页面,但是我这边没有复现的页面。因此,这个方向的路也堵住了。
感恩信任!与「客户的用户」的多次远程、面基……
在走投无路时,几经辗转,为复现场景,我再次联系客户的技术人员,希望能够联系到之前两位用户。经过客户的提前沟通,以及一系列的保密协议签署后,最终提供两位用户的联系方式。
起初我几次联系,都没有能够打通电话。而此时,刷量问题愈演愈烈。因为线索只剩下这一条了,我就硬着头皮继续打电话,终于还是拨通了。
在此很感激两位用户的信任。第一位用户帮忙安装了 TeamViewer,并提供了远程。可惜的是这一次没有复现刷量的情况。
不能放弃,继续联系另一位用户。由于是上班时间,另一位用户电脑在家且不方便配合,约了周末在家配合我远程:当打开他电脑上的浏览器并访问客户的页面,用户反馈电脑风扇开始响起来了。我打开任务管理器,发现其 M 浏览器访问客户网页的 CPU 占比很高,如图所示。
图 7 刷量时的任务管理器
同时从神策分析看,这个时候该机器在进行刷量,如图所示:
图 8 刷量时的监控平台
但是通过控制台,并没有发现大量数据刷量的请求,神策的 sa.gif 发送数据请求都是正常的,如图所示。
图 9 刷量时的控制台信息
反反复复检查了几遍,没有发现其他异常情况和原因。我只好断了远程,并尝试和用户约了下一次远程查看。同时,试探了一下:如果远程查看还是没有发现问题,是否可以面基,直接由北京的同事操作和查看其电脑(由于我在合肥办公),没想到客户居然真的同意了!
由于也占用了他比较长的时间,我这边给他发了个 100 元红包表示感谢(感谢书记给我报销)。
结束客户的远程之后,和根哥沟通了第一次远程的情况。根哥给了关键性的建议:可以查看下是不是电脑防火墙或者浏览器插件导致的。 因此,我与用户约了周一中午再次远程,该用户将电脑背到公司,通过午休时间给我远程查看。
三行代码解决问题,浏览器也同步修复自身 BUG
中午我没有休息,这次远程,依然复现了问题。我从浏览器插件检查,发现了一个可疑的插件:图片下载插件,这个插件和之前发现的针对 sa.gif 图片请求的刷量有一些重合之处。赶快测试下,果然有了突破。
当我禁止该插件时,CPU 降下来了,同时测试后端数据入库也停止了刷量情况。反复测试后,验证了确实是该插件导致的。
结束远程后,我在本地使用机器测试,也能稳定的复现刷量的情况。在群里我同步了该插件的情况,其他同事也帮忙测试。同时,经过进一步的测试,通过打开插件的控制台,发现了插件的确在重复不断地发送图片请求。
查看该插件,发现是该浏览器的应用市场中的一款常用的图片下载扩展应用。应该是使用 Chrome 内核的时候,同时带过来的,通过查看 Chrome 应用商店也发现了该插件。
在 Chrome 中安装该扩展应用时,也复现了刷量的情况。由于 Chrome 商店国内访问很少,所以少有用户能够直接下载该扩展使用。而 M 浏览器非常方便的提供了该应用的下载和安装,且属于图片下载类扩展的前几名扩展,如图所示:
图 10 图片下载类扩展排名
至此,刷量的原因总算是水落石出了。
同事想爷本地测试也验证了刷量情况,并将插件源码获取到后,发给大家测试。基于想爷提供的源码,我从网上查阅、学习插件开发和调试的方法后,在本地调试插件。开启插件的调试模式,在插件的控制台中,呈现了快速大量的重复 image 请求发送出去。调试插件代码发现,该插件通过浏览器提供的 API 拦截浏览器的 image 请求后,又重新创建一个新的图片请求去下载图片。致命的是,该循环没有加限制条件,这个新的图片请求通过浏览器发送出去后,再次被拦截,又创建一个新的图片请求。
研发大康哥通过调试该插件代码,给出建议,只要补充三行代码就可以解决该问题,如下图所示:
图 11 插件优化代码
根哥提出了几个解决方案:
在 SDK 中是否可以检测到该插件,含有该插件的环境将发送方式变更为 ajax 方式;
联系 M 浏览器,反馈该问题插件,希望其可以安排对插件的检测和下架;
联系插件作者,反馈问题,希望可以修复掉问题代码,发布新版。
大康哥研究了一下:由于浏览器的限制,在页面中无法查看这类扩展插件的列表。因此,无法在 Web JS SDK 集成时,从页面的环境中查看当前浏览器是否集成了该插件,从而自动将数据的发送方式从 image 更改为 ajax。我这边也从插件声明的变量和静态资源入手,尝试了很多办法,也没有能够在页面的 SDK 上捕捉插件的变量或者静态资源。至此,寄希望于 SDK 主动识别插件的方案走不通了。
大约在 6 月 8 日,我再次联系浏览器工作人员,与之详细反馈了插件情况。工作人员给了我一个 M 浏览器插件组的邮箱,让我将详细情况通过邮件的方式发给该部门,然后在 1 到 2 个工作日会有工作人员联系我。此时,再去 M 浏览器的应用市场,将该插件截图一下作为邮件内容发送出去,却意外发现了一个惊喜,该插件已经做了升级。当前版本从 1.2.3 变成了 2.2.0 版本,同时去 Chrome 应用商店查看,里面该插件已经升级到 2.2.0 了。
安装调试该插件代码,发现已经将 bug 修复(此处与大康哥之前提出的修复代码基本一致),代码如下图所示。
图 12 新版插件源码
经过测试后,确实不会再出现刷量的情况了。同时,在随后的值班过程中,也发现刷量问题出现的情况大幅减少。
除了解决客户的问题,还能够帮 M 浏览器暴露了插件问题,定位了有插件的用户风扇狂转和 CPU 飙升的问题原因,对我们来说,也是很开心的。
那些费力「不讨好」的事情
在这个解决“刷量”的案例中,牵涉人员众多,包括客户、客户的用户、浏览器工作人员、第三方插件作者,还有神策各地同学,5 方的努力与协作,缺一不可。采用的手段也非常规,比如 SDK 技术顾问给「客户的用户」发红包,再如 SDK 技术顾问通过网络给 M 浏览器工作人员留言……
有外部人评价,我们在单个客户上的付出,有时候会显得“不划算”,毕竟问题的本身不在神策。但对神策来说,这是难而正确的事,客户的员工都看在眼里,我们不辜负客户的信任,客户问题无小事,始终 All In!
类似这种,我们帮客户解决的『非分内』的问题,还有很多:
·某公有云虚拟机性能下降导致神策系统运行慢,某公有云拒认,我们写程序给出证据,某公有云迁移机器解决问题。
·某客户物理机磁盘硬件故障,供应商拒认,我们协助客户证明,后来供应商承认并更换磁盘。
·某客户底层网络 bug 影响神策业务,客户内部部门不认可,我们写程序证明,客户表示 bug 修不了,被迫绕过。
·某公用云虚拟机故障导致指令集不支持,某公有云拒认,我们写程序证明,某公用云解决并上门道歉……
最后附上某位朋友送的一面锦旗。
其实,哪有什么「料 bug 如神」,只不过在每个问题被解决的背后,都付出了超常规的努力。
-
本文作者:神策数据
责任编辑:马亚蒙
本文来源:牛透社
-
分享到: