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

如何实现Django Rest Framework API加密响应并在React前端解密?

可行的API数据加密/解密方案

针对你前后端分离的架构(React+Axios前端,Django+DRF后端),以下是几种成熟的API数据加密实现方案,涵盖对称加密、非对称混合加密和端到端加密场景:

1. 对称加密(AES-GCM/Fernet)

这是最常用的方案,性能高、实现简单,适合大多数场景。核心是前后端共享一个加密密钥,后端用密钥加密响应数据,前端用同一密钥解密。

后端(Django+DRF)实现

  • 使用cryptography库提供的Fernet(封装了AES-GCM,自动处理密钥管理和签名)或直接用AES-GCM:
    1. 安装依赖:pip install cryptography
    2. 编写自定义DRF渲染器,替换默认的JSONRenderer:
    from rest_framework.renderers import BaseRenderer
    from cryptography.fernet import Fernet
    import json
    
    # 密钥建议从环境变量读取,不要硬编码
    ENCRYPTION_KEY = b"your_fernet_key_here" # 可通过Fernet.generate_key()生成
    fernet = Fernet(ENCRYPTION_KEY)
    
    class EncryptedJSONRenderer(BaseRenderer):
        media_type = 'application/json'
        format = 'json'
    
        def render(self, data, accepted_media_type=None, renderer_context=None):
            # 将数据序列化为JSON字符串,再加密
            json_data = json.dumps(data).encode('utf-8')
            encrypted_data = fernet.encrypt(json_data)
            # 返回加密后的base64字符串
            return json.dumps({"encrypted_data": encrypted_data.decode('utf-8')}).encode('utf-8')
    
    1. 在DRF配置中全局启用或为单个视图设置该渲染器:
    REST_FRAMEWORK = {
        'DEFAULT_RENDERER_CLASSES': [
            'path.to.EncryptedJSONRenderer',
        ]
    }
    

前端(React+Axios)实现

  • 使用crypto-js或浏览器原生的Web Crypto API解密:
    1. 安装依赖:npm install crypto-js(或直接用Web Crypto)
    2. 编写Axios响应拦截器,自动解密数据:
    import axios from 'axios';
    import CryptoJS from 'crypto-js';
    
    // 密钥要和后端一致,建议从安全渠道获取(如登录接口返回,而非硬编码)
    const ENCRYPTION_KEY = 'your_fernet_key_here';
    
    const api = axios.create({
      baseURL: 'https://api.mydomain.com',
    });
    
    // 响应拦截器:解密数据
    api.interceptors.response.use(
      (response) => {
        if (response.data.encrypted_data) {
          // Fernet解密需要处理格式,这里以CryptoJS适配为例(或直接用fernet.js库)
          const decryptedBytes = CryptoJS.AES.decrypt(response.data.encrypted_data, ENCRYPTION_KEY);
          response.data = JSON.parse(decryptedBytes.toString(CryptoJS.enc.Utf8));
        }
        return response;
      },
      (error) => Promise.reject(error)
    );
    
    export default api;
    

注意事项

  • 密钥绝对不能硬编码在前端代码中,建议用户登录后通过HTTPS接口返回临时会话密钥,定期轮换。
  • 必须启用HTTPS,防止密钥或加密数据被中间人窃取。

2. 非对称加密+对称加密混合方案

解决对称加密密钥共享的安全风险,通过RSA交换临时AES密钥,再用AES加密数据(兼顾安全和性能)。

流程说明

  1. 前端生成RSA密钥对(公钥+私钥),私钥存在前端内存中(不存储),公钥传给后端。
  2. 后端生成临时AES密钥,用前端公钥加密AES密钥,同时用AES密钥加密响应数据。
  3. 后端返回加密的AES密钥+加密的数据。
  4. 前端用私钥解密AES密钥,再用AES密钥解密数据。

后端(Django)实现

from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import os, json

