针对 Universal PDF XSS 漏洞的服务器端修复






4.50/5 (5投票s)
2007年4月24日
5分钟阅读

40932

326
本文介绍了一种针对最近发现的 Adobe PDF 阅读器插件漏洞的服务器端修复方法。
引言
今年早些时候,两人爆出热门软件 Adobe Acrobat Reader 中发现了一个关键的 XSS 漏洞。由于该 PDF 阅读器免费且普及,加上网上存在大量的 PDF 文件,此漏洞有可能造成大规模的破坏。
本质上,该漏洞为攻击者提供了一种通过特制 URL 访问 PDF 文件的方式,从而在用户浏览器中执行 JavaScript。通过在 URL 参数中添加 JavaScript 命令,攻击者可以窃取 cookie 和敏感数据,更改网站的外观或行为,以及在网络钓鱼攻击中将用户重定向到站外。
这个漏洞已经被广泛讨论,网上有大量的资源可供您查阅,如果您想了解其工作原理和潜在的损害。以下是一些参考链接:
消息爆出后不久,Adobe 和流行的浏览器制造商都发布了针对其软件的修复/更新。不幸的是,我们都知道这些更新需要很长时间(可能需要几年?)才能普及到大众,因此,对于网站管理员来说,从服务器端实施某种形式的防御仍然至关重要。作为一名网站管理员,我被指派负责修复我公司网站上的此问题。以下文章将介绍我已实施的修复方法。源代码和二进制文件(懒人版)已在上面提供。
工作原理
问题的核心在于 PDF 在 Acrobat Reader 的浏览器插件中打开;该插件提供了一个设计不佳的“功能”,可以执行通过 URL 传递的自定义 JavaScript 代码。
解决方案 #1
解决此问题的最直接的方法是始终让用户下载文件,而不是在插件中打开它。要做到这一点,我们只需更改 PDF 文件的 MIME 类型并添加自定义标头以强制下载。
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=xxx.pdf
这两个标头都指示浏览器下载文件,而不是在易受攻击的 Adobe 插件中打开它。
以下是在 Apache 和 IIS 服务器响应 PDF 文档时添加这些标头的说明。
Apache 配置
在 httpd.conf 文件中 <Directory>
块内添加以下行。
AddType application/octet-stream .pdf
<Files *.pdf>
Header add Content-Disposition "attachment"
</Files>
IIS 配置
将 MIME 类型更改为“application/octet-stream
”
- 启动 计算机管理。(右键单击 我的电脑,选择 管理)
- 右键单击 服务和应用程序 > Internet Information Services (IIS 管理器)
- 选择 属性 > MIME 类型
- 查找 .pdf。MIME 类型应为 application/pdf。将其更改为 application/octet-stream
- 单击 确定 两次
添加 Content-Disposition 标头(需要为每个目录或每个 PDF 文件单独进行此操作)
- 在 IIS 管理工具(而不是 Windows 资源管理器)中,选择一个包含 PDF 内容的目录或单个 PDF 文件
- 右键单击目录或文件
- 选择 属性
- 单击 HTTP 标头 选项卡
- 在 自定义 HTTP 标头 部分,单击 添加
- 将出现一个对话框。在 自定义标头名称 字段中输入
Content-disposition
。在 自定义标头值 字段中,输入attachment
- 单击 确定 两次
- 重启 IIS 使更改生效
请注意,这不是一个万无一失的解决方案,但更改 HTTP 响应标头将极大地降低您网站用户面临的风险。尽管这在当前流行的浏览器版本中有效,但某些浏览器可能会忽略这些标头,仍然使用 Adobe 插件打开 PDF。
解决方案 #2
如果您不喜欢强制用户下载 PDF,并且需要让用户在浏览器中直接查看 PDF,那么这里有一个解决方案,可以让您两全其美。然而,这仅适用于 IIS,因为毕竟这是一个 .NET 网站。
关于代码
其基本思想是在 Web/应用程序服务器上实现一个过滤器,该过滤器将验证请求并在将文件流式传输给用户之前剥离 URL 中的任何 JavaScript。如果无法验证请求,我们将强制用户下载文件。
以下代码片段展示了关键功能
public class PDFXSSFilter : System.Web.IHttpHandler
{
...
public void ProcessRequest(HttpContext context)
{
...
tokenvalue = request.QueryString["p"];
...
// Check if the token value is defined in the URL
if (tokenvalue == null)
{
// If not, calculate the secure value
string x = context.Server.UrlEncode
(EncryptData(request.ServerVariables["REMOTE_ADDR"]));
// Now build the redirect URL
string redirectURL =
request.Url.AbsolutePath + "?p=" + x + "#a";
// Now redirect them to the calculated URL
context.Response.Redirect(redirectURL, false);
}
else
{
// Remove the javascript behind first
tokenvalue = tokenvalue.Split("#".ToCharArray(), 2)[0];
// Decode the token next
tokenvalue =
context.Server.UrlDecode(tokenvalue).Replace(' ','+');
// Check if token data is valid
if (ValidateData(tokenvalue,
request.ServerVariables["REMOTE_ADDR"], context))
{
// If so, transmit as PDF
context.Response.ContentType = "application/pdf";
}
else
{
//Otherwise, transmit as an octet-stream
//(i.e., force download)
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("Content-Disposition",
"attachment; filename=" + pdfFileName);
}
//Transmit the file regardless
context.Response.TransmitFile
(context.Server.MapPath(request.FilePath));
}
}
...
}
}
使用代码
您需要做的第一件事是编译代码并将其放入 ASP.NET 应用程序的 /bin 目录中。对于那些懒得编译的人,您可以从上面的链接获取二进制文件。
接下来,配置 IIS,让它为 .pdf 扩展名调用 ASP.NET 应用程序
- 打开 IIS 管理工具
- 右键单击您希望应用此功能的 站点/虚拟目录
- 选择 主目录 选项卡(或 虚拟目录,取决于站点类型)
- 在 应用程序设置 部分,单击 配置 按钮
- 单击 添加 以创建新的扩展类型
- 在 可执行文件 下,单击 浏览 并找到相应的 aspnet_isapi.dll(必须是 v2.0 及以上版本)
- 对于 扩展名,输入 .pdf
- 在 谓词 下,您可以将其保留为 所有谓词 - 如果性能是问题,您可以尝试将其限制为
GET
和POST
- 单击 确定
- 单击 确定 关闭 应用程序配置 窗口
- 单击 确定 关闭站点属性
最后,您需要对 web.config 文件进行以下更改
- 在
<system.web>
部分,查找<httpHandlers>
部分。如果不存在,请立即添加。 - 将
PDFXSSFilter
的条目添加到httpHandlers
部分。您的配置可能如下所示<httpHandlers> <add verb="*" path="*.pdf" type="PDFXSSFilter,PDFXSSFilter" /> </httpHandlers>
- 现在,将
TokenEncryptionKey
的条目添加到<appSettings>
部分。确保为您的网站更改此密钥,否则添加此代码将失去意义。 - 现在,将
TokenTimeout
的条目添加到<appSettings>
部分 - 这允许您指定令牌的超时值。您的配置可能如下所示<appSettings> <add key="TokenTimeout" value="10" /> <add key="TokenEncryptionKey" value="ABCDEF" /> </appSettings>
致谢
本文提出的解决方案 #2 基于 Mike Metzger 的代码。
历史
- 2007 年 4 月 24 日 - 首次公开发布