HTTP协议是使用最广泛的应用层协议,它有4个版本,最新版本HTTP/2于2015年5月就已经记录在RFC 7540中,在2年的时间里已经得到广泛的应用,但很多人并不知道HTTP/2的时代已经到来。本文旨在科普HTTP/2协议,从讲故事到聊技术,再到谈理想,梳理该协议的发展历史、协议特性、安全风险、工具支持情况等,快速了解该了解的信息,方便深入想深入的细节。

关于HTTP/2的协议特性和攻击面,在内部做过一次分享,现将PPT中内容整理成该文章。其中有多处借鉴、盗图,文末给出我有记录的参考链接。文章分为三部分:

1、讲故事

2、聊技术

3、谈理想

 

一、讲故事

HTTP协议发展历程:

Image

需要注意的是,通常会认为HTTP/1.1比HTTP/1.0多了长连接,其实HTTP/1.0也有长连接机制,只是HTTP/1.1是默认打开,HTTP/1.0是默认关闭。

每次变革必定会有一个导火线,我认为HTTP/1.1到HTTP/2的导火线是性能原因。从2011年到2015年,请求每个页面的对象数和传输大小都增大很多,HTTP/1.1无法适应这个环境,所以HTTP/2出现了。

Image

HTTP/1.1为什么无法适应这个环境,它有哪些不给力的地方呢,可以说太多了,后面讲HTTP/2给力的地方,都是HTTP/1.1不给力的地方,所以这里就不一一列举,简单举2个例子:

1、HTTP/1.1过于臃肿,723x系列的6个标准文档,加起来几百页的RFC,开发者很难理解这个协议的每个细节;

Image

Image

2、较之HTTP/1.0添加的Pipelining技术,并没有很好的解决head of line blocking问题,HTTP Pipelining技术示意图如下:

Image

没有很好的解决head of line blocking问题的原因如下:

1)pipelining只能适用于HTTP/1.1,一般来说,支持HTTP/1.1的server都要求支持pipelining;

2)只有幂等的请求(GET,HEAD)能使用pipelining,非幂等请求比如POST不能使用,因为请求之间可能会存在先后依赖关系;

3)head of line blocking并没有完全得到解决,server的response还是要求依次返回,遵循FIFO(first in first out)原则。也就是说如果请求1的response没有回来,2,3,4,5的response也不会被送回来;

4)绝大部分的http代理服务器不支持pipelining;

5)和不支持pipelining的老服务器协商有问题;

6)可能会导致新的Front of queue blocking问题。

人们对改造HTTP/1.1想过很多招,其中有几个尝试:

1、Spriting(图片合并)

Spriting是一种将很多较小的图片合并成一张大图,再用JavaScript或者CSS将小图重新“切割”出来的技术。

Image

2、Inlining(内容内嵌)Inlining是另外一种防止发送很多小图请求的技巧,它将图片的原始数据嵌入在CSS文件里面的URL里。而这种方案的优缺点跟Spriting很类似。

3、Concatenation(文件合并)

类似Spriting,将JavaScript文件打包

4、Domain Sharding(域名分片)

Sharding就是把你的服务分散在尽可能多的主机上。

Image

前三种方法的思路类似,把分散的对象合并起来,就像把随机读写改为顺序读写,效率就上去了,而短板也很明显,其中一个对象有改动,就要重传整个大块。可以说,操碎了心,也没完美解决HTTP/1.1的性能问题。这时候Google看不下去了,于是Google重新制定了一个替代协议:SPDY,带来的收益和推广效果都是不错的,首先看看SPDY的协议概况:

Image

SPDY基础功能1、多路复用(multiplexing)。多路复用通过多个请求stream共享一个TCP连接的方式,解决了HTTP/1.x holb(head of line blocking)的问题,降低了延迟同时提高了带宽的利用率;

2、请求优先级(request prioritization)。多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,这样重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容;

3、header压缩。前面提到过几次HTTP/1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大小和数量。SPDY对header的压缩率可以达到80%以上,低带宽环境下效果很大。


SPDY高级功能

1、server推送(server push)。HTTP/1.x只能由客户端发起请求,然后服务器被动的发送response。开启server push之后,server通过X-Associated-Content header(X-开头的header都属于非标准的,自定义header)告知客户端会有新的内容推送过来。在用户第一次打开网站首页的时候,server将资源主动推送过来可以极大的提升用户体验;

