tools tips reconnaissance

SecurityTrails Blog · Feb 21 · SecurityTrails team

6 Tips to Harden Your HTTP Headers

Reading time: 9 minutes

Web-based attacks are one of the most common types of cybercrime, and in most cases, the attacked protocol is the HTTP, while the component that receives the attacks is the web server.

In the past we’ve shared practical tips for preventing SSH attacks, and on other occasions we’ve explored different types of DNS attacks and how to mitigate them. Today we will once again jump right into a blue team article, and show you how to harden your HTTP headers.

As we’ve seen before, attackers will try to find as much information as possible about your online infrastructure, including your web server, during the information gathering OSINT process. Therefore, hardening your HTTP headers becomes essential for reducing your attack surface.

What are HTTP headers?

As the word implies, HTTP headers are pieces of information that can be found when you interact with an HTTP server. These ‘headers’ are among the most important parts of the HTTP request (made by HTTP clients, such as your web browser) and HTTP responses (made by HTTP servers such as Nginx, Apache, Caddy, etc.).

Within these HTTP headers is valuable information that helps to identify how the request was processed by the web server, the type of HTTP status, and other data such as web server name, version, cookie information, cache configuration, and much more, as you see in the following example:

[research@securitytrails.com ~]\$ curl -I wikipedia.org
HTTP/1.1 301 TLS Redirect
Date: Fri, 31 Jan 2020 19:49:33 GMT
Server: Varnish
X-Varnish: 523402783
X-Cache: cp1089 int
X-Cache-Status: int-front
Server-Timing: cache;desc="int-front"
Set-Cookie: WMF-Last-Access=31-Jan-2020;Path=/;HttpOnly;secure;Expires=Tue, 03 Mar 2020 12:00:00 GMT
Set-Cookie: WMF-Last-Access-Global=31-Jan-2020;Path=/;Domain=.wikipedia.org;HttpOnly;secure;Expires=Tue, 03 Mar 2020 12:00:00 GMT
X-Client-IP: 2800:a4:2499:e700:b3fc:a0c5:f8c9:8923
Location: https://wikipedia.org/
Content-Length: 0
Connection: keep-alive

While we can identify many different HTTP headers, there are really only two kinds of headers in the infosec world: those that are vulnerable, and those that are secure.

Vulnerable HTTP headers vs. hardened HTTP Headers

Hardening HTTP headers involves both real mitigation against attacks as well as some sort of security by obscurity configurations. The truth, however, is that the less information you expose on the Internet, the less data a penetration tester or real malicious attacker will find on your attack surface.

Let’s look at two examples, a vulnerable HTTP header vs. a hardened HTTP header, one that has been tweaked to mitigate more attacks than a standard out-of-the-box web server.

Vulnerable HTTP header example

[research@securitytrails.com ~]\$ curl -I ecosvit.org
HTTP/1.1 200 OK
Date: Sat, 01 Feb 2020 11:57:10 GMT
Server: Apache/1.3.33 (Unix) mod_fastcgi/2.4.0 FrontPage/5.0.2.2623 PHP/4.3.10 mod_gzip/1.3.19.1a mod_ssl/2.8.22 OpenSSL/0.9.7b rus/PL30.20
X-Powered-By: PHP/4.3.10
Connection: close
Content-Type: text/html; charset=windows-1251
Vary: accept-charset, user-agent

From this simple curl command, we can see that:

  • The site is using only HTTP unencrypted traffic.
  • The ‘Server’ and ‘X-Powered-By’ headers are showing critical software banners, including names and versions of a lot of server-based technologies running on the backend.
  • The HTTP headers’ lack of hardened tweaks, such as: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy and Feature-Policy.

This server is running old, vulnerable software and unencrypted traffic, and lacks proper HTTP header security.

Hardened HTTP header example

While it’s not yet 100% hardened, LinkedIn has done a good job of securing their HTTP headers for their main domain name.

The next example includes an HTTP header hardened by using Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options and Content-Security-Policy, as well configurations to hide server software name and versions.

