Early this year, two guys broke the news of a critical XSS vulnerability found in the popular software Adobe Acrobat Reader. Being free and de facto, the PDF reader coupled with the abundance of pdfs online, this vulnerability has the potential to wreck havoc in a big way.
This vulnerability has been much talked about and there are plenty of resources online that you can look for if you want the details on how it works and what kind of damage it can bring. Here are a few links for reference:
Not long after the story broke, Adobe and the popular browser makers have issued fixes/updates for their software. Unfortunately, we all know that much time (years maybe?) is required for these updates to propagate to the masses, hence it is still crucial for webmasters to put up some form of defence from the server side. As a webmaster myself, I was tasked to fix this up on my company websites. The following article describes the fix which I've implemented. Source code and binaries (for the lazy) are provided above.
How it works
The most obvious way to fix this problem is to always get the user to download the file instead of opening it in the plugin. To do that, we simply need to change MIME type for PDF files and add a custom header to force the download.
Content-Disposition: attachment; filename=xxx.pdf
Both of these headers instruct the browser to download the file instead of opening it in the vulnerable Adobe plugin.
Here are instructions for adding these headers to the server response for PDF documents in Apache and IIS.
Add these lines to the httpd.conf file inside the <directory>tags.
AddType application/octet-stream .pdf
Header add Content-Disposition "attachment"
Change the MIME type to "
- Start Computer Management. (Right click My Computer, select Manage)
- Right click on the Services and Applications > Internet Information Services (IIS Manager)
- Select Properties > MIME Types
- Look for .pdf. The MIME type should be application/pdf. Change this to application/octet-stream
- Click OK twice
Add the Content-Disposition header (this needs to be done on each directory or for each PDF file individually):
- In the IIS Management tool (not in Windows Explorer), select a directory with PDF content or an individual PDF file
- Right-click on the directory or file
- Select Properties
- Click the HTTP Headers tab
- In the Custom HTTP Headers section, click Add
- A dialog appears. In the Custom-header name field enter
Content-disposition. In the Custom-header value field, enter
- Click OK twice
- Restart IIS for the changes to take effect
Do note that this is not a foolproof solution, but changing the HTTP response headers will go a long way toward reducing the risk to users of your site. Although this works in current versions of the more popular browsers, it is possible that some browsers could ignore these headers and open the PDF with the Adobe plugin anyway.
If forcing the user to download the PDF is not your cup of tea, and you have to let your users view the PDF in place in the browser, then here is a solution that lets you have your cake and eat it too. It is however for IIS only since this is after all a .NET site.
About the code
The snippets of code below show the key functionalities:
public class PDFXSSFilter : System.Web.IHttpHandler
public void ProcessRequest(HttpContext context)
tokenvalue = request.QueryString["p"];
if (tokenvalue == null)
string x = context.Server.UrlEncode
string redirectURL =
request.Url.AbsolutePath + "?p=" + x + "#a";
tokenvalue = tokenvalue.Split("#".ToCharArray(), 2);
context.Response.ContentType = "application/pdf";
context.Response.ContentType = "application/octet-stream";
"attachment; filename=" + pdfFileName);
Using the code
First thing you need to do is to compile the code and drop it into the /bin directory of the ASP.NET application. For those who are lazy, you can grab the binaries in the link above.
Next, configure IIS to call the ASP.NET application for the .pdf extension:
- Open the IIS Admin tool
- Right click on the site/virtual directory you wish to implement this on
- Select the Home Directory tab (or Virtual Directory depending on the type of site)
- Under the Application settings section, click on the Configuration button
- Click Add to create a new extension type
- Under Executable, click on Browse and look for the appropriate aspnet_isapi.dll (must be v2.0 and up)
- For Extension, enter .pdf
- Under Verbs, you can leave this as All verbs - if performance is an issue, you may try limiting this to
- Click OK
- Click OK to close the Application Configuration window
- Click OK to close the site properties
Finally, you will need to make the following changes to the web.config file:
- In the
<system.web> section, look for the
<httpHandlers> section. If one does not exist add it now.
- Add the entry for the
PDFXSSFilter to the
httpHandlers section. Your configuration may look something like this:
<add verb="*" path="*.pdf" type="PDFXSSFilter,PDFXSSFilter" />
- Now add an entry for the
TokenEncryptionKey to the
<appSettings> section. Make sure to change this key for your site or it will defeat the purpose of adding this code.
- Now add an entry for the
TokenTimeout to the
<appSettings> section - this allows you to specify the timeout value of the token. Your configuration may look something like this:
<add key="TokenTimeout" value="10" />
<add key="TokenEncryptionKey" value="ABCDEF" />
Solution #2 presented in this article is based on code by Mike Metzger.
- 24th April, 2007 - First public release