Django 中的 XFF 问题
字数 746 2025-08-07 08:22:25
Django 中的 XFF 问题分析与防护指南
1. XFF 问题概述
X-Forwarded-For (XFF) 是 HTTP 请求头中的一个字段,用于标识通过代理或负载均衡器连接的客户端的原始 IP 地址。Django 在处理 XFF 头时存在潜在的安全风险,攻击者可能通过伪造 XFF 头绕过 IP 限制。
2. Django 默认行为分析
Django 默认情况下:
- 使用
REMOTE_ADDR获取客户端 IP - 不信任任何代理服务器
- 需要显式配置才能使用 XFF 头
3. 常见错误配置
3.1 直接信任 XFF 头
# 危险示例:直接取 XFF 的第一个IP
client_ip = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')[0] or request.META.get('REMOTE_ADDR')
3.2 未设置可信代理
# 危险示例:未设置 ALLOWED_HOSTS 或 USE_X_FORWARDED_HOST
ALLOWED_HOSTS = ['*']
4. 安全配置方案
4.1 使用 Django 内置设置
# settings.py
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
USE_X_FORWARDED_HOST = True
USE_X_FORWARDED_PORT = True
4.2 配置可信代理
# settings.py
# 明确指定可信代理IP
TRUSTED_PROXIES = ['192.168.1.1', '10.0.0.1']
4.3 自定义中间件处理
from django.utils.deprecation import MiddlewareMixin
class CustomProxyMiddleware(MiddlewareMixin):
def process_request(self, request):
xff = request.META.get('HTTP_X_FORWARDED_FOR', '')
if xff:
ips = [ip.strip() for ip in xff.split(',')]
# 取最后一个不可信代理后的IP
request.META['REMOTE_ADDR'] = ips[-1]
5. IP 限制实现方案
5.1 基于中间件的实现
class RestrictIPMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.allowed_ips = ['127.0.0.1', '192.168.0.0/16']
def __call__(self, request):
client_ip = self.get_client_ip(request)
if not self.is_allowed(client_ip):
from django.http import HttpResponseForbidden
return HttpResponseForbidden()
return self.get_response(request)
def get_client_ip(self, request):
xff = request.META.get('HTTP_X_FORWARDED_FOR')
if xff:
ips = [ip.strip() for ip in xff.split(',')]
# 根据可信代理数量获取真实IP
return ips[-len(TRUSTED_PROXIES)-1] if len(ips) > len(TRUSTED_PROXIES) else ips[0]
return request.META.get('REMOTE_ADDR')
def is_allowed(self, ip):
from ipaddress import ip_address, ip_network
try:
ip_addr = ip_address(ip)
for allowed_ip in self.allowed_ips:
if '/' in allowed_ip:
if ip_addr in ip_network(allowed_ip):
return True
else:
if ip_addr == ip_address(allowed_ip):
return True
except ValueError:
return False
return False
5.2 基于装饰器的实现
from functools import wraps
from django.http import HttpResponseForbidden
from ipaddress import ip_address, ip_network
def restrict_ip(allowed_ips=None):
if allowed_ips is None:
allowed_ips = ['127.0.0.1', '192.168.0.0/16']
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
client_ip = get_client_ip(request)
if not is_ip_allowed(client_ip, allowed_ips):
return HttpResponseForbidden()
return view_func(request, *args, **kwargs)
return _wrapped_view
return decorator
def get_client_ip(request):
xff = request.META.get('HTTP_X_FORWARDED_FOR')
if xff:
ips = [ip.strip() for ip in xff.split(',')]
return ips[-1] # 根据实际情况调整
return request.META.get('REMOTE_ADDR')
def is_ip_allowed(ip, allowed_ips):
try:
ip_addr = ip_address(ip)
for allowed_ip in allowed_ips:
if '/' in allowed_ip:
if ip_addr in ip_network(allowed_ip):
return True
else:
if ip_addr == ip_address(allowed_ip):
return True
except ValueError:
return False
return False
6. 测试验证方法
6.1 测试用例示例
from django.test import TestCase, RequestFactory
class IPRestrictionTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
def test_xff_spoofing(self):
request = self.factory.get('/admin/', HTTP_X_FORWARDED_FOR='1.1.1.1, 192.168.1.1')
middleware = RestrictIPMiddleware(lambda r: None)
response = middleware(request)
self.assertEqual(response.status_code, 403)
def test_local_access(self):
request = self.factory.get('/admin/', REMOTE_ADDR='127.0.0.1')
middleware = RestrictIPMiddleware(lambda r: None)
response = middleware(request)
self.assertIsNone(response)
6.2 使用 curl 测试
# 测试直接访问
curl -H "X-Forwarded-For: 1.1.1.1" http://localhost:8000/admin/
# 测试通过代理访问
curl -H "X-Forwarded-For: 192.168.1.1, 10.0.0.1" http://localhost:8000/admin/
7. 最佳实践总结
- 明确可信代理:在配置中明确列出所有可信代理的IP地址
- 多层验证:结合 ALLOWED_HOSTS 和 IP 限制
- 日志记录:记录所有被拒绝的访问尝试
- 定期审计:检查配置和中间件逻辑
- 最小权限原则:只开放必要的IP范围
- 使用安全中间件:如 django-ipware 等经过验证的库
8. 推荐工具和库
-
django-ipware:专门处理IP地址检测的Django应用
from ipware import get_client_ip client_ip, is_routable = get_client_ip(request) -
django-xforwardedfor-middleware:专门处理XFF头的中间件
-
django-allowcidr:支持CIDR表示的IP范围限制
通过以上配置和实践,可以有效防范Django中的XFF安全问题,确保后台管理等功能只能通过指定的IP地址访问。