Blog
Informative, up-to-date and exciting - the Oneconsult Cybersecurity Blog.

Making Websites More Secure With the Right Security Headers

Security headers are an important component of the security of any modern web application. Although this measure is relatively easy to implement, the security headers of many applications are incomplete or incorrectly configured.

Security headers, or more precisely security response headers, consist of meta-information that a web server sends to a client in its responses. The server thus provides the browser with important information on how to handle the content of the response. The number of available security headers that a web server can insert into its responses has grown into a veritable jungle. The fact that some of these headers are already considered outdated, or in rare cases can even lead to security vulnerabilities, does not make it any easier to navigate this thicket. In this blog post, we clear up the confusion and provide an up-to-date recommendation on which headers should be used and how.

These are security measures that are primarily active in visitors’ browsers. This way, visitors can be protected from attacks such as cross-site scripting and clickjacking.

Content-Type and X-Content-Type-Options

The Content-Type Header tells the user’s browser which media type (MIME-Type) is contained in the response. This helps the browser to process the received data correctly. Normally, the vast majority of web servers and frameworks automatically set this header correctly. However, in self-built web applications, errors can occur. A normal HTLM website is delivered with the following header:

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

The browser will therefore attempt to render the received data as a website and will also execute any script code. However, if the Content-Type is set as “text/plain; charset=UTF-8”, the browser will display the same response as plain text, i.e., as HTML code. It is therefore important that the Content-Type header is set according to the purpose of the response. If this is not the case, data will be misinterpreted, which can enable attackers to execute arbitrary code in the context of a website.

In cases where the Content-Type header is incorrect or not set at all, modern browsers attempt to guess the correct MIME type based on the received response (MIME Sniffing). Not only is this process prone to errors, but there is also a risk that the used algorithm itself contains security vulnerabilities. This behavior can be controlled via the X-Content-Type-Options Header. It is good practice to ensure that the content of the Content-Type header is always correct and to disable MIME sniffing with the following header:

X-Content-Type-Options: nosniff

Referrer-Policy

The Referrer-Policy controls how much information a browser should write in the Referer header when it sends requests. The attentive reader will have noticed a spelling mistake at this point. This error originates from the original implementation and was also included in the standardization (RFC-1945). The Referer header contains the URL of the last request sent, which only plays a minor role as long as the browser always sends requests to the same page. However, if this page now forwards the browser to a third party, the URL in the Referer header may contain sensitive information that must not be disclosed to them. Furthermore, this also jeopardizes the user’s privacy, as the destination of the redirect is informed of which page a user is coming from. To avoid these scenarios, set the Referrer-Policy as follows:

Referrer-Policy: no-referrer

X-XSS-Protection

The X-XSS-Protection header controlled the built-in cross-site scripting filter in Internet Explorer. Older versions of Chrome, Edge and Safari briefly supported the header (versions from before 2020). However, all modern browsers ignore this header. To harden your site against cross-site scripting, use the Content-Security-Policy header.

X-Frame-Options

Websites can embed and display the content of other websites. The HTML tags “<frame>” and “<iframe>” are used for this purpose. Attackers can embed the content of a harmless page into their own malicious website and trick the user into interacting with elements of the supposedly harmless page. What the user does not know, however, is that an attacker has placed an additional, invisible layer of control elements over the harmless page. When the victim clicks on a control element, it triggers actions that were not intended. This type of attack is called clickjacking.

In an illustrative example, an attacker could prepare a page with a fictitious lottery and place a second, invisible button over the “Win” button (see figure 1). The victim wants to click on the “Win” button, but in fact clicks on the invisible one, which confirms a bank payment in the victim’s name.

Figure 1: Conceptual Representation of Clickjacking, Diagram of how clickjacking works by Lord Belbury, CC BY-SA 4.0

To prevent such attacks, set the X-Frame-Options Header on your application as follows:

X-Frame-Options: deny

This header is still supported by modern browsers but is considered to be outdated. You should therefore also set the “frame-ancestors” directive in the Content-Security-Policy.

Content-Security-Policy

The Content-Security-Policy (CSP) is a security measure that is supported by all modern browsers (since around 2016). It replaces outdated headers such as X-Frame-Options and X-XSS-Protection, but also offers a variety of additional security settings. Creating a CSP that allows an existing application to function correctly but still offers significant protection is not an easy task and beyond the scope of this article. There are several services and browser plug-ins available on the web that can help generate an initial CSP. In order to take advantage of the full potential of protection offered by a CSP, it is sometimes also necessary to make adjustments to the website that is to be protected. For this reason, it is only possible to make a limited recommendation at this point.

To protect your application from clickjacking, set at least the following header:

Conten-Security-Policy: frame-ancestors 'none';

Further directives can specify restrictions on where which type of content may be loaded from and which functions a page may execute. When creating a CSP, the sources “unsafe-inline” and “unsafe-eval” should be avoided, as these undermine the protection of the CSP.

Strict-Transport-Security

