Django Rest Framework如何限制未认证用户非法访问公开API
DRF联系表单API安全修复方案
核心问题梳理
你当前的接口存在两个高危问题:
- 匿名用户可直接通过GET请求拉取所有用户提交的联系表单数据,属于严重的数据泄露
- 接口无来源校验,任何工具、站点都可以随意构造POST请求提交垃圾数据
- 仅靠频率限制无法拦截低频次的恶意请求,必须配合来源、合法性校验
修复步骤
1. 第一优先级:关闭公开的GET数据接口
匿名用户完全没有获取所有联系表单数据的需求,不要把数据查询和表单提交写在同一个接口里:
- 把提交接口的允许方法仅保留
POST - 如果确实需要后台查看提交数据,单独开一个仅管理员可访问的GET接口
修改后的视图代码:
from django.conf import settings from django.http import HttpResponseForbidden from django.views.decorators.csrf import csrf_protect from rest_framework import status from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAdminUser, AllowAny from .serializers import ContactSerializer from .models import Contact @api_view(['POST']) @permission_classes([AllowAny]) @csrf_protect def farhyn_api(request): # 先校验请求来源 origin = request.META.get('HTTP_ORIGIN', '') referer = request.META.get('HTTP_REFERER', '') allowed_domains = settings.ALLOWED_FRONTEND_ORIGINS source_valid = any(origin.startswith(domain) or referer.startswith(domain) for domain in allowed_domains) if not source_valid: return HttpResponseForbidden("非法请求来源") # 表单提交逻辑 serializer = ContactSerializer(data=request.data) if serializer.is_valid(): serializer.save() return Response( {'status': True, 'message': 'success'}, status=status.HTTP_201_CREATED ) return Response( {'status': False, 'message': serializer.errors}, status=status.HTTP_400_BAD_REQUEST ) # 仅管理员可访问的列表接口 @api_view(['GET']) @permission_classes([IsAdminUser]) def contact_list_admin(request): contact = Contact.objects.all() serializer = ContactSerializer(contact, many=True) return Response(serializer.data)
注意:原代码里返回的sucess是拼写错误,正确拼写为success
2. 配置允许的前端域名
在settings.py里添加你自己的前端域名白名单,同时保留原有的限流配置:
# 新增前端域名白名单 ALLOWED_FRONTEND_ORIGINS = [ "https://你的正式站点域名", "http://localhost:5173", # 本地开发用的前端地址,上线后可删除 ] REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', ], 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', ], 'DEFAULT_THROTTLE_RATES': { 'anon': '10/minute', } }
同时记得在Django的CORS_ALLOWED_ORIGINS配置里也加上上述白名单域名,不要开CORS_ALLOW_ALL_ORIGINS。
3. 前端配合携带CSRF令牌
Django的CSRF校验会拦截没有合法令牌的伪造请求,React侧只需要在提交请求时从cookie读取csrftoken,放到请求头X-CSRFToken中即可,以axios为例:
import axios from 'axios' // 从cookie读取csrftoken的工具方法 const getCookie = (name) => { let value = "; " + document.cookie; let parts = value.split("; " + name + "="); if (parts.length === 2) return parts.pop().split(";").shift(); } // 全局配置axios axios.defaults.withCredentials = true axios.defaults.headers.post['X-CSRFToken'] = getCookie('csrftoken')
补充说明
没有任何方案能100%阻止专业人员用工具伪造请求——所有前端能携带的参数、请求头都可以被模拟。但上述配置做完后:
- 用户直接在浏览器输入API地址,会因为方法不被允许返回405,看不到任何数据
- 用Postman等工具直接构造请求,会因为来源不合法、没有正确CSRF令牌被直接拦截,返回403
- 批量刷接口的行为会被你之前配置的频率限制拦住
如果还遇到垃圾提交,可以再加一层图形验证码、人机验证,拦截率会更高。
内容的提问来源于stack exchange,提问作者hawaj
相关产品推荐
相关产品推荐