[reseach@securitytrails ~]\$ curl -I www.linkedin.com
HTTP/1.1 200 OK
Server: Play
Content-Type: text/html; charset=utf-8
Content-Length: 85198
Date: Sat, 01 Feb 2020 12:05:40 GMT
X-FS-TXN-ID: 2af2d2d25bf0
X-FS-UUID: d81793e0cc45ef15305fd3e50c2b0000
Expect-CT: max-age=86400, report-uri="https://www.linkedin.com/platform-telemetry/ct"
Strict-Transport-Security: max-age=2592000
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: sameorigin
Content-Security-Policy: default-src _; connect-src 'self' https://media-src.linkedin.com/media/ www.linkedin.com s.c.lnkd.licdn.com m.c.lnkd.licdn.com s.c.exp1.licdn.com s.c.exp2.licdn.com m.c.exp1.licdn.com m.c.exp2.licdn.com wss://_.linkedin.com dms.licdn.com https://dpm.demdex.net/id https://lnkd.demdex.net/event blob: static.licdn.com static-exp1.licdn.com static-exp2.licdn.com static-exp3.licdn.com media.licdn.com media-exp1.licdn.com media-exp2.licdn.com media-exp3.licdn.com; img-src data: blob: _; font-src data: _; style-src 'unsafe-inline' 'self' static-src.linkedin.com _.licdn.com; script-src 'report-sample' 'unsafe-inline' 'unsafe-eval' 'self' spdy.linkedin.com static-src.linkedin.com _.ads.linkedin.com _.licdn.com static.chartbeat.com www.google-analytics.com ssl.google-analytics.com bcvipva02.rightnowtech.com www.bizographics.com sjs.bizographics.com js.bizographics.com d.la4-c1-was.salesforceliveagent.com slideshare.www.linkedin.com https://snap.licdn.com/li.lms-analytics/insight.min.js platform.linkedin.com platform-akam.linkedin.com platform-ecst.linkedin.com platform-azur.linkedin.com; object-src 'none'; media-src blob: _; child-src blob: lnkd-communities: voyager: \*; frame-ancestors 'self'; report-uri https://www.linkedin.com/platform-telemetry/csp?f=l
X-Li-Fabric: prod-ltx1
Set-Cookie: JSESSIONID=ajax:7170467862736554594; Domain=.www.linkedin.com; Path=/; Secure; SameSite=None
Set-Cookie: lang=v=2&lang=en-us; Domain=linkedin.com; Path=/; Secure; SameSite=None
Set-Cookie: bcookie="v=2&8ba1dbff-cb14-4e9a-8389-ef6464c4355d"; domain=.linkedin.com; Path=/; Secure; Expires=Mon, 31-Jan-2022 23:43:12 GMT; SameSite=None
Set-Cookie: lissc=1; domain=.linkedin.com; Path=/; Secure; Expires=Sun, 31-Jan-2021 12:05:40 GMT; SameSite=None
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: no-cache, no-store
Connection: keep-alive
X-Li-Pop: prod-vmi1
X-LI-Proto: http/1.1
X-LI-UUID: 2BeT4MxF7xUwX9PlDCsAAA==
Set-Cookie: lidc="b=TGST03:g=1963:u=1:i=1580558740:t=1580645140:s=AQFbngs6ioqBE4hybQsgThVqn98nXlUt"; Expires=Sun, 02 Feb 2020 12:05:40 GMT; domain=.linkedin.com; Path=/

Now that you know how a vulnerable header and hardened header look, let’s go right to the fun, practical part.

After following any of these tips, remember to reload your HTTP server to apply the changes.

Hide your PHP information

A lot of web servers run PHP as their main application language server, and something commonly overlooked by many system administrators is the precaution of hiding the PHP version from the HTTP headers.

The PHP version can be disabled by altering your PHP main configuration, usually by modifying the expose_php variable from your php.ini file.

Where the php.ini is, depends on what stack you’re using; whether you’re using PHP as a module using mod_php on Apache, or if you’re using PHP as a standalone daemon with PHP-FPM and Nginx for example.

Nginx and Apache users

Edit your php.ini file, usually located at /etc/php.ini

Then search for the following directive:

expose_php = On

And change it to be:

expose_php = Off

Reload your PHP-FPM daemon, or Apache in order to apply the changes.

Hide your web server version

Nginx users

The server_tokens variable allows you to reduce the information sent to the HTTP headers while sending a response to the web client. This allows you to hide the Nginx version, however, it does still show the ‘Nginx’ name.

To apply this tweak, you merely need to alter the ‘server_tokens’ variable, often located in the main nginx.conf configuration file, probably within an http or server block.

Apache users

Locate your main httpd.conf file (usually located at /etc/httpd/conf/httpd.conf), edit it and change the value of the ServerSignature and ServerTokens directives to ‘Off’ and ‘Prod’:

ServerSignature Off
ServerTokens Prod

This will reveal only your Apache server name while hiding the software version.

Enable CSP

CSP stands for Content Security Policy, a security standard that helps web server administrators prevent and mitigate certain types of network threats, such as XSS (cross-site scripting) attacks.

CSP defines which resources from your websites can be loaded by any remote web browser, including Javascript and CSS files.

Nowadays most modern browsers already support CSP, so it’s a pretty safe technique for securing part of your content.

There are many ways to configure CSP. For example:

Content-Security-Policy: default-src 'self' \*.securitytrails.com

This CSP configuration will let you load any resource from the apex domain securitytrails.com, as well as any of its subdomains.

On the other hand, the following configuration will allow you to load resources from your own website, as well as from the google-analytics.com domain name:

Content-Security-Policy: script-src 'self' https://www.google-analytics.com

And, as per the following example, you can force visitors to load resources only from the securitytrails.com domain name over the 443 port:

Content-Security-Policy: default-src https://securityrails.com:443

Refer to the official documentation to find your ideal configuration. Once you find the right configuration for your needs, you can move forward by following these instructions:

Nginx users

Add the following directive inside your server configuration block:

add_header Content-Security-Policy-Report-Only: "default-src 'none'; script-src http://yoursite.com";

Apache users

Locate your vhost configuration block or file, and insert the desired config line, for example:

Header set Content-Security-Policy-Report-Only "default-src 'none'; script-src [http://yoursite.com][8];"

Enable HSTS

HSTS stands for HTTP Strict Transport Security, a security mechanism that obligates web browsers like Google Chrome and Mozilla Firefox to establish secure communications only by an encrypted protocol such as HTTPS.

By using this configuration, you can reduce the chances of falling victim to a man-in-the-middle attack.

Nginx users

Just add the following line to your vhost block:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
The full configuration should look like this:
server {
listen 443 ssl
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

Apache users

In your httpd.conf file, make sure you are loading mod_headers.so first. If this line is not in there, just add it:

LoadModule headers_module modules/mod_headers.so

After that, add this configuration to your vhost configuration block:

Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains;"

It should look like this:

<VirtualHost 11.22.33.44:443>
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains;"
</VirtualHost>

In this example, max-age sets the duration (in seconds) for which the web server should deliver the encrypted HTTPS protocol, and the includeSubDomains directive will make sure to include all existing subdomains as well.

X-Content-Type-Options

The X-Content-Type-Options header is used to prevent some types of MIME-type sniffing techniques.

By applying the ‘nosniff’ variable, you can ensure the browser will render the data as original, and not as any other.

For example, the web server specifies that the content is text/plain. By using this protection, the browser will render the content only as that MIME type, preventing any sniffing attempts.

Apache users

Add the following parameter to your vhost configuration:

Header always set X-Content-Type-Options "nosniff"

Nginx users

Add the following line to your server configuration block:

add_header X-Content-Type-Options "nosniff" always;

X-Frame-Options

In the 90’s and 2000’s it was pretty common to build websites using iframes, a quick way to include headers, footers, and even remote content from other websites.

Today it’s not as common a practice, but some web applications still use it. Unfortunately, a lot of malicious actors use iframes to perform clickjacking attacks and steal sensitive data from users.

To prevent this type of cybercrime, you can use the X-Frame-Options header to allow or deny iframes.

Nginx users

Use the following configuration inside the vhost block:

add_header X-Frame-Options "SAMEORIGIN" always;

Here we used ‘SAMEORIGIN’ so your site is the only one allowed to use iframes, however, you can also simply set it to ‘DENY’—to deny all websites (including your own) from using iframes on your URL.

Apache users

Place this configuration inside your virtual host block:

Header always set X-Frame-Options "SAMEORIGIN"

Summary

As you can see, HTTP headers can be a great way to grab OSINT data about any web server in the world. While it can be a weak point, it’s also really easy to harden these headers by tweaking the web server configuration.

But you know what? It doesn’t end with the headers. Once you’ve secured them, there’s a lot more to discover, this time on the rest of your online infrastructure—open ports, SSL certificates, DNS records, subdomain mapping, associated domains, it’s a long list.

Discover other weak points on your infrastructure surface: try SurfaceBrowser™, our OSINT enterprise-grade exploration tool that will empower you to find known and unknown data in all your online assets, in mere seconds. Book a demo with our sales team today!