2、server暗示(server hint)。和server push不同的是,server hint并不会主动推送内容,只是告诉有新的内容产生,内容的下载还是需要客户端主动发起请求。server hint通过X-Subresources header来通知,一般应用场景是客户端需要先查询server状态,然后再下载资源,可以节约一次查询请求。

性能上的提升也是很明显的:

Image

Image

Image

通过以上统计,得出2个结论:

1、SPDY较之HTTP/1.1性能上大幅提升;

2、网络环境越差,提升越明显。

之后的桥段就如同香港警匪片,事情搞定了,警察来了。IETF认识到,在现有互联网设施基础和HTTP协议广泛使用的前提下,是可以通过修改协议层来优化HTTP/1.x的,另外针对HTTP/1.x的修改确实效果明显而且业界反馈很好,所以IETF以SPDY/3为蓝图起草了HTTP/2,并邀请了SPDY的部分设计人员参与HTTP/2的设计。在设计新的HTTP协议之前,有几个前提:

1、客户端向server发送request这种基本模型不会变


2、老的scheme不会变,使用http://和https://的服务和应用不会要做任何更改,不会有http2://


3、使用HTTP/1.x的客户端和服务器可以无缝的通过代理方式转接到HTTP/2上


4、不识别HTTP/2的代理服务器可以将请求降级到HTTP/1.x

体验HTTP/1.1和HTTP/2的区别,可以访问这个站点:https://http2.akamai.com/demo。故事讲到这,下面聊技术。

 

二、聊技术

HTTP/2最详细的说明应该就是它的RFC文档了:https://tools.ietf.org/html/rfc7540,从其中提炼几个点进行分析:

1、协议格式

HTTP/1.1基于文本ASCII,HTTP/2改为基于二进制,它的优点是1)不用考虑文本的多样性 2)对起始帧和结束帧识别更简单 3)不需要填充,缺点是不利于调试,但这个缺点影响不大,因为1)浏览器会把HTTP/2解析成类似HTTP/1.1的格式 2)主要浏览器厂商(除了IE)只支持TLS上的HTTP/2。

调试HTTP/2可以用wireshark,wireshark通过读取浏览器使用的密钥解码TLS加密的数据,就可以看到明文数据(https://jimshaver.net/2015/02/11/decrypting-tls-browser-traffic-with-wireshark-the-easy-way/):

Image

2、传输单元

HTTP/2的传输单元改为Frame,不管是什么类型的数据,都是用Frame去承载:

Image

Length: 表示 Frame Payload 部分的长度,另外 Frame Header 的长度是固定的 9 字节(Length + Type + Flags + R + Stream Identifier = 72 bit)。


Type: 区分这个 Frame Payload 存储的数据是属于 HTTP Header 还是 HTTP Body;另外 HTTP/2 新定义了一些其他的 Frame Type,例如,这个字段为 0 时,表示 DATA 类型(即 HTTP/1.x 里的 Body 部分数据)


Flags: 共 8 位, 每位都起标记作用。每种不同的 Frame Type 都有不同的 Frame Flags。例如发送最后一个 DATA 类型的 Frame 时,就会将 Flags 最后一位设置 1(flags &= 0x01),表示 END_STREAM,说明这个 Frame 是流的最后一个数据包。


R: 保留位。


Stream Identifier: 流 ID,当客户端和服务端建立 TCP 链接时,就会先发送一个 Stream ID = 0 的流,用来做些初始化工作。之后客户端和服务端从 1 开始发送请求/响应。

下面是几个协议包对照图:

Image


Image

Image

Image


3、传输形式

HTTP/2的传输形式改为Streams,多个Streams可以共享一个Connection,一个Stream里头是顺序排列的Frames。

Stream Identifier将HTTP/2连接上传输的每个帧都关联到一个Stream。Stream是一个独立的、双向的帧序列,可以通过一个HTTP/2的连接在服务端与客户端之间不断的交换数据。

每个单独的HTTP/2连接都可以包含多个并发的Stream,这些Stream中交错的包含着来自两端的Frame。Stream既可以被客户端/服务器端单方面的建立和使用,也可以被双方共享,或者被任意一边关闭。在Stream里面,每一个Frame发送的顺序非常关键,接收方会按照收到Frame的顺序来进行处理。

Image

下面通过简单的数据包分析,看看一个HTTP/2的传输过程:

1)连接开始时,客户端会向服务端发送一个Magic Frame,然后通过SETTINGS Frame对此次连接进行一些配置,这些Frame的Stream ID都为0:

