该PHP脚本未过滤用户输入的token参数,直接拼接到Content-Disposition响应头中,导致攻击者可通过构造恶意文件名实现HTTP头部注入,进而触发浏览器端XSS。

这段PHP代码的问题,其实藏得很深。表面上看,它只是把用户输入的token参数拼到了Content-Disposition响应头里,但正是这个“简单拼接”,直接打开了HTTP头部注入的大门——最终演变成反射型XSS。漏洞的核心,在于$input没有经过任何有效的校验或编码,就被扔进了header()函数的文件名字段:

$input = $_GET['token'];
$input = str_replace(array("\n", "\0"), '', $input); // 仅过滤换行符和空字节,防护严重不足
header("Content-Disposition: attachment; filename='$input'"); // ⚠️ 危险拼接!

开发者可能觉得,去掉换行符和空字节就够了。但现实是,攻击者仍然可以自由注入回车符(\r)、单引号、双引号、分号以及各种HTML特殊字符。关键点在于:当浏览器解析Content-Disposition值时,如果服务端返回的响应头中存在换行(比如通过\r\n绕过),攻击者就能注入额外的HTTP头——比如X-XSS-ProtectionContent-Type,甚至直接把

这里面的逻辑其实不复杂:

  • %0d%0a\r\n 的URL编码,用于在filename=后面注入新的HTTP头;
  • Content-Type: text/html 强制浏览器以HTML方式渲染响应体;
  • 后续的两个 %0d%0a(即\r\n\r\n)则用来分隔响应头和响应体;
  • 最后, 成为响应正文,被浏览器当作HTML执行。

当然,这里需要提醒几点:

  • 并非所有浏览器都支持在Content-Disposition头中注入HTML MIME类型并执行脚本。现代Chrome和Firefox对附件响应中的JS执行有严格限制,但IE、旧版Edge以及部分配置宽松的环境仍然可能触发
  • 更稳定的利用方式其实是结合filename="test.html"加上响应体内容(比如print ",且响应没有设置正确的Content-Type,浏览器可能因为MIME嗅探误执行(不推荐依赖这种方式);
  • 根本的修复方案是:禁止用户控制文件名中的任意字符。应该使用白名单过滤(比如只允许字母、数字、下划线、短横线),并强制添加安全扩展名:
$filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $_GET['token']) . '.txt';
header("Content-Disposition: attachment; filename=\"$filename\"");
  • 另外,始终对输出到HTTP头的变量进行严格编码。注意rawurlencode()只适用于URL路径,不适用于header()。更安全的做法是完全避免动态文件名,或者使用固定名称加上服务端映射ID。

总结一下:XSS不仅仅躲在HTML标签里,它同样潜伏在HTTP响应头中。任何将用户输入拼接到header()setcookie()Location等函数的行为,都必须经过严格的白名单校验和上下文编码——信任用户输入,永远是Web安全的第一道失守防线。

本文转载于:https://www.php.cn/faq/2314074.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。