Gateway Hardening Middleware#
Protects the app at the edge of the request pipeline.
Responsibilities#
Host allow-list & HTTPS enforcement (308 redirects as configured).
Trusted proxy handling; strips hop-by-hop headers.
Guard against open redirects and invalid
X-Forwarded-*chains.
Placement#
Put it near the top of preGlobal (after validators if you want validators to short-circuit first).
preGlobal: [
\Infocyph\Webrick\Middleware\CacheValidatorsMiddleware::class,
\Infocyph\Webrick\Middleware\GatewayHardeningMiddleware::class,
// ...
]
Configuration#
use Infocyph\Webrick\Middleware\GatewayHardeningMiddleware;
$preGlobal[] = new GatewayHardeningMiddleware(
trustedProxyCidrs: ['10.0.0.0/8', '172.16.0.0/12'], // Trusted proxy IPs
denyIpCidrs: ['192.168.1.100/32'], // Blocked IPs
trustedHosts: ['example.com', '*.example.com'], // Host whitelist
forwardedHeaderMask: null, // Symfony-style mask
enforceHttps: true, // Force HTTPS
httpsPort: 443, // HTTPS port
stripHopByHop: true, // Strip Connection, Keep-Alive, etc.
redirectAllowedHosts: [] // Open redirect guard
);
Constructor Parameters#
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
CIDR ranges of trusted proxies |
|
|
|
CIDR ranges to block |
|
|
|
Allowed Host header values |
|
|
|
Symfony ForwardedHeaderMask flags |
|
|
|
Redirect HTTP → HTTPS |
|
|
|
HTTPS port for redirects |
|
|
|
Remove hop-by-hop headers |
|
|
|
Allowed redirect destinations |
Features#
1. Host Validation#
Blocks requests with untrusted Host headers:
new GatewayHardeningMiddleware(
trustedHosts: ['example.com', 'api.example.com', '*.partner.com']
);
Supported patterns:
Exact:
example.comWildcard subdomain:
*.example.comCatch-all (dev only):
['*']
Why: Prevents Host header injection attacks.
2. IP Filtering#
Trusted Proxies:
trustedProxyCidrs: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
// Honor X-Forwarded-For from these IPs only
IP Deny List:
denyIpCidrs: ['203.0.113.0/24', '198.51.100.50/32']
// Block these IPs entirely
3. HTTPS Enforcement#
Redirects HTTP → HTTPS with 308 (permanent):
new GatewayHardeningMiddleware(
enforceHttps: true,
httpsPort: 443 // Omits :443 from Location header
);
Example:
Request: http://example.com/page
Response: 308 Permanent Redirect
Location: https://example.com/page
4. Hop-by-Hop Header Stripping#
Removes headers that shouldn’t pass through proxies:
Removed:
ConnectionKeep-AliveProxy-AuthenticateProxy-AuthorizationTETrailerTransfer-EncodingUpgrade
Safe for HTTP/2 (never emits Connection).
5. Open Redirect Protection#
Validates Location headers to prevent open redirects:
new GatewayHardeningMiddleware(
redirectAllowedHosts: [] // Empty = same-origin only
);
// Or explicit whitelist
new GatewayHardeningMiddleware(
redirectAllowedHosts: ['cdn.example.com', 'assets.example.com']
);
Blocks:
Schemes other than
http/https(preventsjavascript:,data:, etc.)External hosts not in whitelist
Request Attributes#
Middleware attaches useful attributes:
$r->getAttribute('client_ip'); // End-user IP (honors proxies)
$r->getAttribute('peer_ip'); // Direct socket peer
$r->getAttribute('is_trusted_proxy'); // bool
Examples#
Production (Behind AWS ALB)#
new GatewayHardeningMiddleware(
trustedProxyCidrs: ['10.0.0.0/8'], // VPC CIDR
trustedHosts: ['example.com', 'www.example.com'],
enforceHttps: true,
stripHopByHop: true
);
Development (Local)#
new GatewayHardeningMiddleware(
trustedHosts: ['localhost', '*.localhost', '127.0.0.1'],
enforceHttps: false // Allow HTTP in dev
);
CDN + Origin#
new GatewayHardeningMiddleware(
trustedProxyCidrs: [
'103.21.244.0/22', // Cloudflare IPs
'103.22.200.0/22',
// ... (all Cloudflare ranges)
],
trustedHosts: ['example.com'],
enforceHttps: true
);
Troubleshooting#
Issue: Wrong client IP detected#
Cause: Proxy not trusted, or X-Forwarded-For not honored.
Fix: Add proxy to trusted list:
trustedProxyCidrs: ['10.0.0.0/8']
Issue: HTTPS redirect loop#
Cause: Proxy terminates TLS but doesn’t set X-Forwarded-Proto.
Fix: Configure proxy:
# Nginx
proxy_set_header X-Forwarded-Proto $scheme;
Or disable enforcement:
enforceHttps: false
Issue: Host header rejected#
Cause: Valid host not in trustedHosts.
Fix: Add host:
trustedHosts: ['example.com', 'api.example.com']
Best Practices#
✅ Do#
Whitelist exact hosts
trustedHosts: ['example.com', 'api.example.com']
Trust only known proxies
trustedProxyCidrs: ['10.0.0.0/8'] // Your VPC
Enforce HTTPS in production
enforceHttps: $_ENV['APP_ENV'] === 'production'
Validate redirects
redirectAllowedHosts: [] // Same-origin only
❌ Don’t#
Don’t trust all proxies
trustedProxyCidrs: ['0.0.0.0/0'] // ❌ DANGEROUS
Don’t use catch-all hosts in production
trustedHosts: ['*'] // ❌ Defeats the purpose
Don’t skip IP validation
// ❌ Trusting X-Forwarded-For without validation $ip = $request->getHeaderLine('X-Forwarded-For');