Django+React无认证API防滥用问题技术咨询
解决React+Django无认证API的恶意请求问题
针对无认证但需要限制仅浏览器访问的API,不用JWT也有很多实用的防御手段,以下是按优先级排序的方案:
1. 速率限制(核心防御)
这是最有效的基础手段,直接限制单IP的请求频率,挡住批量恶意调用。用django-ratelimit实现非常简单:
from django.http import HttpResponseTooManyRequests from ratelimit.decorators import ratelimit @ratelimit(key='ip', rate='15/m', method='GET') # 每分钟最多15次请求 def public_home_api(request): if getattr(request, 'limited', False): return HttpResponseTooManyRequests("请求过于频繁,请稍后再试") # 你的API逻辑 return JsonResponse({"data": "首页内容"})
- 可以根据API重要性调整速率,比如首页API放宽到20次/分钟,其他公共API设为10次/分钟;
- 进阶玩法:结合用户代理+IP的组合键,减少同一IP下多浏览器的误限制,但用户代理可伪造,仅作辅助。
2. 域名来源验证(低成本辅助)
通过检查请求头的Origin或Referer,只允许来自你的前端域名的请求,挡住直接通过Postman等工具发起的无来源请求。写一个Django中间件即可:
from django.http import HttpResponseForbidden class AllowedOriginMiddleware: def __init__(self, get_response): self.get_response = get_response self.allowed_origins = ["https://your-react-domain.com"] # 替换为你的前端域名 def __call__(self, request): # 放行OPTIONS预检请求 if request.method == "OPTIONS": return self.get_response(request) origin = request.headers.get("Origin") referer = request.headers.get("Referer") # 验证Origin或Referer是否在允许列表内 if origin and origin in self.allowed_origins: return self.get_response(request) if referer and any(referer.startswith(o) for o in self.allowed_origins): return self.get_response(request) return HttpResponseForbidden("非法请求来源")
- 注意:这些请求头可以被伪造,无法挡住专业攻击者,但能过滤大部分脚本批量调用。
3. 无感知会话验证(提升防御等级)
当用户首次访问前端页面时,后端给浏览器设置一个随机会话Cookie,后续API请求自动携带该Cookie,后端验证Cookie的有效性:
import secrets from django.core.cache import cache from django.http import HttpResponseForbidden, JsonResponse from django.shortcuts import render # 前端首页视图:设置会话Cookie def index(request): if not request.COOKIES.get("public_session"): session_id = secrets.token_hex(16) response = render(request, "index.html") # 设置httponly Cookie,防止前端读取,仅浏览器自动携带 response.set_cookie( "public_session", session_id, max_age=3600, # 1小时有效期 httponly=True, secure=True # 生产环境开启,仅HTTPS传输 ) # 把会话ID和用户IP绑定存储到缓存(用Redis更佳) cache.set(f"pub_session_{session_id}", request.META.get("REMOTE_ADDR"), timeout=3600) return response return render(request, "index.html") # 公共API视图:验证会话Cookie def public_api(request): session_id = request.COOKIES.get("public_session") if not session_id: return HttpResponseForbidden("请通过浏览器访问") stored_ip = cache.get(f"pub_session_{session_id}") if stored_ip != request.META.get("REMOTE_ADDR"): return HttpResponseForbidden("会话验证失败") return JsonResponse({"data": "受保护的公共内容"})
- 用户完全无感知,Cookie无法被前端JS读取,恶意调用需要模拟完整的浏览器会话流程,大幅提升攻击成本。
4. 隐藏API端点(辅助手段)
避免使用直白的API路径,比如不用/api/public/home,改用/api/v3/7a2d/feed这种带随机前缀的路径;同时前端把端点地址存在环境变量中,编译后不会直接暴露在源码里。虽然开发者工具仍能看到,但能挡住批量扫描API的脚本。
5. 验证码(高风险场景用)
如果公共API涉及敏感数据或高成本计算,可以在请求时要求验证码,比如首次请求返回验证码图片,前端渲染后用户输入,后端验证通过再返回数据。用django-simple-captcha可以快速实现,但会影响用户体验,仅适合必要场景。
内容的提问来源于stack exchange,提问作者Saikat Islam
相关产品推荐
相关产品推荐

