为什么一个Http Header中的空格会被骇客利用 - HTTP request smuggling

前言 507383170

前阵子在Netty的issue里有人提了一个问题 http request smuggling, cause by obfuscating TE header ,描述了一个Netty的http解码器一直以来都存在的问题:没有正确地分割http header field名称,可能导致被骇客利用。

引起问题的那段code很简单,它的作用是从一个字符串中分割出header field-name:

for (nameEnd = nameStart; nameEnd < length; nameEnd ++) {
            char ch = sb.charAt(nameEnd);
            if (ch == ‘:‘ || Character.isWhitespace(ch)) {
                break;
            }
 }

这段有什么问题呢?它不应该把空格也当成header field-name的终止符,这会导致Transfer-Encoding[空格]: chunked 被解析为Transfer-Encoding 而不是Transfer-Encoding[空格] 。

乍一看可能让很多人迷惑,一个Header Field名称识别错了为什么会被骇客攻击呢?大不了就缺了一个Header嘛。但真实世界,却没这么简单。这事,还得从现代的web服务架构说起。

起因

链式处理

通常现代的Web服务器并非单体存在的,而是由一系列的程序组成(比如nginx -> tomcat),为了实现均衡负载、请求路由、缓存等等功能。这些系统都会解析HTTP协议来进行一系列处理,处理完后,会将对应的请求分发链路中的下一个(往往也是通过HTTP协议)。

假设现在一个用户的请求到达逻辑服务器B的过程为: 用户 -> A -> B 。通常会有许多个用户连接到A,而为了减少连接建立的消耗,A到B的连接数会少很多,A与B之间会保持稳定的长连接,这样能够使性能得到提升。所以可能属于不同客户端的多个请求会共用同一个连接。(以NGINX举例,Nginx从1.1.4开始支持到后端的长连接池,不过在之前是使用的HTTP/1协议与后端通信,即创建连接处理完之后销毁。)

大家的请求都合在一起了,如何区分请求与请求之间的边界呢?接下来我们再来看看Http协议中的Transfer-Encoding: chunkedContent-Length

Transfer-Encoding: chunked和Content-Length

Http协议通过Transfer-Encoding: chunked(以下简称TE)或Content-Length(以下简称CL)来确定entity的长度。

TE表示将数据以一系列分块的形式进行发送,在每一个分块的开头需要添加当前分块的长度,以十六进制的形式表示,后面紧跟着 ‘\r\n‘ ,之后是分块本身,后面也是‘\r\n‘ 。终止块是一个常规的分块,不同之处在于其长度为0。

而CL通过直接指定entity的字节长度来完成同样的使命。

那么当它们两个同时出现在Http Header里时会发生什么呢?实际上Http协议规范有明确定义,当这两种确定长度的Header都存在时,应该优先使用TE,然后忽略CL。

看到这,结合上述那段不规范的header field-name解析代码,或许你想到了一些东西?假如一个包含了TE和CL的Http请求被处理了两次会发生什么?接下来,我们来看看具体的情况。

发送攻击请求

此时我们依旧使用 用户 -> A -> B 的例子。 这里我们假设B是存在问题的Netty,而A是一个正常的只识别CL的程序。一个包含了Transfer-Encoding[空格]: chunked 和Content-Length的Http请求来了

POST / HTTP/1.1

Host: vulnerable-website.com

Content-Length: 8

Transfer-Encoding[空格]: chunked

0

FOR

然后又来了一个正常的用户请求

GET /my-info HTTP/1.1

Host: vulnerable-website.com

A

A识别到了CL为8,并将这两个请求合并到一个连接中一起传给B

B

B识别到了Transfer-Encoding[空格],按照规则忽略了CL,此时B会怎么分割这两个请求呢:

请求1

POST / HTTP/1.1

Host: vulnerable-website.com

Content-Length: 8

Transfer-Encoding[空格]: chunked

0

请求2

FORGET /my-info HTTP/1.1

Host: vulnerable-website.com

这就糟糕了:服务端报了一个错,正常用户获得了一个400的响应。

(任何上下游识别请求的分界不一致的情况都会出现这样的问题)

如何避免这样的漏洞

  1. 避免重用连接,这样的方式会造成性能的下降。
  2. 使用HTTP/2作为系统间协议,此协议避免了http header的混淆。
  3. 使用同一种服务器作为上下游服务器。
  4. 大家都按规范来,不出BUG。

其中我们展开了解一下为什么HTTP/2可以避免这种情况

HTTP/2不会有含糊不清的Header

HTTP/2是一个二进制协议,致力于避免不必要的网络流量以及提高TCP连接的利用率等等。

它对于常用的Header使用了一个静态字典来压缩。比如Content-Length使用28来表示;`

Transfer-Encoding`使用57来表示。这样一来,各种实现就不会有歧义了。更多的定义详见HTTP/2关于Header静态表的定义

