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

如何确保Django+DRF开发的API仅被自有CSR前端访问?

针对CSR前端的DRF API保护方案

1. 基于请求头的签名验证

在前端请求时动态生成签名并添加到请求头,后端通过约定规则验证签名合法性,大幅提高非法调用的门槛:

  • 实现逻辑:
    • 前端:每次请求时,用与后端协商的密钥(建议构建时从环境变量注入,避免硬编码),对请求URL、时间戳、JWT令牌的payload部分做HMAC-SHA256哈希,生成签名后放入自定义请求头X-Request-Signature,同时带上时间戳X-Request-Timestamp。
    • 后端(DRF自定义权限类):
      1. 校验时间戳是否在有效范围(比如5分钟),防止重放攻击。
      2. 用相同密钥和算法重新计算签名,与前端传来的签名比对。
      3. 签名一致则允许访问,否则拒绝。
  • DRF权限类示例:
from rest_framework.permissions import BasePermission
import hmac
import hashlib
from datetime import datetime, timedelta
from django.conf import settings

class SignedRequestPermission(BasePermission):
    def has_permission(self, request, view):
        secret_key = settings.FRONTEND_SIGN_SECRET.encode('utf-8')
        timestamp = request.META.get('HTTP_X_REQUEST_TIMESTAMP')
        signature = request.META.get('HTTP_X_REQUEST_SIGNATURE')
        jwt_token = request.META.get('HTTP_AUTHORIZATION', '').split(' ')[-1]

        if not timestamp or not signature or not jwt_token:
            return False

        # 校验时间戳有效期
        try:
            request_time = datetime.fromtimestamp(int(timestamp))
            if datetime.now() - request_time > timedelta(minutes=5):
                return False
        except ValueError:
            return False

        # 生成待签名字符串
        payload_part = jwt_token.split('.')[1]
        sign_string = f"{request.path}#{timestamp}#{payload_part}".encode('utf-8')
        computed_signature = hmac.new(secret_key, sign_string, hashlib.sha256).hexdigest()

        return hmac.compare_digest(computed_signature, signature)

2. 结合JWT的前端指纹验证

在JWT的payload中加入前端唯一标识,后端每次请求校验该标识的一致性:

  • 实现步骤:
    1. 用户登录时,前端生成唯一指纹(比如hash(user-agent + 本地存储的UUID)),随登录请求传给后端。
    2. 后端生成JWT时,将指纹加入payload(如frontend_fingerprint字段)。
    3. 后续API请求,前端在请求头X-Frontend-Fingerprint中带上该指纹,后端校验JWT中的指纹与请求中的是否匹配。
  • DRF权限类示例:
from rest_framework.permissions import BasePermission
import jwt
from django.conf import settings

class FingerprintPermission(BasePermission):
    def has_permission(self, request, view):
        token = request.META.get('HTTP_AUTHORIZATION', '').split(' ')[-1]
        fingerprint = request.META.get('HTTP_X_FRONTEND_FINGERPRINT')

        if not token or not fingerprint:
            return False

        try:
            payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
            return payload.get('frontend_fingerprint') == fingerprint
        except jwt.InvalidTokenError:
            return False

3. 动态API路径/参数混淆

定期或根据请求上下文动态调整API的访问规则:

  • 比如前端根据当前时间戳的哈希值生成API路径后缀(如/api/v1/data/{hash(timestamp)}),后端提前计算合法后缀,仅匹配的路径允许访问。
  • 或在请求中添加动态生成的nonce参数,后端校验参数是否符合约定的生成规则。

4. 强化JWT安全策略

基于已有的JWT机制进一步优化:

  • 缩短JWT过期时间(比如15分钟),配合刷新令牌机制,减少令牌泄露后的风险。
  • 强制使用HTTPS传输,避免令牌被中间人劫持。
  • 启用JWT黑名单机制,用户注销或令牌泄露时可及时失效。

关键提醒

CSR前端的代码本身是暴露的,不存在绝对完美的防护方案。上述方案的核心是提高非法调用的技术门槛,再结合你已遵循的最小权限原则(API仅返回必要数据),能有效降低恶意调用的风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 22:40:58