What is SSRF?
Server Side Request Forgery (SSRF) is a vulnerability where an attacker can make your server send HTTP requests to unintended destinations. In NGINX, this typically happens when user-controlled input is used in proxy_pass directives.
⚠️ Why SSRF is Critical
SSRF can allow attackers to:
- Access internal services (databases, admin panels, cloud metadata)
- Bypass firewalls and network segmentation
- Steal cloud credentials (AWS, GCP, Azure metadata endpoints)
- Scan internal networks
How SSRF Happens in NGINX
SSRF occurs when variables that can be controlled by attackers are used to determine where NGINX proxies requests. The most common vulnerable pattern:
location /proxy/ {
# $host is taken from the HTTP Host header - attacker controlled!
proxy_pass http://$host/;
}
An attacker can exploit this by sending:
GET /proxy/admin HTTP/1.1
Host: internal-admin-panel.local
NGINX will then proxy the request to http://internal-admin-panel.local/admin, potentially exposing internal services.
Dangerous Variables
These NGINX variables can be controlled by attackers and should never be used in proxy_pass:
$host— From the Host header or server_name$http_host— Directly from the Host header$request_uri— The full original URI including query string$uri— The normalized URI (can be manipulated)$arg_*— Query string parameters$http_*— Any HTTP header- Capture groups from regex locations without validation
Vulnerable Patterns
Pattern 1: Dynamic Host
# Attacker controls the backend via Host header
location /api/ {
proxy_pass http://$host$request_uri;
}
Pattern 2: Query Parameter Backend
# Attacker controls backend via ?target= parameter
location /fetch {
proxy_pass http://$arg_target;
}
Pattern 3: Path-Based Backend
# Attacker can inject @internal into the path
location ~ ^/service/(?[^/]+)/ {
proxy_pass http://$backend:8080;
}
Pattern 4: Header-Based Routing
# Attacker sets X-Backend-Server header
location /internal/ {
proxy_pass http://$http_x_backend_server;
}
Secure Patterns
Use Hardcoded Backends
# Backend is fixed, not user-controlled
location /api/ {
proxy_pass http://backend-server:8080;
}
Use Upstream Groups
upstream api_backends {
server backend1.internal:8080;
server backend2.internal:8080;
}
location /api/ {
proxy_pass http://api_backends;
}
Whitelist with Map
# Only allow known backends
map $arg_service $backend {
default "";
"users" "users-service:8080";
"orders" "orders-service:8080";
"products" "products-service:8080";
}
location /api/ {
if ($backend = "") {
return 400;
}
proxy_pass http://$backend;
}
Validate Regex Captures
# Validate capture groups against allowed values
location ~ ^/v1/(?v[12])/ {
# $version can only be "v1" or "v2" - safe
proxy_pass http://api-$version.internal;
}
Detecting SSRF with Gixy
Gixy automatically detects SSRF vulnerabilities in your NGINX configuration:
$ gixy /etc/nginx/nginx.conf
==================== Results ====================
⚠ [ssrf] Server Side Request Forgery
Severity: HIGH
Description: Using variables that can contain "\n" or be
controlled by an attacker in proxy_pass may
lead to SSRF vulnerabilities.
Reason: At least variable "$host" can be controlled by
an attacker via the Host header.
Pseudo config:
server {
location /api/ {
proxy_pass http://$host$request_uri;
}
}
File: /etc/nginx/conf.d/api.conf
Line: 15
==================== Summary ====================
Total issues: 1 (High: 1)
gixy in your CI/CD pipeline to catch SSRF vulnerabilities before they reach production. Use gixy -lll to fail builds on HIGH severity issues only.
Cloud Metadata Attacks
SSRF is especially dangerous in cloud environments. Attackers can target metadata endpoints to steal credentials:
- AWS:
http://169.254.169.254/latest/meta-data/ - GCP:
http://metadata.google.internal/computeMetadata/v1/ - Azure:
http://169.254.169.254/metadata/instance
Even if your NGINX config seems safe, always block these IPs as defense in depth:
# Block cloud metadata endpoints
location / {
# Deny requests trying to access metadata
if ($http_host ~* "169\.254\.169\.254|metadata\.google\.internal") {
return 403;
}
# Your normal proxy config
proxy_pass http://backend;
}
Checklist
- ✓ Never use
$host,$http_host, or$http_*inproxy_pass - ✓ Use hardcoded backends or upstream groups
- ✓ Whitelist allowed values with
mapdirectives - ✓ Validate regex capture groups before use
- ✓ Block cloud metadata IP ranges
- ✓ Run Gixy in CI/CD to catch issues automatically