使用 image 发请求的隐患

3,190 阅读4分钟

通常,我们只会用 img 标签或 Image 对象来加载图片资源,但实际上 Image 也常被用来发请求作数据的上报,一方面原因就是不需要考虑跨域,而且通常数据上报也不需要处理响应。但是,如果在代码中使用 Image 来发送一些业务请求等,则要考虑其必要性了,如果没有必要,还应该使用 ajax 来发送请求,否则可能埋下一个隐蔽的坑。

背景

最近接手了一个用户反馈的余额没有显示的 bug,由于自身没法在现有信息的条件下复现 bug,又难以从代码中找到逻辑问题(还是缺少经验),只能通过打日志的方式找用户配合联调,经过几天的折腾才最终定位到是使用 image 来发请求引起的 bug。那么使用 image 来发请求是如何引起这个 bug 的?存在什么隐患?

排查过程

首先,来看下用户反馈的截图:(其中暗含了一个关键信息)

登录态问题

通过查看该用户的后台日志信息,发现用户的账户信息请求的 Cookie 中缺少了一个关键的票据信息,导致获取不到余额,于是发现获取该票据的请求在日志中并没有找到。因此,可以首先判断,是登录态问题导致用户取不到余额信息,但是,为何关键的获取票据请求后台没有收到呢?

请求发出了没有?

遇到请求后台没有收到,首先怀疑的是逻辑的问题:应该是存在逻辑漏洞导致在某条件下没有发送该请求。然后通过本地调试以及对该段逻辑的梳理,实际上并没能发现存在请求不被发送的可能。到这一步,就很难走下去了,没有更多的信息,且业务逻辑几乎确定没有问题。不过,我多少也开始觉得奇怪,为啥这里是用 Image 来发送请求:

var imgReq = document.createElement('img');
imgReq.onload = imgReq.onerror = function () {
	...
}
imgReq.src = url;

打更多日志

只通过代码,即便能从理论上判断逻辑没有问题,但是没有办法保证运行起来就的确如此。无奈之下,只能在代码中打上更多日志,然后联系用户操作,并将手机上 vconsole 打印的日志发给我(这是十分需要用户配合的 OTZ)。很幸运,用户很配合地按我的指示操作了,我才能拿到用户手机客户端的日志来进行分析。

根据日志,我能肯定请求一定是发出去了,但是服务器也的确没有收到请求。这让我觉得诡异,我开始怀疑是 img.src 这种请求方式的问题了,这才惊悚地发现,**用户的头像没有显示!**这就是那个暗含的关键信息!一开始我以为用户的头像就是灰色的,于是没有注意,但是在与用户沟通的 QQ 群里,用户是有头像的。不难想到,这极有可能和浏览器无图模式有关。然而,在我的手机上依旧不能复现,于是我只剩这样的假设了:

  1. 与手机机型有关;
  2. 与无图模式和 image 请求有关;

带着这样的假设,我增加了 ajax 的请求方式,不得不让用户再试一次,很快就在日志中证实了这个假设:

...
cookie: ...  // 无票据(img 请求之后)
...
cookie: ...  // 有票据(ajax 请求之后)

于是我在用户同机型的手机上也复现了这个问题。。。(为什么不早点找同机型的手机复现😭 OTZ)

结案

在浏览器无图模式下(这里具体场景为 QQ 浏览器),且在某些机型的手机上,通过 Image 动态发起请求是有可能被拦截的! 上报请求被拦截其实也还能接受(毕竟现在还有多少人在乎流量呢),但是业务请求被拦截就可能直接影响功能了。为避免这样的隐患,还是应该尽量避免用 Image 来发业务请求。