The Strict-Transport-Security header (HSTS) informs the user’s browser that the requested page is only allowed to be loaded via an encrypted connection. This helps to prevent attacks in which attackers interpose themselves between the victim and a requested website (attacker-in-the-middle). Another advantage is that all unencrypted requests to the application are converted directly into encrypted requests by the browser. This can prevent scenarios in which sensitive data is transmitted via an unencrypted connection. To activate HSTS, set the following header:

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

The “max-age” directive specifies how long the browser should remember that this website should only be accessed via an encrypted connection. The “includeSubDomains” directive tells the browser that these rules also apply to all sub-domains. This means that a browser that has already visited “example.com” will also only access “sub.example.com” via an encrypted connection. However, this does not mean that “sub.example.com” should not set the HSTS header, as there is no guarantee that a visitor has already visited the parent domain.

Data Minimization in Product Headers

There are other headers that do not directly control the security functions in the browser but can still influence the security of an application. Various frameworks and platforms set headers by default that provide the visitor with information about the software products used. The visitor’s browser does not process this information, but it can provide attackers with important clues about their target. Attackers can use this information, for example, to search online for known security vulnerabilities. The following common headers fall into this category:

  • Server
  •  X-Powered-by
  •  X-AspNet-Version

For an Appache server that delivers a PHP website, this could look something like this:

Server: Apache/2.4.18 (Ubuntu)
X-Powered-By: PHP-7.2.0+ubuntu16.04.1+deb.sury.org+2

Disabling these headers does not fix any of the actual security vulnerabilities, but an attacker must now identify the technologies used manually. It also prevents botnets and worms from automatically identifying the system as a target. Data minimization therefore pays off in this case. The way in which these headers can be deactivated depends heavily on the technologies and products used. In many cases, a simple directive in a configuration file is sufficient, but more effort is required in rare cases.

Where Do You Set Security Headers

When configuring security headers, it is best to do this in a central location in your application. If an application is protected by a reverse proxy or a web application firewall, this is an ideal place to set the desired security headers. This means you only have to manage the configuration in one place and there is no risk of an endpoint or service being forgotten or headers being set twice. If you do not use a reverse proxy or a WAF, it is best to set the security headers in the web server configuration. This ensures that the headers are always set, even if the same web server delivers several websites (virtual host). You should avoid setting security-relevant headers only for a specific part of a single application as much as possible. Headers such as X-Content-Options do not add any value to a REST API or static resources, but they do not have any negative effects either.

Another pitfall to avoid is the duplication of headers and directives. If a response contains several or even contradictory headers, as in the example below, it depends on the respective browser, which instructions are implemented:

X-Frame-Options: DENY SAMEORIGIN
Content-Type: text/html; charset=UTF-8
Content-Type: application/json

In the example above, the browser must decide whether it should implement the “DENY” or “SAMEORIGIN” directive of the X-Frame-Options header. It is also not clear whether the browser should interpret the response received as text data in JSON format or as an HTML page. When you make adjustments to the headers of your application, you should subsequently check whether the end result matches your intentions.

Conclusion

The correct configuration of modern security headers cannot eliminate existing vulnerabilities, but their exploitation can be prevented or at least made significantly more difficult. Security headers therefore form an important and relatively inexpensive safety net and are quick and easy to implement. To achieve solid basic protection for your application, the security headers of your application should look something like this:

Content-Type: text/html; charset=UTF-8
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
X-Frame-Options: deny
Conten-Security-Policy: frame-ancestors 'none';
Strict-Transport-Security: max-age=31536000; includeSubDomains

Please note that the Content-Type header is often automatically set correctly by the web server, depending on the content delivered. Various of the headers mentioned can be further adapted to your application, either to add exceptions or to define even stricter protection. Customizing the CSP to the individual application can be particularly worthwhile.

In the area of information security, it is worthwhile to have your applications regularly tested by a third party. Oneconsult has many years of experience in the field of web application security and offers various services in this area. This allows you to tailor your next planned audit to your application and your needs. The Oneconsult Penetration Testing Team will be happy to help you comprehensively test the security of your applications. In our Cyber Security Academy, we also train your next security specialist or teach the whole team the basics of secure web development.

We look forward to hearing from you:

Published on: 27.11.2023

Share

Never miss the latest news on cyber security topics again? Sign up for our newsletter

Author

Simon Farner

Simon Farner is a penetration tester at Oneconsult. He carries out projects in the field of web application security and vulnerability management and is a certified OSSTMM Professional Security Tester (OPST).

Don’t miss anything! Subscribe to our free newsletter.

Your security is our top priority – our specialists provide you with professional support.

Availability Monday to Friday 8:00 a.m. – 12:00 p.m. and 1:00 p.m. – 5:00 p.m (exception: customers with SLA – please call the 24/7 IRR emergency number).

Private individuals please contact your trusted IT service provider or the local police station.

For more information about our DFIR services here:

QR_CSIRT_2022_EN@2x
Add CSIRT to contacts