fbpx

NGINX / Server Setup

Protecting from the Host header vulnerability injection in NGINX

by ,


We have by far the largest RPM repository with NGINX module packages and VMODs for Varnish. If you want to install NGINX, Varnish, and lots of useful performance/security software with smooth yum upgrades for production use, this is the repository for you.
Active subscription is required.

Protecting your web applications from the Host header vulnerability injection is crucial in maintaining the security and integrity of your web infrastructure. This type of vulnerability can be exploited by attackers in several ways, including password reset poisoning and web cache poisoning, potentially leading to a wide range of attacks.

Fortunately, when using NGINX, several configurations can be implemented to mitigate such risks.

Understanding cache poisoning via Host header injection

Cache poisoning through the Host header injection is a sophisticated attack where an attacker manipulates the cache of a web application by injecting malicious data into the cache through the Host header. This manipulated cache can then serve malicious content to legitimate users. Mitigating this attack in NGINX involves ensuring that only valid Host headers are used in responses that get cached, and any request with a suspicious or manipulated Host header does not poison the cache.

Example Usage of curl for Testing Host Header Injection

The following command can be used to send a request to a server with a custom Host header. This is a common technique used in security assessments to determine if a server might be improperly handling the Host header, potentially making it vulnerable to attacks like cache poisoning or redirection hijacks:

curl -v -H "Host: malicious.host" https://example.com/
  • -v enables verbose mode, which outputs detailed information about the request and response, aiding in the analysis.
  • -H "Host: malicious.host" sets a custom Host header for the request. In a security assessment, the malicious.host value would be replaced with a domain or value controlled by the researcher or a harmless value designated for testing purposes.
  • `https://example.com/` is the URL of the the website you own.

A vulnerable server will respond with HTTP/1.1 200 OK for such requests, meaning it accepts an arbitrary Host header value. This allows an attacker to send unlimited number of various Host: header values in their requests, ultimately increasing cache storage of the server to entirely fill the disk, or causing early purging of the cache, degrading performance.

The correct behavior would be to deny requests containing untrusted Host header value.

There are two different approaches to protect an NGINX server from the Host header injection vulnerabilities.

The approach with whitelisting known domains

In this approach, you can configure NGINX to mitigate cache poisoning attacks via Host header injections, by listing all the default domains on your server.

What are the default domains? Those are any of your own domains listed in your NGINX configuration alongside default_server flag. In other words, you only need to whitelist domains which are normally served when Host header is not provided at all. Or, in other words, when your website is being accessed directly by the IP address of the server. They are so-called “main” domains for your server.

It is also the best practice to set default_server in your NGINX configuration:

For example:

listen 80 default_server;
listen 443 ssl default_server;
...
server {
    server_name www.example.com example.com ;
    ...
}

Here, example.com is the primary website on your server. There may be more website on your server, but they can’t be designated as primary, and must not have the default_server flag set, as it’s unique per listening IP:port combination.

Step 1: Define Trusted Hosts

Start by defining the set of the main/trusted hosts for your application. In a real-world scenario, these would be the domains your application is intended to serve.

map $http_host $is_trusted {
    default         0;
    "www.example.com"  1;
    "example.com"      1;
}

This map block maps trusted Host headers to a variable $is_trusted, marking them with 1 for trusted and 0 for all others.

Step 2: Block or Redirect Untrusted Host Headers

Using the $is_trusted variable, we can configure NGINX to block or redirect requests with untrusted Host headers, preventing them from poisoning the cache.

server {
    listen 80;
    server_name www.example.com example.com;

    if ($is_trusted = 0) {
        return 444; # No response to untrusted hosts
    }

    # Your usual server configuration
}

Caveats

While this approach effectively protects your server from the Host header vulnerability, it requires explicitly whitelisting domains manually. However, if you manage your NGINX configuration via configuration tools like ansible, it becomes a non-issue.

The added benefit of this approach is that your server will work perfectly fine with web clients which do not support SNI. More on that later.

Approach with the catch-all default_server website

A default server block can catch all other requests that do not match server_name of any other website. It is the easiest way to protect from the Host header vulnerability injection in NGINX:

server {
    listen 80 default_server;
    return 444;
    server_name _;
}

Now any requests with an arbitrary (not listed elsewhere) Host: header will reach this denying server block. Make sure to remove default_server flag from other websites.

Caveats

The approach, while most elegant, does not account for web clients which do not support SNI. When such clients genuinely load your website over secure connection, e.g. by opening “https://example.com/”, NGINX does not see any Host header, and because of this, the request will end up in the catch-all server block. And due to this, non-SNI supporting clients will not be able to load any websites on your server.

Despite widespread support, there are still areas where SNI clients face limitations or special considerations:

  • Legacy Systems and IoT Devices: Some older systems or IoT (Internet of Things) devices may not support SNI due to outdated software or limited capabilities. This can affect their ability to connect to specific services hosted on shared servers requiring SNI for proper routing.
  • Privacy Concerns with SNI: Traditional SNI exposes the hostname in plaintext during the TLS handshake, which can be a privacy concern. Encrypted SNI (ESNI) or its successor, Encrypted Client Hello (ECH), address this by encrypting the portion of the handshake that contains the hostname. However, adoption rates and support for ESNI/ECH can vary, impacting privacy.
  • Networking and Security Appliances: Certain middleboxes (like firewalls and load balancers) and older security appliances may have incomplete or outdated support for SNI, affecting their ability to route or inspect HTTPS traffic correctly. This can require updates or configuration changes to ensure compatibility.

Recommendations

To avoid inadvertently blocking legitimate clients while still protecting against Host header injection attacks, consider using the first approach outlined in this article, while ensuring best practices:

  • Configure server blocks for all known, legitimate hostnames your server is intended to serve. This ensures that any SNI supporting clients can establish a connection to desired websites
  • Configure the primary website on your server, with default_server flag alongside its listen directive. This ensures that any client which does not support SNI can access the primary website
  • If support for outdated non-SNI clients is crucial and you want more than just primary website to work for those, you must use multiple IP addresses in your server and NGINX configuration

For these reasons, when configuring NGINX to mitigate vulnerabilities, including Host header injections, it’s crucial to consider the behavior of clients that do not support Server Name Indication (SNI).

Conclusion

While NGINX does not directly mitigate the Host header injections out of the box, through careful configuration and adherence to best practices, you can fully protect your applications from such attacks. It’s a critical part of maintaining the security posture of your web applications, ensuring they only respond to legitimate and recognized Host headers.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

This site uses Akismet to reduce spam. Learn how your comment data is processed.