Image

2)正式的请求响应从Stream ID=1开始,如果是GET请求,就只会发送一个HEADERS Frame,如果是POST请求,则会把请求拆分成HEADERS Frame和DATA Frame:

Image

3)之后的Stream的ID会递增,每次加2,正常数据传输的Stream ID必须为奇数,需要注意的是,Stream ID必须越来越大,用过的ID必须废弃,不能再用,当Stream ID超出最大值,该连接会断开,然后重新建立连接从1开始:

Image

Image

Image

4)连接过程中,客户端或服务端会向对方发送PING Frame检测连接可用性,长时间没有数据交互,服务端会向客户端发送GOAWAY Frame,然后该连接就会断开,之后再要通信,需要按照这个流程从第一步重新来过:

Image

Image

4、多路复用

Image

流的多路复用意味着在同一连接中来自各个流的数据包会被混合在一起,所以流存在优先级和依赖性。

我们可以通过PRIORITY Frame和WINDOW_UPDATE Frame对Streams进行控制,带来便利的同时也会带来安全风险,后面多个漏洞就是因为这个特性导致。

5、状态机

Image

每个协议都有状态机,上图表示的是,客户端或服务端在发送或接收什么类型的Frame后,就会置为什么状态。

6、headers压缩

1)为 HTTP/2 头压缩专门设计的 HPACK,使用索引表和霍夫曼编码压缩headers

2)索引表分为静态表(http://http2.github.io/http2-spec/compression.html#rfc.section.A)和动态表

3)使用霍夫曼编码对headers进行encode,如果encode后更长,则不encode

Image

Image

7、Server Push

HTTP/2能通过push的方式将客户端需要的内容预先推送过去,所以也叫“cache push”。另外有一点值得注意的是,客户端如果退出某个业务场景,出于流量或者其它因素需要取消server push,也可以通过发送RST_STREAM类型的frame来做到。

前提条件:1)客户端的SETTINGS_ENABLE_PUSH不为0 2)服务端PUSH的PUSH_PROMISE必须包含”:authority”字段

需要注意的是,HTTP/2取消了SPDY中的Server Hint机制。

8、HTTP/2协商

Next Protocol Negotiation (NPN)是一个用来在TLS服务器上协商SPDY的协议。IETF将这个非正式标准进行规范化,从而演变成了ALPN(Application Layer Protocol Negotiation)。ALPN会随着HTTP/2的应用被推广。(https://tools.ietf.org/html/rfc7301)

ALPN和NPN的主要区别在于:谁来决定通信协议。在ALPN的描述中,是让客户端先发送一个协议优先级列表给服务器,由服务器最终选择一个合适的。而NPN则正好相反,客户端有着最终的决定权。

Image


HTTP/2中的安全问题,通过三部分阐述,1)已经发现的安全漏洞(Imperva发现的4个安全漏洞) 2)IETF的安全考虑 3)其它可能存在的风险

1、已经发现的安全漏洞

1)慢速连接攻击 (CVE-2016-1546)

慢速连接攻击在HTTP/1.1时代就已被提出并被广泛利用,到了HTTP/2时代,该类攻击没有得到缓解,甚至有所加剧。这跟HTTP/2的特性有关,一个TCP连接可以承载很多streams,服务端得为每个stream维持一个worker thread,客户端通过请求一个很大的资源,并且维持一个很小的窗口,就能一直维持着这个worker thread,反复复制这种stream,就能在一个connection上占用大量worker thread,直至占满服务端的连接数,导致DoS。由于HTTP/2较之HTTP/1.1维持一个连接开销更低,所以更容易发起慢速连接攻击。

关于慢速连接攻击,不同的中间件利用方法有点区别,如Apache,当窗口太小时,服务端可能会杀掉这个thread,所以得时不时发个WINDOW_UPDATE frame,keep thread alive。

Image


2)HPACK BOMB (CVE-2016-1544, CVE-2016-2525)

HPACK是专为HTTP/2的压缩机制,其中有一项是dynamic table,可以动态的把较长的字符串以很短的字符串代替。这个dynamic table存在一个风险,攻击者可以构造一个数据包,在dynamic table里添加一条value很长的映射,然后客户端持续向服务端发带这条映射的key的请求,服务端收到后,会把key放到内存中,取出对应的value,再去做分析处理,如果这个value很长的话,甚至几个请求就能把服务端的内存打满,value长度受SETTINGS_HEADER_TABLE_SIZE字段限制。

