SignalHunt
← All rules

RP-7005

critical

Cache Poisoning via Spoofed X-Forwarded-Host on a Cached Response

Detects a cache HIT/cacheable response where the X-Forwarded-Host header the client sent does not match the real Host header. If the backend trusts X-Forwarded-Host to build absolute URLs (password reset links, redirects, asset URLs) but the cache key doesn't include that header, an attacker can poison the shared cache with a response containing their own attacker-controlled links - served to every subsequent visitor who hits the same cache key.

Detection logic

Fires as soon as one event matches: `cache_status` is "HIT" AND `X-Forwarded-Host` is "Host".

{
  "detection_type": "selection",
  "selection": {
    "cache_status": "HIT",
    "X-Forwarded-Host|neq_field": "Host"
  }
}

Log source: category=reverse_proxy · Event ID(s): rp_access_log

Required log fields

  • client_ip
  • Host
  • X-Forwarded-Host
  • cache_status
  • uri

Enabling this in your environment

Where: Reverse proxy / CDN access log configuration - log the request's X-Forwarded-Host header value and the cache HIT/MISS/BYPASS status per request

nginx: log_format including $http_x_forwarded_host and $upstream_cache_status; a CDN's edge logging config for the equivalent forwarded-host and cache-status fields.

The real fix is including X-Forwarded-Host (or dropping/normalizing it entirely) in the cache key, not just detecting after a poisoned response is already cached and being served.

False positives

  • A legitimate internal service-to-service call that intentionally sets X-Forwarded-Host while proxying on behalf of another vhost - confirm the cache key strategy actually accounts for this before assuming poisoning

References