kevinhakanson.com

Investigating Web Cache Poisoning

August 22, 2018 #http #caching #security

Practical Web Cache Poisoning had me wondering about Host header validation in one application I was consulting on. I decided to test against an internal endpoint that reflects back HTTP headers. I used curl’s -H to lie about my Host and X-Forwarded-* headers and routed through an internal proxy server.

Note: For these examples I replaced the real hostname with the reserved example.com from Special-Use Domain Names.

$ curl  -H "Host: poison0.example.com" -H "X-Forwarded-Host: poison1.example.com" \
-H "X-Forwarded-For: 1.1.1.1" -H "X-Forwarded-Proto: ftp" -H "X-Forwarded-Port: 8080" \
--proxy 10.20.30.40:5678 https://ci.int.example.com/api/internalsecurity/v1/requests
{
    "headers": {
        "x-amzn-trace-id": "Root=1-5b7db1ca-321c6f7616980480e520feb6",
        "x-forwarded-host": "poison0.example.com",
        "x-forwarded-proto": "https",
        "host": "poison0.example.com",
        "x-forwarded-port": "443",
        "connection": "close",
        "x-forwarded-for": "1.1.1.1, 10.20.30.40",
        "user-agent": "curl/7.58.0",
        "accept": "*/*"
    },
    "URI": "http://poison0.example.com/api/internalsecurity/v1/requests"
}

The AWS Application Load Balancers allow the Host header to pass through and correctly set the X-Forwarded-* headers on to the target. The value for X-Forwarded-For is a chain of values that was appended to

Recently Amazon added “Redirect Rules” into Application Load Balancers and can preform the www to naked domain logic by matching on Host and triggering a redirect. Additionally, you could require all load balancer targets to match a known hostname which has the effect of Host validation. The load balancer is already indirectly aware of hostnames by the fact it has a TLS Certificate configured containing numerous subjectAltName DNS entries.

However, is it “better” to validate the Host by leveraging the virtual host functionality of nginx.conf? This is already the place where the Host header value is copied to the X-Forwarded-Host header. However, it may require specific nginx.conf files for each environment and regional deploy.

server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}
server {
    server_name example.com;
    # [...]
}

Server names from the nginx docs documents the strange name _ which is “just one of a myriad of invalid domain names which never intersect with any real name.”

    # This is the default server that will 444 on requests that do not
    # have an acceptable host name as defined in external_domains.conf
    server {
        listen 8080;
        server_name _;
        return 444;
    }

    server {
        listen 8080;
        include ./external_domains.conf;
        # [...]
    }

In external_domains.conf, all the allowable server_names are listed.

server_name example.com
    example.net
    example.org

This configured worked in that it rejected “unwanted” Host headers.