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

如何配置Django API仅接收自身站点请求并拦截外部非法调用

首先明确一个前提:所有运行在用户浏览器侧的代码、请求头、参数都是完全公开可篡改的,不存在任何纯前端配置能彻底阻止他人复制请求调用你的接口,你需要通过多层后端校验把非法调用的成本拉到足够高,同时避免核心密钥泄露。

多层防护实现方案

1. 基础防护:开启CSRF校验

CSRF是Django自带的基础防护能力,可以挡住跨站伪造的请求,注意不要只靠Origin、Referer头做校验——这两个头客户端可以随意伪造,只能拦住最低级的爬虫。

  • 配置Django settings:
    1. 确认django.middleware.csrf.CsrfViewMiddleware在中间件列表中开启
    2. Heroku部署时必须添加配置SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https'),否则反向代理会导致HTTPS请求识别异常
    3. 配置CSRF_TRUSTED_ORIGINS,只填入你自己的站点域名,不要留通配符
    4. 开启SESSION_COOKIE_SECURE = True、CSRF_COOKIE_SECURE = True,强制Cookie只在HTTPS连接下传输
    5. ALLOWED_HOSTS严格配置为自己的域名,禁止使用*通配
  • 修改前端请求逻辑,带上CSRF令牌,同站请求优先用相对路径避免跨域问题:
// 从Cookie读取CSRF令牌的工具函数
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
const csrftoken = getCookie('csrftoken');

var clientSecret = await fetch('/api/', {
    method: 'POST',
    body: params,
    headers: {
        'Content-Type': 'text/plain',
        'X-CSRFToken': csrftoken
    },
    credentials: 'same-origin' // 同站请求自动携带Cookie
}).then(r => r.json())

注意:CSRF校验只能挡跨站发起的请求,挡不住用户手动在控制台抓包、复制参数从本地发请求,必须叠加下一层防护。

2. 核心防护:短期一次性签名校验(防重放)

这部分是防止他人复制请求重放的核心:每次发敏感请求前,先向后端申请一个有效期极短、和当前用户会话绑定、只能使用一次的随机签名,签名验证不通过直接拒绝请求。

  • 后端实现示例(缓存可以用Heroku的Redis插件,不要把签名存在前端或Cookie里):
from rest_framework.request import Request as RESTRequest
from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework import status
from django.core.cache import cache
import uuid
import requests

# 发放临时签名的接口,页面加载或发起敏感请求前先调用
@api_view(['GET'])
def get_request_sign(request):
    if not request.session.session_key:
        request.session.create()
    # 生成随机签名,缓存有效期设为10-15秒即可
    sign = uuid.uuid4().hex
    cache.set(f"pay_sign_{request.session.session_key}", sign, timeout=15)
    return Response({"sign": sign})

@api_view(['POST'])
# 如果接口需要登录才能访问,加上下面的装饰器
# @permission_classes([IsAuthenticated])
def payment(request, *args, **kwargs):
    # 校验会话合法性
    if not request.session.session_key:
        return Response({"error": "非法请求"}, status=status.HTTP_403_FORBIDDEN)
    # 校验签名
    req_sign = request.headers.get('X-Request-Sign', '')
    cache_key = f"pay_sign_{request.session.session_key}"
    valid_sign = cache.get(cache_key)
    if not valid_sign or req_sign != valid_sign:
        return Response({"error": "非法请求"}, status=status.HTTP_403_FORBIDDEN)
    # 校验通过立刻删除缓存中的签名,防止重放
    cache.delete(cache_key)

    # 原有业务逻辑
    ... #define headers_in and params_in here
    # 注意:出站接口的密钥只能存在Heroku环境变量里,绝对不能传到前端
    response = requests.post('https://outboundapirequest.com/v1/request', 
            headers=headers_in,
            data=params_in)

    return Response(response.json()['value'])
  • 前端对应修改,发敏感请求前先获取临时签名:
// 先拿临时签名
const signRes = await fetch('/api/get-sign/', {
    credentials: 'same-origin'
}).then(r=>r.json());
// 再发敏感请求
var clientSecret = await fetch('/api/', {
    method: 'POST',
    body: params,
    headers: {
        'Content-Type': 'text/plain',
        'X-CSRFToken': csrftoken,
        'X-Request-Sign': signRes.sign
    },
    credentials: 'same-origin'
}).then(r => r.json())

3. 补充防护:频率与参数校验

  • 给敏感接口加DRF频率限制,比如同一个IP/会话1分钟内最多允许调用3次,防止接口被批量刷取
  • 所有业务参数必须做后端校验,比如支付类接口绝对不能信任前端传的金额、商品ID参数,必须以后端存储的订单数据为准
  • 开启SECURE_SSL_REDIRECT = True,强制所有请求跳转到HTTPS,防止传输链路被窃听
  • 所有敏感请求打日志,记录请求IP、用户ID、参数、时间,出现异常调用可以快速溯源
额外注意事项
  • 不要在静态JS文件里写任何固定的秘钥字符串,这类静态文件任何人都可以直接查看,等于没有防护
  • 不要试图通过判断浏览器指纹、前端JS环境混淆等方式做拦截,这类手段都可以被绕过,核心校验必须放在后端
  • 你现在用后端代理调用第三方敏感接口的逻辑是正确的,第三方接口的密钥只能存在Heroku的环境变量配置中,绝对不能出现在前端代码或接口返回值里

内容的提问来源于stack exchange,提问作者bernardo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 03:15:47