Inglorious Headers

Inglorious Headers

Veit Hailperin
by Veit Hailperin
time to read: 19 minutes

Protection doesn’t have to be cost much. Neither time nor money. For individuals, I’ve documented protection in the series Healthy Paranoia but there are also tricks for server administrators that grant significantly more security with not that much effort. This article is primarily about headers that are sent with an HTTP response and that influence browser behaviour that aims to prevent Cross-Site-Scripting (XSS) attacks among others.

During XSS attacks, JavaScript-Code is being snuck into websites. There are several classes of XSS. So-called Reflected XSS that return the user input without any filters in the response. It’s one of the most commonly found XSS vulnerabilities. Stored or Persistent XSS additionally stores the entered code on the website in things such as guest books or forums. DOM-based XSS that uses active code such as JavaScript on the browser side in an insecure way and Mutation-Based XSS that sees browsers interpret slightly altered code and transform it into working code. Other, more obscure versions of XSS are not being respected here.


During the Browser Wars, in which the browsers competed for dominance on the web, many a function was newly introduced in order to display as many websites as possible correctly. Apart from the fact that this behaviour fostered bad design, the consequences for security can be felt until this day. One of those is the guessing of the character set used in a document.

The problem has several layers. XSS filters are often only defined for one character set (charset). This means that these filters can be circumvented easily if the malicious JavaScript code is being supplied in a different encoding. A classic example for this is UTF-7.

UTF-7 is an encoding that was initially devised for SMTP gateways due to the fact that some gateways were unable to handle 8-bit characters. UTF-7 is easy to recognize, though. Therefore, a filter that recognizes regular XSS such as


could be circumvented by


if the function to guess charsets was enabled in a browser because the browser recognized +ADw- as a < in UTF-7 which lead to the browser adapting to that charset. More details on UTF-7 attacks can be found in OWASP’s XXS-Filter-Evasion Cheat-Sheet. The number of existing encodings is huge. Hackvertor offers a range of various charsets that help in web application penetration tests.

The Content-Type header can be set as follows:

Content-Type: text/html; charset=UTF-8

Using this line, the browser is instructed to not guess the charset and therefore , the protection against these attacks is improved.


Another child of the browser wars is the function that guesses content. If a browser, on the basis of content type guessing, determines that a file should be downloaded, an attacker can abuse this to stage a Drive-by Download Attack. Even Chrome is not safe against those attacks. But bigger impact can be observed when Cross-Site Script Inclusion-attacks (XSSI) attacks are deployed. During an XSSI attack, CSV and JSON can, among others, leak by including a malicious site in a script tag. By including the site in a script tag, it is no longer subject to the Same-Origin Policy and even though they are no longer being executable as a script – due to the fact that they aren’t scripts – data can be leaked using an error message. Various CVEs concerning this have been published.

Limiting this is simple and is stated in the title. The recommended value is:

X-Content-Type-Options: nosniff

The class of XSSI vulnerabilities is not completely mitigated using this line, as shown by current research but this header manages to cover most XSSI attacks.


Going by the name X-XSS-Protection alone, it is easy to assume that this header protects against XSS. But at most it corrects the protection level back to the level offered out of the box by browsers Internet Explorer (IE) and Google Chrome. Both browsers enable the user to deactivate their protection against XSS. The header fixes this setting for a websites. The recommended value is:

X-XSS-Protection: 1; mode=block

; mode=block keeps the browser from getting the idea to correct things and display them. If ; mode=block isn’t set, the XSS filter opens a new attack vector. All headers that are set as protection by Meta tag such as the following Content-Security-Policy header

<meta http-equiv="Content-Security-Policy" content="super-secure-csp" />

can be circumvented. This is, once more, due to Internet Explorer and his successor, Microsoft Edge. As opposed to other browsers such as Firefox and Chrome, Internet Explorer reads the entire page in order to identify Meta tags. Due to its XSS filter, it recognizes parameters such as

?a=<meta http-equiv="

that are being sent along, as attacks, because it’s being used in the body of the website. By default, IE simply replaces one of these characters and therefore circumvents this protective measure.

If ; mode=block is missing, not only protection measures can be circumvented. It could even introduce new vulnerabilities! Regarding this, the paper X-XSS-Nightmare by researcher Masato Kinugawa is worth a read.


The X-Frame-Options header is known due to it being created in order to defend against Click-Jacking attacks. But the header also “protects against the docmode problem as as a number of special XSS such as the Copy&Paste XSS, Invisible Site-Wide XSS and even against a subversion of the Content-Security-Policy. Security Engineer Frederik Braun and security researcher Mario Heiderich have written a comprehensive paper that I highly recommend. It describes this and more vulnerabilities that are protected by the X-Frame-Options header.

The Docmode issue is quite the exception and deserves its own paragraph. Internet Explorer tries to support old and broken websites come what may. Some of these websites run only in old versions of the browser. In order to display those sites correctly, Internet Explorer 11 is being instructed to behave as if it was Internet Explorer 8. This can be enforced using the following header:

X-UA-Compatible: IE=8

