You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 01:10:14