Any opinions expressed here are my own and not necessarily those of my employer (I'm self-employed).

Sep 6, 2012

Security through HTTP response headers

Security headers in an HTTP response
There are many things to consider when securing a web application but a definite "quick win" is to start taking advantage of the security HTTP response headers that are supported in most modern browser. It doesn't matter which development platform you use to build your application, these headers will make a notable difference for the security of your website anyway!

The screenshot shows what the security headers look like. The security headers are included in the web server's response to a browser — instructing the browser to enable (or disable) certain security features. They're invisible to the user, but you can have look at them with tools such as Fiddler or the developer tools that are built into the major browsers. In IE or Chrome press F12, in Opera (Ctrl+Shift+i), in Firefox (Ctrl+Shift+k), for Safari have a look here to enable the developer tools.

A great thing about these response headers is that they're very easy to get started with. In many cases you might not even have to change a single line of code in your application as you can set the headers either through your application's configuration, or they can likely be set by whatever web server you use.

If you're building ASP.NET applications I would like to point you to NWebsec, an ASP.NET security library that lets you easily configure these headers for your application. Go and have a look at the documentation, it explains how you can configure the headers through web.config. Don't worry, if you're the MVC kind of person you can use filter attributes instead. You'll find the library on NuGet so you'll be up and running in a matter of minutes! Disclaimer: I built it, so I think it's pretty cool.

A quick note: Last year, I gave a lightning talk at the ROOTs conference about the role browsers play for your online security. There I also discussed security headers. Slides and video are online if you want to check them out: "The browser - your best friend and worst enemy" (slides / video).

Now let's have a look at the headers and how they can improve the security of your website.

The security headers
Here's the security headers that are supported by some or all of the major browsers at the time of writing.

  • X-Frame-Options
  • Strict-Transport-Security
  • X-Content-Type-Options
  • X-Download-Options
  • X-XSS-Protection
  • X-Content-Security-Policy / X-Content-Security-Policy-Report-Only
  • X-WebKit-CSP / X-WebKit-CSP-Report-Only

We'll have a look at each header and discuss their merits. I've included some important references for each header so you can study them in more detail if you'd like. To remove any doubt that these headers help prevent attacks that are both real and practical, I've also included some videos showing how some of the attacks work.

The X-Frame-Options header was introduced a couple of years ago to hamper Clickjacking (AKA UI redressing) attacks. In a typical Clickjacking attack a malicious website will load your website in an iframe and use various UI tricks to make the frame invisible for the user. Then, when the user clicks something on what appears to be the main website, the click is actually done in the hidden iframe. Consequently, the user has been tricked into clicking something on your website. I've embedded a short video to show how the attack works — it's much easier to understand when you see it in action. Note how the target website is loaded in a small iframe, which follows the mouse cursor around. Pretty cool, huh?

The framesniffing attack was discovered more recently, demonstrating how information can be extracted from a page by loading it in an iframe and then try scrolling to known elements on the page. The attack itself is very interesting, and their demo is absolutely fantastic, you should go watch the video on the Context Information Security blog: Framesniffing against SharePoint and LinkedIn.

The X-Frame-Options header will help thwart these attacks, it will instruct the browser to not load your page in a frame. The header can have to values:

X-Frame-Options: Deny 
X-Frame-Options: SameOrigin 

Setting it to "Deny" will make the browser refuse to load the page in an iframe altogether. Setting it to "SameOrigin" will allow pages from the same origin to load the page in an iframe.

You can see the header demonstrated on this demo site by the NoScript developer: http://evil.hackademix.net/frameopts/. Open it in different browsers and see the result!

As a final note, this header does not protect against Cross Site Request Forgery (CSRF) attacks. Here's an excellent write up about just that: CSRF, Clickjacking, and the Role of X-Frame-Options.

Browser support since: Opera 10.50, IE 8, Firefox 3.6.9, Chrome, Safari 4

Nakedsecurity: Facebook clickjacking: Dirty Italian schoolteacher undresses
OWASP: Clickjacking
Internet-Draft: HTTP Header Frame Options
Browser Security Handbook: Arbitrary page mashups (UI redressing)
IEBlog: Combating ClickJacking With X-Frame-Options
Mozilla Developer Network: The X-Frame-Options response header
Microsoft.com: Mitigating framesniffing with the X-Frame-Options header

The Strict-Transport-Security header will instruct the browser to do two important things:
  1. Load all content from your domain over HTTPS
  2. Refuse to connect in case of certificate errors and warnings
Employing this header will help prevent attacks such as SSL stripping and other middleperson attacks, and will prevent the user from clicking through certificate warnings.

The SSL stripping attack is quite interesting, see the video for a quick demo of how it works. A tool to perform this attack was first presented by Moxie Marlinspike at the Blackhat conference back in 2009. You can download the tool and watch the Blackhat talk over at Marlinspike's website.

So how does it work? Conceptually, it's quite simple. You sit in-between the user and the server and rewrite all links pointing to "https" so they instead point to "http", in real time. Now you have "stripped" away SSL, and the user's communication to you is unencrypted. You might argue that the attack could be detected by the user since there is no padlock or other indication in the browser that the connection is sure. I say watch the video!

Now, if you're running a secure site over SSL and you've got a proper SSL certificate installed for your site your users should not see any certificate warnings. If they do, it might be caused by an attacker trying to impersonate your site with a fake certificate. In any case, certificate warnings means that something isn't right. Strict Transport Security will in such cases make the browser terminate the connection — not giving the user the option to "continue anyway".

Strict Transport Security defines a max-age parameter, and an optional includeSubdomains flag. max-age tells the browser for how many seconds it should enforce the policy. includeSubdomains indicates whether the policy should also be applied to subdomains. Here's what the header looks like:

Strict-Transport-Security: max-age=43200
Strict-Transport-Security: max-age=31536000; includeSubDomains

Browsers will ignore the header if it's included in a response over HTTP — it must be served over HTTPS to have an effect. Hence, the header is of no use to you if you're running a site that only serves content over HTTP. If you're running a site with mixed context, i.e. some content is served over HTTP and some content is served over HTTPS, this header will force all traffic to HTTPS. If you're running a site where all content should be served over HTTPS, the header will function as a safety net and you should definitely enable it.

To learn more about the challenges related to content served over HTTP vs HTTPS I highly recommend that you read this blog post by Adam Langley from the Google Security Team: Living with HTTPS. And remember, login forms must always be served over HTTPS, Troy Hunt has a nice write-up on those issues.

Browser support since: Opera 12, Firefox 4, Chrome

Thoughtcrime.org: sslstrip
Wikipedia: HTTP Strict Transport Security
Internet-Draft: HTTP Strict Transport Security (HSTS)
The Chromium projects: HTTP Strict Transport Security
Mozilla Developer Network: HTTP Strict Transport Security

X-Content-Type-Options / X-Download-Options
These headers were introduced in IE 8 and are both related to MIME-handling in IE, so we'll cover them within the same section. MIME-types are used to identify different types of data. Consider what happens when the browser downloads a file from a web server — and keep in mind that a file is just a chunk of bytes. The browser has no idea how to interpret the file unless the server gives it a hint. This is where MIME-types come into play, it lets the server tell the browser just what kind of file it is. If it's a PDF it should for example tell the browser application/pdf and the browser would now how to handle the file.

Handling MIME-types correctly is important for any website, but especially for those serving user controlled content. When a resource is returned from a webserver, the response includes a Content-Type header to tell the browser what kind of resource was served. If it was a plain text file, it should include the response header:

Content-Type: text/plain

Problem is, Internet Explorer has a MIME-sniffing feature. Even if you claim it's a plain text file IE might decide that you got the media type wrong, make a guess on what the content was, and then possibly execute it. It's all well explained on the IEBlog:
Unfortunately, MIME-sniffing also can lead to security problems for servers hosting untrusted content. Consider, for instance, the case of a picture-sharing web service which hosts pictures uploaded by anonymous users. An attacker could upload a specially crafted JPEG file that contained script content, and then send a link to the file to unsuspecting victims. When the victims visited the server, the malicious file would be downloaded, the script would be detected, and it would run in the context of the picture-sharing site. This script could then steal the victim’s cookies, generate a phony page, etc.
Head over to the IEBlog to read the entire article, it's quite interesting.

(Update Sep. 30): IE9 will not sniff "plain/text" resources, unless "Compatability" view is enabled.

To disable the MIME-sniffing, add the header:

X-Content-Type-Options: nosniff

You'll find that the next header, X-Download-Options, is also explained in the same blog post. It's a similar problem, but for downloads of html files. If a user chooses to open the file directly, it will execute as if it were part of the website. Setting the header will force the user to save the file, then open it manually — and the file will then not be executed in the site's context.

To disable the option to open a file directly on download, set the header:

X-Download-Options: noopen

The IEBlog explains that these headers increase security when you deal with user controlled content and you might conclude that "nobody uploads stuff to our website so we'll be fine." I would argue that you have to think beyond "user controlled." If your site has some other vulnerability that lets an attacker manipulate any of the files served from your site, the MIME-sniffing might be what determines whether or not the attacker can execute scripts in your users' browsers. Therefore, you should seriously consider enabling these headers as a defense-in-depth measure.

Browser support since: IE 8

IEBlog: IE content-type logic
IEBlog: IE8 Security Part V: Comprehensive Protection
IEBlog: MIME-Handling Changes in Internet Explorer (concerns IE9)
IANA: MIME Media Types
Wikipedia: Internet media type

The XSS protection was also introduced in IE 8 as a security measure designed to thwart XSS (Cross Site Scripting) attacks. In short, IE tries to detect whether there has occurred an XSS attack, if so it will modify the page to block the attack and display a warning to the user. Head over to the IEBlog  for screenshots and a more thorough explanation.

You can set the XSS filter on or off (1 or 0), and there's an optional parameter called mode. If you set mode to block, the page will not be displayed at all. Here are examples of how you can set the header:

X-XSS-Protection: 0 
X-XSS-Protection: 1; mode=block 

Note that the XSS filter is enabled by default in IE, but it's not in blocking mode. Hence, you don't need to send the header unless you want to disable the filter for some reason, or if you want to enable blocking mode.

You can go ahead and give it a try over at: http://www.enhanceie.com/test/xss/BlockMode.asp. Remember, you must open that page in IE!

Browser support since: IE 8

IEBlog: IE8 Security Part IV: The XSS Filter
IEBlog: Controlling the XSS Filter
MSDN: Event 1046 - Cross-Site Scripting Filter

Content security policy (CSP) is a fairly new initiative to counter XSS attacks. It disables execution of inline scripts in webpages and lets you specify a whitelist of sources from where your webpages are allowed to load scripts and other content. CSP version 1.0  is currently a W3C working draft but is expected to be ratified any time soon.

CSP defines a number of directives for different types of content that are commonly loaded by webpages:

default-src, script-src, object-src, style-src, img-src, media-src, frame-src, font-src, connect-src, sandbox (optional), report-uri

If you're familiar with HTML you'll recognize most of these. The default-src is special, it serves as the default setting for all the other directives. report-uri is also special, it will tell the browser where it should report CSP violations. That's right, the browser can report violations back to your site so you can log them!

For each of these directives you can specify one or more sources. There are four keywords that have special meaning and they must be enclosed in single quotes in your CSP header:

  • 'none' (nothing will be loaded)
  • 'self' (load things from the same domain as the page was served, i.e. same scheme, host, port)
  • 'unsafe-inline' (enables execution of inline and possibly insecure scripts/styles)
  • 'unsafe-eval' (enables execution of eval and other risky functions)

In addition to these reserved keywords you can supply one or more hosts that you will want to load resources from. If there's multiple sources they must be separated by a whitespace character. It's probably best explained with an example:

X-Content-Security-Policy: default-src 'self' stuff.nwebsec.codeplex.com; script-src scripts.nwebsec.codeplex.com ajax.googleapis.com

If it was sent for the page you're reading now, this header would set the default sources to http://www.dotnetnoob.com ( 'self' ) and stuff.nwebsec.codeplex.com for ALL of the directives. Next, the script-src directive overrides the default-src and specifies that scripts can be loaded from scripts.nwebsec.codeplex.com and ajax.googleapis.com.

Another cool part of the specification is the Report-Only mode. Using a Report-Only header will avoid enforcing the CSP but will still make the browser report violations back to the server. That way you can deploy a new CSP in Report-Only mode first to get a feeling of whether it will break your site or not. And that's a very cool feature.

Since CSP is currently a working draft, browser support is a bit lacking. The good news is that Firefox supports it through the HTTP headers:


Chrome also has support for it, but uses different headers:


One would also expect and hope that other browsers (most notably IE, Opera, Safari) would be fast followers in implementing the standard once it's ratified. When it is, the CSP header will be:


To learn more about CSP, I would urge you to read the "Introduction to CSP" found in the references. The standard is also very readable. While you're waiting for completion of the standard you can always check your browser's CSP support.

Draft spec browser support since: Firefox 4, Chrome 16

OWASP: Cross-site_Scripting_(XSS)
HTML5 rocks: An introduction to Content Security Policy
W3C Working Draft: Content Security Policy 1.0

Setting HTTP headers
I guess you're now all excited and motivated to get started with these security headers in your web application. Since this post didn't turn out to be very ASP.NET specific I'll include some pointers on how to do that for a couple of other platforms as well.

Now, some useful links for the non-ASP.NET people and those reluctant to use my ninja bits. Headers can usually be set globally through web server configuration. If you're running IIS, here's how you can add headers in IIS itself. If you're running Apache you should have a look at mod_headers, it will do what you want.

Headers can also be set by your web application. If you're building stuff with e.g. PHP, the header function is your friend. If you're an ASP.NET person and don't trust so-called security libraries you find around the Internet, fine. Do it yourself with the HttpResponse.AddHeader Method.

That was it. I look forward to reading the reports saying that the use of security headers around the web is on the rise. Good luck!


  1. Very useful article.

  2. Do you know if there is any support for content security policies in ASP.NET 3.5 webforms sites or is support limited to ASP.NET MVC sites Framework 4 / 4.5 only?

    1. I've never tried using CSP with Web Forms, but I assume it would be rather problematic since you often get auto generated JavaScript in your Web Forms. There might be hope though, as CSP 1.1 introduces script nonces. At least in theory, a script nonce could be added to those auto generated scripts and you'd benefit from CSP.


Copyright notice

© André N. Klingsheim and www.dotnetnoob.com, 2009-2015. Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and/or owner is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to André N. Klingsheim and www.dotnetnoob.com with appropriate and specific direction to the original content.

Read other popular posts