Image


3)依赖循环攻击 (CVE-2015-8659)

HTTP/2中的stream具有依赖和优先级的特性,其中stream的依赖示意图如下,执行13后执行11,执行11后执行9,依此类推。

Image

当streams的数量超过MAX_CONCURRENT_STREAM时,会销毁最早的stream,新的stream会替代该位置,如下:

Image

这样就形成一个依赖循环,一直占用服务器资源。

Image


4)数据流多路复用(CVE-2016-0150)

RFC中规定,每个stream的ID必须大于该connection上之前的stream的ID,并且stream ID不能重复使用。在IIS 10中,如果向服务端发送2个相同stream ID的请求包,会导致服务器蓝屏。

Image

Image

Image


2、IETF对HTTP/2安全性的考虑:

Image

IETF对HTTP/2有一些安全方面的考虑,应用出现的一些安全风险,可能是中间件厂商没有遵守IETF安全的建议,下面列举几项:

1)Server Authority 服务端认证

Server Push开启了HTTP协议的全新交互方式,服务端可主动向客户端推送数据,这个过程是为了提高用户体验,所以肯定不能让用户去验证服务端推送过来的数据是否安全,这就需要客户端在接收响应前验证PUSH_PROMISE Frame中的authority字段有效性。

2)Cross-Protocol Attacks 跨协议攻击

跨协议攻击在TLS下是很难利用的,因为ALPN提供了明确的指示说明服务端支持HTTP/2,这有效阻止基于TLS协议的跨协议攻击。但在明文版本的HTTP/2来说,基本没有对这类攻击进行防护,所以尽量只支持TLS上的HTTP/2,事实上大部分浏览器也只支持TLS上的HTTP/2。

3)拒绝服务的考虑

HTTP/2虽然在传输上提高了效率,但占用了更多的系统资源,各种新增的优化机制如果配置不当,会引起服务端的拒绝服务,如Frame的优先级和依赖性、报头压缩、流量控制等等。


3、其它可能存在的风险

除了以上谈到的安全风险,HTTP/2支持的滞后和不完善,也会带来很多安全风险。如防护设备不支持解析HTTP/2,或者没有理解HTTP/2的特性,不能识别HTTP/2中特有的风险等,安全基线产品不能理解HTTP/2中新的安全挑战,以上这些风险,都需要各个安全厂商在攻击者前面及时分析,做好应对措施。

 

三、谈理想

HTTP/2是否有未来,这个话题是很多大刀阔斧改进的协议不可避免的话题,因为升级成本很高,但通过下面的统计图表可以看到,未来已来(以下纵坐标单位为%):

Image

Image


大家可能意识不到身边哪些网站是HTTP/2,可以在chrome上装个插件:HTTP/2 and SPDY indicator,我暂时发现www.aliyun.comwww.teambition.com是HTTP/2。

 

下一个话题,各种Web测试怎么办,如安全测试,Burp还能用吗?官方回答,不能:

Image

https://support.portswigger.net/customer/portal/questions/11690301-http2-support

但据说Fiddler可以用,配置方法跟以上介绍的wireshark调试HTTP/2的方法类似。

 

另外,安全产品要如何支持,主动检测的产品需要支持建立HTTP/2的连接,Python现阶段只有Hyper支持HTTP/2,Requests并不支持,被动检测的产品需要解析HTTP/2的数据包,并且能识别HTTP/2中特有的安全风险,不然面对HTTP/2的站点或HTTP/2的流量,安全产品基本就不能用了。

 

最后,还有很多内容没有写,比如HTTP/2在移动APP中的应用。全文大部分是在粘贴PPT中的内容,我对HTTP/2这个协议理解有限,后期有新的认识再做更新。

 

参考链接:

https://tools.ietf.org/html/rfc7540

https://www.imperva.com/docs/Imperva_HII_HTTP2.pdf

https://www.gitbook.com/book/ye11ow/http2-explained/details

https://segmentfault.com/a/1190000002765886

https://tools.ietf.org/html/draft-ietf-httpbis-http2-17#page-12

https://zhuanlan.zhihu.com/p/22875375

本文版权归漏洞盒子所有