文献

  1. Hypertext Transfer Protocol (HTTP/1.1)关于Http Header Field的解析定义
  2. Transfer-Encoding - HTTP | MDN
  3. Content-Length - HTTP | MDN
  4. What is HTTP request smuggling? Tutorial & Examples
  5. HTTP/1.x 的连接管理 - HTTP | MDN
  6. HTTP/2关于Header静态表的定义
  7. HTTP Keepalive Connections and Web Performance

原文地址:https://www.cnblogs.com/fffl339/p/11695547.html

时间: 2024-08-30 05:03:42

为什么一个Http Header中的空格会被骇客利用 - HTTP request smuggling的相关文章

面试题5:请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

请实现一个函数,将一个字符串中的空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 注意从后向前替换,使得时间复杂度为O(n); public class Main { public static void main(String[] args) { Main main01=new Main(); String str=main01.replaceSpace(new StringBuffer("old s

调用Restful Service 出现415 Unsupported Media Type的问题(Rest Request Header中的Content-Type问题)

用Chrome的插件Simple REST Client 调用POST的REST服务时,老是报415错误,如图. 一开始就以为是服务端的问题,各种google,百度,折腾了一下午未果. 晚上继续看,一不小心看到返回的Response的Headers中Content-Type的值与冒号之间有一个空格,于是将Request的Content-Type:application/json;charset=UTF-8改成Content-Type: application/json;charset=UTF-8

C++用%20替换字符串中的空格(O(n)的时间效率)

#include <iostream> #include <malloc.h> #include <string.h> using namespace std; //将字符串中的空格用%20替换. void Grial(char *str) { if(str==NULL)return ; char *p = str; char *q = NULL; char buf[strlen(str)+1]; int count = 0;//计数空格数. while(*p!='\0

替换字符串中的空格

题目描述: 请实现一个函数,将一个字符串中的空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 输入: 每个输入文件仅包含一组测试样例. 对于每组测试案例,输入一行代表要处理的字符串. 输出: 对应每个测试案例,出经过处理后的字符串. 样例输入: We Are Happy 样例输出: We%20Are%20Happy -------------------------------------------------

Html中各种空格的显示

一.使用全角空格 全角空格被解释为汉字,所以不会被被解释为HTML分隔符,可以按照实际的空格数显示. 二.使用空格的替代符号 替代符号就是在需要显示空格的地方加入替代符号,这些符号会被浏览器解释为空格显示. 空格的替代符号有以下几种: 名称编号描述  不断行的空白(1个字符宽度)  半个空白(1个字符宽度)  一个空白(2个字符宽度)  窄空白(小于1个字符宽度) 可以用名称或编号作为空格的替代符号,名称必须小写,末尾的“;”不能省略. 如: 欢  迎  光  临! 显示效果为:欢  迎  光 

去掉字符串中的空格

1)Trim方法 string   tt=" aaa "; tt=tt.Trim()       去字符串首尾空格的函数 tt=tt.TrimEnd() 去掉字符串尾空格 tt=tt.TrimStart() 去掉字符串首空格 (2)通过ASCII码值去掉字符中的空格 由于空格的ASCII码值是32,因此,在去掉字符串中所有的空格时,只需循环访问字符串中的所有字符,并判断它们的ASCII码值是不是32即可.去掉字符串中所有空格的关键代码如下: CharEnumerator CEnumer

去除字符串中的空格

用C语言写一个函数,去除字符串中的空格,并返回删除的空格的个数.不允许开辟新的空间,只能申请简单类型的自动变量.时间复杂度要求为O(n). #include <bitset> #include<iostream> int deleteSpace(char * pstr); void main() { char word[]="dhkak df d fd fdjfkda dfd ff f fd da "; deleteSpace(word); std::cout&

java路径中的空格问题(转)

java路径中的空格问题 1. URLTest.class.getResource("/").getPath(); URLTest.class.getResource("/").getFile(); URLTest.class.getClassLoader().getResource("").getPath(); Thread.currentThread().getContextClassLoader().getResource("&q

(练手备忘)汇编实现将输入的字符串中的空格去掉后反序输出

功能:任意输入一个字符串,去掉其中的空格后反序输出 注:使用 int 21h 里的 0AH 功能 输入一个字符串时,字符串的第一个字节存储的是字符串的最大长度,第二个字节存储的是实际读入字符的个数 编译器使用的是MASMPlus ;#Mode = DOS MAXLEN = 64 ;设置字符串的最大长度 SPACE = ' ' ;空格 datasg segment buffer db MAXLEN+1,0,MAXLEN+1 dup(0) ;字符串输入缓冲区 string db MAXLEN+3 d