def encrypt_aes_key_with_rsa(public_key_pem, aes_key):
    # 加载前端公钥
    public_key = serialization.load_pem_public_key(public_key_pem.encode('utf-8'))
    # 加密AES密钥
    encrypted_aes_key = public_key.encrypt(
        aes_key,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    return encrypted_aes_key

def encrypt_data_with_aes(data, aes_key):
    # AES-GCM加密数据
    iv = os.urandom(12)
    cipher = Cipher(algorithms.AES(aes_key), modes.GCM(iv))
    encryptor = cipher.encryptor()
    json_data = json.dumps(data).encode('utf-8')
    ciphertext = encryptor.update(json_data) + encryptor.finalize()
    return iv, ciphertext, encryptor.tag

# 在视图中使用:接收前端公钥,返回加密的AES密钥和加密数据
def some_api_view(request):
    public_key_pem = request.data.get('public_key')
    if not public_key_pem:
        return Response({'error': 'Public key required'}, status=400)
    
    # 生成临时AES密钥
    aes_key = os.urandom(32)
    # 加密数据
    iv, ciphertext, tag = encrypt_data_with_aes({'message': 'sensitive data'}, aes_key)
    # 加密AES密钥
    encrypted_aes_key = encrypt_aes_key_with_rsa(public_key_pem, aes_key)
    
    return Response({
        'encrypted_aes_key': encrypted_aes_key.hex(),
        'iv': iv.hex(),
        'ciphertext': ciphertext.hex(),
        'tag': tag.hex()
    })

前端(React)实现

使用Web Crypto API处理RSA和AES操作:

// 生成RSA密钥对
async function generateRSAKeyPair() {
  return window.crypto.subtle.generateKey(
    {
      name: "RSA-OAEP",
      modulusLength: 2048,
      publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
      hash: "SHA-256",
    },
    true,
    ["encrypt", "decrypt"]
  );
}

// 导出公钥为PEM格式
async function exportPublicKey(key) {
  const exported = await window.crypto.subtle.exportKey("spki", key);
  return btoa(String.fromCharCode(...new Uint8Array(exported)));
}

// 解密AES密钥
async function decryptAESKey(privateKey, encryptedAesKeyHex) {
  const encryptedAesKey = new Uint8Array(Buffer.from(encryptedAesKeyHex, 'hex'));
  return window.crypto.subtle.decrypt(
    { name: "RSA-OAEP" },
    privateKey,
    encryptedAesKey
  );
}

// AES-GCM解密数据
async function decryptData(aesKey, ivHex, ciphertextHex, tagHex) {
  const iv = new Uint8Array(Buffer.from(ivHex, 'hex'));
  const ciphertext = new Uint8Array(Buffer.from(ciphertextHex, 'hex'));
  const tag = new Uint8Array(Buffer.from(tagHex, 'hex'));
  const cipher = await window.crypto.subtle.importKey(
    "raw",
    aesKey,
    { name: "AES-GCM" },
    false,
    ["decrypt"]
  );
  const decrypted = await window.crypto.subtle.decrypt(
    { name: "AES-GCM", iv: iv, tag: tag },
    cipher,
    ciphertext
  );
  return JSON.parse(new TextDecoder().decode(decrypted));
}

// 发送请求示例
async function fetchEncryptedData() {
  const keyPair = await generateRSAKeyPair();
  const publicKeyPem = await exportPublicKey(keyPair.publicKey);
  
  const response = await axios.post('/api/data', { public_key: publicKeyPem });
  const { encrypted_aes_key, iv, ciphertext, tag } = response.data;
  
  const aesKey = await decryptAESKey(keyPair.privateKey, encrypted_aes_key);
  const decryptedData = await decryptData(aesKey, iv, ciphertext, tag);
  
  console.log(decryptedData);
}

优点

  • 无需长期共享密钥,临时AES密钥每次请求可轮换,安全性更高。
  • 兼顾非对称加密的安全性和对称加密的性能。

3. 端到端加密(E2EE)

适用于高度敏感的数据场景,后端完全无法解密数据,只有前端持有解密密钥(比如基于用户密码派生密钥)。

实现思路

  1. 用户登录时,前端用用户密码通过PBKDF2或Argon2派生加密密钥。
  2. 前端将密钥的公钥(或派生的对称密钥标识)传给后端,后端存储该标识。
  3. 后端用该公钥加密数据,返回给前端;前端用派生的密钥解密。

技术栈

  • 后端:使用libsodium的Python绑定pysodium处理加密。
  • 前端:使用libsodium-wrappers库,简化加密操作。

注意事项

  • 后端无法恢复明文数据,必须由用户自己保管密钥(如密码),丢失则无法恢复数据。
  • 需处理密钥轮换、设备同步等复杂场景。

通用安全建议

  • 所有通信必须使用HTTPS,防止中间人攻击窃取加密数据或密钥。
  • 前端避免将密钥存储在localStorage/sessionStorage,建议存在内存中,页面刷新后重新生成或从登录流程获取。
  • 对前端解密后的明文数据做XSS防护,避免数据泄露。
  • 定期轮换密钥,尤其是对称加密的长期密钥。

内容的提问来源于stack exchange,提问作者Muhammad Talha Abbas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:05:58