如何模拟并阻断HTTP_HOST伪造攻击?Nginx反向代理问题排查
问题解决:伪造Host请求绕过Nginx到后端Django的处理方案
一、正确的攻击模拟方法
根据你的场景,请求能绕过Nginx直接到达Django,大概率是两种情况,对应模拟方式如下:
1. 直接访问后端Django服务端口(未被防火墙限制)
如果你的Django服务监听的端口(如8000)对外网开放,攻击者可直接绕过Nginx连接该端口,伪造Host头触发报错:
curl -H "Host: fake-domain.example" http://你的服务器IP:8000
这种情况下,请求完全不经过Nginx,自然不会被默认server块拦截。
2. Nginx未覆盖HTTP/HTTPS全场景的默认规则
如果你的默认server块只配置了HTTPS,而HTTP协议的默认server未做处理,攻击者可通过HTTP请求伪造Host,可能被Nginx转发到后端:
curl -H "Host: fake-domain.example" http://你的服务器IP
或者针对HTTPS场景,若默认server块未正确配置SSL证书(比如缺少ssl_certificate/ssl_certificate_key),部分客户端可能绕过SNI验证发起请求:
curl --insecure -H "Host: fake-domain.example" https://你的服务器IP
二、Nginx防护修复方案
1. 防火墙隔离后端服务
首先确保Django服务仅允许Nginx本地访问,禁止外网直接连接:
# 允许本地IP访问Django端口(假设为8000) ufw allow from 127.0.0.1 to any port 8000 # 拒绝其他来源访问该端口 ufw deny 8000 # 重启防火墙生效 ufw reload
2. 完善Nginx默认Server块(覆盖HTTP+HTTPS)
HTTP默认Server块(拦截所有非法HTTP请求)
server { listen 80 default_server; listen [::]:80 default_server; server_name _; return 444; # 直接关闭连接,无响应 }
HTTPS默认Server块(拦截所有非法HTTPS请求)
注意:需要配置可用的SSL证书(可使用Certbot生成的默认证书,或自签名证书)
server { listen 443 ssl default_server; listen [::]:443 ssl default_server; server_name _; # 使用Certbot生成的默认证书路径(Ubuntu 20.04下) ssl_certificate /etc/letsencrypt/live/你的主域名/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/你的主域名/privkey.pem; # 直接关闭非法请求连接 return 444; }
3. 强制验证合法Host后再转发后端
在每个对应合法域名的Server块中,添加Host验证规则,仅允许指定域名的请求转发到Django:
server { listen 443 ssl; listen [::]:443 ssl; server_name your-valid-domain1.com; # Certbot生成的SSL配置 ssl_certificate /etc/letsencrypt/live/your-valid-domain1.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-valid-domain1.com/privkey.pem; # 验证Host合法性,非法则直接拒绝 if ($host !~* ^your-valid-domain1.com$) { return 444; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 添加X-Forwarded-Proto头,让Django识别HTTPS请求 proxy_set_header X-Forwarded-Proto $scheme; } }
也可通过map指令统一管理所有合法Host,简化配置:
http { map $host $allowed_host { default 0; your-valid-domain1.com 1; your-valid-domain2.com 1; } # 然后在每个Server块中添加: if ($allowed_host = 0) { return 444; } }
4. 确认请求强制走HTTPS
在HTTP Server块中,将所有请求重定向到HTTPS(仅针对合法域名,非法请求直接返回444):
server { listen 80; listen [::]:80; # 合法域名重定向到HTTPS server_name your-valid-domain1.com your-valid-domain2.com; return 301 https://$host$request_uri; # 非法域名直接拒绝 server_name _; return 444; }
同时在Django的settings.py中配置,确保识别Nginx转发的HTTPS请求:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True
5. Django层面二次加固
虽然Nginx应拦截所有非法请求,但仍需确保Django的ALLOWED_HOSTS严格配置:
ALLOWED_HOSTS = ['your-valid-domain1.com', 'your-valid-domain2.com']
避免使用通配符,同时确保DEBUG = False(生产环境必须关闭)。
内容的提问来源于stack exchange,提问作者Kaustubh Bhalerao
相关产品推荐
相关产品推荐