But this leads to the browser being vulnerable to old vulnerabilities. Because the Docmode is inherited by the parent frame, Internet Explorer can be instructed to display a current site using an old Docmode. An example of a framed site with old, vulnerable Docmode:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">
<html xmlns="">
    <title>Test Layout</title>
	<meta http-equiv="X-UA-Compatible" content="IE=8" />
    <style type="text/css">
      body, html
          margin: 0; padding: 0; height: 100%; overflow: hidden;
          position:absolute; left: 0; right: 0; bottom: 0; top: 0px;
    <div id="content">
      <iframe width="100%" height="100%" frameborder="0" src="http://domain-without-xfo-header.tld" />

Therefore, the X-Frame-Options header is one of the most important headers at the moment. For those asking if this problem persists in Microsoft Edge, the answer is a definite Yesnomaybe. On one hand, Edge doesn’t support old Docmodes anymore. But it wouldn’t be Microsoft if the solution didn’t come with some kind of catch. Because on the other hand, Edge suggests to users to open pages using old Docmode headers in Internet Explorer.

Framing should be forbidden using

X-Frame-Options: DENY

or at the very most allow itself using the following header:

X-Frame-Options: SAMEORIGIN

This header is being succeeded by the CSP policy frame-ancestors. Until this has happened, though, it is suggested to set both.


It is complicated, but very powerful, to set the Content-Security-Policy (CSP) Header. In this header, a guideline can be defined as to which sources are allowed to be used. This means that when embedding scripts, only the scripts on your own server or from external sources or only specific external hosts are being embedded. Currently, work is being done on the Content Security Policy 2.0 W3C Candidate Recommendation.

Experimental implementations use the names X-WebKit-CSP and X-Content-Security-Policy. Microsoft Internet Explorer has not quite made the jump and still uses X-Content-Security-Policy, as opposed to all other browsers. Luckily, Microsoft has seen the way in their new browser Edge and realized that standards are standards for a reason and support the usual CSP header. According to Can I use, the CSP header is supported by 80 per cent of all browsers in the world.

All possible options:

There are two options that should be looked at from the get go: default-src and report-uri. Basically, default-src should be as restrictive as possible.

Keywords for this are

It is self-explanatory that unsafe-inline and unsafe-eval are insecure and therefore should not be used.

report-uri indicates where transgressions of the guidelines are being sent in order for evaluation of their breaking points. In order to not have unavailable services during potential adjustments of unsafe web elements, it is recommended to start with Content-Security-Policy-Report-Only.

Content-Security-Policy-Report-Only: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; frame-ancestors 'none'

If you don’t want to use your own report-uri in the beginning, there’s the one offered by REPORT URI.

With a solid starting point the guidelines can either be loosened by necessity or the website can be corrected. Twitter, as one of the early adopters of CSP on their website, have published a report.

CSP Bypasses and Header Injections have become increasingly rare over the course of the past six years so that the effort of the configuring the CSP pays off. It is recommended to have a look at the Content Security Policy Quick Reference Guide. It contains further information regarding the header and of its configuration. It also pays off to read the CSP Cheat Sheet by security consultant Scott Helme.

Current Situation

The situation on the Internet (data collected using Shodan) looks as follows when looking at security headers:

All values are given in percent. They are drawn from the complete traffic in the entire row. The first cell states that 0.95% of all websites in the world have set the Strict-Transport-Security header on Port 443.

Land und PortStrict-Transport-SecurityXFO DenyXFO SameoriginPublic-Key-PinningContent-TypeX-Xss-ProtectionX-Content-Type-OptionsContent-Security-Policy
80/tcp 0.17691.7509 25.15150.72830.40580.0233
80/tcp 0.16541.1788 36.83490.51550.63620.1272
80/tcp 0.11572.2089 32.30210.81620.50670.0553

There are two conclusions that can be reached from this.

  1. Hardly anyone seems to bother with security headers, despite the fact that some of them are really easy to set up. The positive exception here is Content-Type with charset. It is being used on more than a quarter of all websites.
  2. Switzerland and Germany don’t look too shabby when compared to the rest of the world.

Home Sweet Home

To check your own headers is even easier than to set them. A website that automatically tests them for you is, for example, The site is a good indicator but note that the site can’t deliver a complete analysis of the CSP headers. What happens when the CSP configuration is not done correctly can be found in @filedescriptor’s blog post.

There are more relevant security headers. More about this can be found in the article titled Big Brother or: How I Learned to Stop Worrying and Love Encryption.

About the Author

Veit Hailperin

Veit Hailperin has been working in information security since 2010. His research focuses on network and application layer security and the protection of privacy. He presents his findings at conferences.


Are you interested in a Penetration Test?

Our experts will get in contact with you!



Michael Schneider

Online Tracking

Online Tracking

Ralph Meier

Deepfake Audio Text to Speech

Deepfake Audio Text to Speech

Andrea Hauser

Qubes OS

Qubes OS

Ahmet Hrnjadovic

You want more?

Further articles available here

You need support in such a project?

Our experts will get in contact with you!

You want more?

Further articles available here