
该PHP脚本未对用户可控的token参数做任何过滤或编码,直接拼入Content-Disposition响应头中,可被利用构造恶意文件名实现HTTP头注入,进而触发浏览器解析上下文中的XSS。
该PHP脚本未对用户可控的`token`参数做任何过滤或编码,直接拼入`Content-Disposition`响应头中,可被利用构造恶意文件名实现HTTP头注入,进而触发浏览器解析上下文中的XSS。
这段代码存在典型的HTTP头注入(Header Injection)引发的XSS漏洞,而非传统HTML上下文中的反射型XSS。关键问题在于:
$input = $_GET['token'];
$input = str_replace(array("\n", "\0"), '', $input); // 仅过滤换行和空字节,防护严重不足
header("Content-Disposition: attachment; filename='$input'");虽然代码移除了\n和\0,但未过滤回车符\r、单引号'、双引号"、分号;等关键字符,攻击者仍可通过注入\r\n(CRLF)实现响应头分裂(CRLF Injection),从而注入任意HTTP头(如Content-Type、X-XSS-Protection),甚至操控后续响应体内容。
更隐蔽且实际可行的利用路径是:利用浏览器对Content-Disposition中畸形filename值的不一致解析。现代浏览器(如Chrome、Firefox)在处理如下请求时:
GET /test/2.php?token='"><script>alert(1)</script>
会将响应头解析为:
Content-Disposition: attachment; filename=''"><script>alert(1)</script>'
尽管服务端声明了attachment,但若用户手动取消下载、或浏览器因MIME类型推测失败而选择渲染响应体(尤其当响应无明确Content-Type: application/octet-stream时),则<script>alert(1)</script>可能被当作HTML执行——这正是Security StackExchange所讨论的Content-Disposition XSS场景的核心风险。
✅ 正确修复方式(三重防御):
- 输入验证:白名单限制token仅含字母、数字、下划线;
- 输出编码:对filename值使用rawurlencode()(适用于RFC 5987标准)或addcslashes($input, '"\\') + 双引号包裹;
- 强制安全响应头:添加Content-Type: application/octet-stream和X-Content-Type-Options: nosniff。
示例加固代码:
<?php
require 'flag.php';
$token = $_GET['token'] ?? '';
// 白名单校验
if (!preg_match('/^[a-zA-Z0-9_]{1,64}$/', $token)) {
http_response_code(400);
exit('Invalid token');
}
// RFC 5987 兼容编码(推荐)
$encoded = rawurlencode($token);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename*=UTF-8''$encoded");
header("X-Content-Type-Options: nosniff");
echo "Hello. $token";
?>⚠️ 注意:仅过滤\n\0远远不够;依赖客户端行为(如“用户是否点击保存”)的防御是不可靠的;所有用户输入进入HTTP头前必须严格转义或编码。