HTTP 響應拆分(HTTP Response Splitting),又稱 **CRLF 注入**。攻擊者通過控制輸入數據注入換行符(\r\n,URL 編碼為 %0d%0a),截斷原響應頭,注入偽造的響應頭與響應體,構造雙重響應,實現緩存投毒、XSS、釣魚等攻擊。
所有將用戶輸入直接用於 HTTP 響應頭的函數:
header() —— 如 Location 重定向setcookie() / setrawcookie() —— Cookie 值session_id() —— Session ID 設置<?php
// 直接使用用戶輸入構造重定向
header("Location: " . $_GET['page']);
?>
請求:
GET /location.php?page=%0d%0aContent-Type:%20text/html%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%20158%0d%0a%0d%0a<html><body><form%20method=post%20name=form1>%E5%B8%90%E5%8F%B7%20<input%20type=text%20name=username%20/%3E<br%20/%3E%E5%AF%86%E7%A0%81%20<input%20name=password%20type=password%20/%3E<br%20/%3E<input%20type=submit%20name=login%20value=%E7%99%BB%E5%BD%95%20/%3E</form></body></html> HTTP/1.1
Host: localhost
解碼後的注入內容:
Content-Type: text/html
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 158
<html><body><form method=post name=form1>帳號 <input type=text name=username /><br />密碼 <input name=password type=password /><br /><input type=submit name=login value=登錄 /></form></body></html>
服務器響應:
HTTP/1.1 302 Found
Location: https://www.00aq.com
...(原響應)
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 158
<html>...(攻擊者注入的偽造登錄頁)
✅ **一個請求產生兩個響應**,瀏覽器/代理可能解析第二個響應,導致用戶看到偽造頁面。
某站掃描器報告 /?r=XXXXX 存在 CRLF 漏洞。代碼審計發現:
<?php
$redirect = $_GET['r']; // 直接賦值,無任何過濾
header("Location: $redirect");
?>
<?php
$allowed_redirects = [
'home' => '/index.php',
'login' => '/login.php',
'dashboard' => '/user/dashboard.php',
];
$key = $_GET['r'] ?? 'home';
$redirect_url = $allowed_redirects[$key] ?? '/index.php';
header("Location: $redirect_url");
exit;
?>
<?php
$redirect = $_GET['r'] ?? '/';
// 移除所有換行符及變體
$redirect = str_replace(["\r", "\n", "%0d", "%0a", "%0D", "%0A"], '', $redirect);
// 可選:僅允許相對路徑或特定域名
if (!preg_match('~^(/|https?://allowed\.domain\.com/)~', $redirect)) {
$redirect = '/';
}
header("Location: $redirect");
exit;
?>
在框架入口/中間件統一過濾所有 header()、setcookie() 參數:
<?php
// 基礎控制器或中間件
class Response {
public static function header(string $header, bool $replace = true, int $code = 0): void {
// 過濾頭名稱與值中的 CRLF
$header = preg_replace('/[\r\n]/', '', $header);
header($header, $replace, $code);
}
public static function redirect(string $url, int $code = 302): void {
$url = preg_replace('/[\r\n]/', '', $url);
// 可選:驗證 URL 合法性
self::header("Location: $url", true, $code);
exit;
}
public static function setCookie(string $name, string $value = '', array $options = []): void {
$value = preg_replace('/[\r\n]/', '', $value);
setcookie($name, $value, $options);
}
}
?>
# 隱藏版本信息
server_tokens off;
# 禁止非法頭字符(需 Nginx 1.19.1+)
# 更推薦在應用層處理
; php.ini
expose_php = Off ; 隱藏 PHP 版本
; disable_functions = header,setcookie,session_id ; 極端情況可禁用,但會影響正常功能
ServerTokens Prod
ServerSignature Off
%0d%0aSet-Cookie:test=injected,觀察響應頭header(、setcookie(、session_id(,檢查參數來源是否經過過濾CRLF 注入雖屬經典漏洞,但近年在 API 重定向、OAuth 回調、多域名 Cookie 共享等場景頻發。核心修復原則:**絕不信任用戶輸入用於 HTTP 頭**,採用白名單驗證或框架級統一過濾,配合服務器配置加固,徹底消除風險。