本文主要是针对自己在实际的协议分析过程中遇到的X-Requested-With头域进行了分析,主要分析了该头域为什么会出现,以及在什么情况下出现。好像是同一个问题,但是细究还是有所不同。
最近在报文抓包的时候遇到了X-Requested-With头域,该头域在RFC2616中并未提及,以X打头的头域作为非HTTP标准协议,一般是某种技术的出现而产生或者某个组织指定的,像我遇到的X-Requested-With头域就是用来判断一个请求是传统的HTTP请求,还是Ajax请求。也就是说Ajax的请求一般都会带上X-Requested-With头域。有一个比较有趣的X头域就是x-up-calling-line-id其值是用户终端手机号码,还有就是诺基亚的wap网关头域x-nokia-msisdn其值也是表示手机号码,是未加密的文本。更多的X头域我是参考了如下网址http://www.yeeyan.org/articles/view/37503/37323。
因为X-Requested-With头域作为一个特征被协议识别广泛的应用,因此就花了一点时间研究一下这个非标准HTTP/1.1也就是RFC2616中并没有提到的头域。Http2.0没有拜读过,但是大概扫了一下目录,里面并没有加入对X-Requested-With的支持(主要原因个人猜测是不是在制定草稿的时候Ajax应用还不是很广泛,Http2.0正式版好像刚发布吧),不过这也都没有关系,TCP/IP的四层模型也和标准的七层模型不一致,这并不妨碍TCP应用的很广泛。对于X-Requested-With背后的Ajax技术也是一样。Ajax技术在不加载整个网页的情况下,更新部分的网页,是一种异步的JavaScript和XML,应用非常广泛,不然我们协议分析也不能够老是遇到这个头域啊。刚才提的HTTP2.0参考http://yuedu.baidu.com/ebook/478d1a62376baf1ffc4fad99,这个是由百度FEX团队人员花费近半年时间翻译完成,应该比较专业了,不像我在看Http1.1的时候,中文翻译的真不如直接看英文原版的。
前面提到X-Requested-With头域是用来判断一个请求是传统的HTTP请求,还是Ajax请求,用JSP写web服务器端的代码如下:
if(request.getHeader("X-Requested-With") != null
&&request.getHeader("X-Requested-With").equalsIgnoreCase("XMLHttpRequest")){
out.print("该请求是 AJAX 异步HTTP请求。");
}else{
out.print("该请求是传统的 同步HTTP请求。");
}
但是该代码还是有问题的,因为在实际的抓包过程之中X-Requested-With的值是多种多样的,默认可能会设置X-Requested-With为XMLHttpRequest(设置成XMLHttpRequest的原因是XMLHttpRequest类是Ajax的基础,Ajax使用该对象来发送数据),但并不总是这样,因为前台的请求可以是如下的代码:HttpRequest对象.setRequestHeader("X-Requested-With","任意字符串")。
任意字符串可以被被设置为XMLHttpRequest。但是在实际的抓包分析过程中X-Requested-With的值在很多的情况下跟各个应用是相关的,比如我分析的包中存在X-Requested-With: cld.navi.mainframe这样的头域,我们通过这个头域可以表明有一定概率确定该报文为该类应用。
另一个有趣的现象是该头域出现的时机问题,按照前面提到的凡是使用了Ajax技术的都有可能出现这样一个头域。但是抓包的过程中X-Requested-With这个头域并不总是会出现,这可能是在前台没有设置的问题。我们通常抓的包中存在了许多流(由5元组来确定的),这里面主要是指针对同一个应用有很多流,因为应用的端口老是出现变化(原因还有待于进一步探索背后的秘密)。而目前我发现X-Requested-With这个属性头域出现往往是(以APP为例)在APP访问外链的流量的时候,会产生该头域,外链的流量嵌入在属于该APP的页面中。比如我所分析的地图类软件,往往其中会连接到某个卖车网站。卖车网站的流量并不应该归属于该地图的流量,虽然理论上是这么回事,但是有的局方是要求把该流量归属到地图中,那么其识别往往用到该字段。为什么会产生这样一个现象,不可能是人家软件为了让我们识别做的好事吧。以前曾想过我们现有的识别引擎非常复杂,如果每款应用之中加入类似与ID这样一个字段,这样识别就非常简单了。其实这个X-Requested-With有点类似于ID的雏形,包括Host也是这样一个概念。但是加入这样一个ID字段会导致什么样的问题是值得我们思考的问题,不然的话,各大组织为什么不积极推动这样一个字段的制定呢?
前面我们提到的web服务器端的代码表明我们可以在服务器端写如下代码request.getHeader("X-Requested-With").equalsIgnoreCase("cld.navi.mainframe")),客户端添加HttpRequest对象.setRequestHeader("X-Requested-With","cld.navi.mainframe "),也就是说一方知道另一方的请求数据具体来自哪里,这往往可以用于在A,B两方合作的时候,A能够知道B具体给自己带来了多少的异步访问量,或者说A会针对来自B的请求优先处理等等。但是这时候会出现一个问题,如果我自己开发了一个APP,我在我的前端设置了HttpRequest对象.setRequestHeader("X-Requested-With","cld.navi.mainframe "),那不是给人家捣乱了吗。其实也不必担心,头域中不是还存在Referer属性域,再不济我使用POST方法传一个key就好了。对于我们协议分析来说,如果单独使用X-Requested-With头域,风险还是很大的。比如X-Requested-With:cld.navi.mainframe定位为某个应用,但是如果该流之中的Host为百度文库的Host,那么如果百度文库协议之中使用了该Host强特征,就会出现误识别的情况,这时候优先级高的将会被优先识别。因此针对具体的业务,首先要定位这条流属于那一个业务比较合理,然后再决定是否添加该特征是很重要的。
至于把X-Requested-With设置为XMLHttpRequest,那就没有告诉对方该请求来自哪里,或者干脆就不设置该头域字段,这貌似说得通哈。但是如果不设置的话,对于我们识别来说,那么难度就会增加,可能后面会用到设置事件等技术,这是后话。关于X-Requested-With头域出现的时机这个问题目前还处于自己对报文分析的推测阶段,也请大神多多吐槽。
这些仅仅是我实际分析协议过程中思考总结的内容,可能还会有诸多的不足和错误的地方,也请大家指正。