前后端安全数据处理方案设计

这是一个典型的前后端安全数据处理方案设计,需要同时解决数据脱敏展示安全传输完整数据编辑的问题。以下是完整的解决方案:

一、整体架构设计

复制代码
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   前端       │    │   后端       │    │   数据库     │
├─────────────┤    ├─────────────┤    ├─────────────┤
│ 1.获取掩码数据 │◄──│ 3.返回掩码数据 │◄──│ 4.存储完整数据│
│ 2.提交编辑请求 │──►│ 5.处理编辑请求 │──►│ 6.更新完整数据│
└─────────────┘    └─────────────┘    └─────────────┘

二、核心方案:双ID + 密文传输

方案要点:

  1. 每个敏感字段使用两个版本

    • 完整版:存储在数据库,用于业务处理
    • 掩码版:用于前端展示和大部分API返回
  2. 安全传输机制

    • 敏感数据使用非对称加密传输
    • 前端持有公钥,后端持有私钥

三、详细实施方案

1. 数据库设计

sql 复制代码
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    -- 原始敏感数据(加密存储)
    phone_encrypted VARCHAR(128) NOT NULL,
    email_encrypted VARCHAR(256),
    
    -- 脱敏数据(明文存储,避免每次计算)
    phone_masked VARCHAR(20) DEFAULT NULL,  -- 如: 138****1234
    email_masked VARCHAR(100) DEFAULT NULL, -- 如: a***@gmail.com
    
    -- 唯一标识符(用于关联编辑)
    phone_hash VARCHAR(64) UNIQUE,  -- 原始手机号的hash
    email_hash VARCHAR(64) UNIQUE,  -- 邮箱的hash
    
    -- 版本控制
    data_version INT DEFAULT 1,
    updated_at TIMESTAMP
);

2. 接口设计

2.1 查询接口(返回脱敏数据)
javascript 复制代码
// GET /api/user/profile
{
    "code": 200,
    "data": {
        "id": 12345,
        "phone": "138****1234",     // 掩码版
        "email": "a***@gmail.com",  // 掩码版
        "phone_hash": "a1b2c3d4...", // 用于标识此条数据
        "email_hash": "e5f6g7h8...",
        "data_version": 3
    }
}
2.2 编辑接口(安全提交完整数据)
javascript 复制代码
// POST /api/user/update-phone
{
    "hash": "a1b2c3d4...",  // 标识要修改的是哪个字段的哪条数据
    "encrypted_data": "RSA_AES_ENCRYPTED_PAYLOAD", // 加密的完整数据
    "data_version": 3,       // 当前数据版本(防并发冲突)
    "signature": "..."       // 请求签名
}

3. 加密传输流程

前端处理:
javascript 复制代码
// 1. 生成临时对称密钥
const aesKey = CryptoJS.lib.WordArray.random(32); // 256-bit AES key

// 2. 用AES加密完整数据
const encryptedData = CryptoJS.AES.encrypt(
    JSON.stringify({
        phone: '13812345678',      // 完整手机号
        timestamp: Date.now(),     // 防重放
        nonce: '随机字符串'
    }),
    aesKey
).toString();

// 3. 用后端RSA公钥加密AES密钥
const encryptedKey = RSA.encrypt(aesKey.toString(), PUBLIC_KEY);

// 4. 提交到后端
const payload = {
    hash: 'a1b2c3d4...',
    encrypted_key: encryptedKey,
    encrypted_data: encryptedData,
    data_version: currentVersion,
    signature: signRequest(encryptedData)
};
后端解密:
java 复制代码
@Service
public class SensitiveDataService {
    
    @Autowired
    private RSAKeyService rsaKeyService;
    
    public UpdateResult handleEncryptedUpdate(UpdateRequest request) {
        try {
            // 1. 验证请求签名
            verifySignature(request);
            
            // 2. RSA解密获取AES密钥
            String aesKeyStr = rsaKeyService.decrypt(request.getEncryptedKey());
            SecretKey aesKey = decodeAESKey(aesKeyStr);
            
            // 3. AES解密数据
            String decryptedJson = AES.decrypt(request.getEncryptedData(), aesKey);
            UpdateData data = parseUpdateData(decryptedJson);
            
            // 4. 验证数据有效性
            validateData(data, request.getDataVersion());
            
            // 5. 更新数据库(完整数据重新加密存储)
            updateDatabase(data, request.getHash());
            
            // 6. 生成新的掩码数据和hash
            MaskedData masked = generateMaskedData(data.getPhone());
            
            return UpdateResult.success(masked);
            
        } catch (Exception e) {
            return UpdateResult.error("解密或验证失败");
        }
    }
    
    private MaskedData generateMaskedData(String phone) {
        return MaskedData.builder()
            .phone(maskPhone(phone))           // 138****1234
            .phoneHash(hashPhone(phone))       // 新的hash值
            .dataVersion(incrementVersion())
            .build();
    }
}

4. 高级安全措施

4.1 防重放攻击
java 复制代码
@Component
public class ReplayAttackProtection {
    
    // 使用Redis存储已使用的nonce
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public boolean isValidRequest(String nonce, long timestamp) {
        // 1. 检查时间戳(5分钟内有效)
        if (Math.abs(System.currentTimeMillis() - timestamp) > 5 * 60 * 1000) {
            return false;
        }
        
        // 2. 检查nonce是否已使用
        String key = "nonce:" + nonce;
        if (redisTemplate.hasKey(key)) {
            return false;
        }
        
        // 3. 存储nonce,设置5分钟过期
        redisTemplate.opsForValue().set(key, "used", 5, TimeUnit.MINUTES);
        return true;
    }
}
4.2 数据版本控制(乐观锁)
sql 复制代码
-- 更新时检查版本
UPDATE users 
SET phone_encrypted = ?,
    phone_masked = ?,
    phone_hash = ?,
    data_version = data_version + 1,
    updated_at = NOW()
WHERE id = ? 
  AND phone_hash = ? 
  AND data_version = ?  -- 版本必须匹配
4.3 审计日志
java 复制代码
@Entity
@Table(name = "sensitive_data_audit_log")
public class SensitiveDataAuditLog {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private Long userId;
    private String dataType;      // "PHONE", "EMAIL"
    private String operation;     // "VIEW", "UPDATE"
    private String oldHash;       // 修改前的数据hash
    private String newHash;       // 修改后的数据hash
    private String ipAddress;
    private String userAgent;
    private LocalDateTime createdAt;
    
    // 不存储实际数据,只存储hash
}

5. 前端完整实现示例

javascript 复制代码
// sensitiveDataService.js
class SensitiveDataService {
    
    constructor() {
        this.publicKey = null;
        this.cache = new Map(); // 缓存hash和版本
    }
    
    // 初始化获取公钥
    async init() {
        const response = await fetch('/api/security/public-key');
        this.publicKey = await response.text();
    }
    
    // 获取脱敏数据
    async fetchMaskedData() {
        const response = await fetch('/api/user/profile');
        const data = await response.json();
        
        // 缓存hash和版本
        this.cache.set('phone', {
            hash: data.phone_hash,
            version: data.data_version,
            masked: data.phone
        });
        
        return data;
    }
    
    // 提交完整数据编辑
    async updatePhone(newPhone) {
        const cached = this.cache.get('phone');
        
        // 准备加密数据
        const payload = {
            original_phone: cached.masked,  // 原始掩码值(用于验证)
            new_phone: newPhone,           // 新完整手机号
            timestamp: Date.now(),
            nonce: this.generateNonce()
        };
        
        // 加密传输
        const encryptedRequest = await this.encryptRequest(
            cached.hash,
            payload,
            cached.version
        );
        
        // 发送请求
        const response = await fetch('/api/user/update-phone', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(encryptedRequest)
        });
        
        const result = await response.json();
        
        if (result.success) {
            // 更新缓存
            this.cache.set('phone', {
                hash: result.data.phone_hash,
                version: result.data.data_version,
                masked: result.data.phone
            });
        }
        
        return result;
    }
    
    // 加密请求数据
    async encryptRequest(hash, data, version) {
        // 生成AES密钥
        const aesKey = await window.crypto.subtle.generateKey(
            { name: "AES-GCM", length: 256 },
            true,
            ["encrypt", "decrypt"]
        );
        
        // 加密数据
        const encodedData = new TextEncoder().encode(JSON.stringify(data));
        const iv = window.crypto.getRandomValues(new Uint8Array(12));
        const encryptedData = await window.crypto.subtle.encrypt(
            { name: "AES-GCM", iv: iv },
            aesKey,
            encodedData
        );
        
        // 导出AES密钥并用RSA加密
        const exportedKey = await window.crypto.subtle.exportKey("raw", aesKey);
        const encryptedKey = await this.rsaEncrypt(exportedKey);
        
        return {
            hash: hash,
            encrypted_key: this.arrayBufferToBase64(encryptedKey),
            encrypted_data: this.arrayBufferToBase64(encryptedData),
            iv: this.arrayBufferToBase64(iv),
            data_version: version,
            timestamp: Date.now()
        };
    }
    
    // RSA加密
    async rsaEncrypt(data) {
        // 使用Web Crypto API进行RSA加密
        const publicKey = await this.importPublicKey(this.publicKey);
        return await window.crypto.subtle.encrypt(
            { name: "RSA-OAEP" },
            publicKey,
            data
        );
    }
    
    // 生成随机nonce
    generateNonce() {
        return window.crypto.getRandomValues(new Uint32Array(4)).join('');
    }
    
    // 工具函数
    arrayBufferToBase64(buffer) {
        return btoa(String.fromCharCode(...new Uint8Array(buffer)));
    }
}

6. 性能优化建议

6.1 缓存脱敏数据
java 复制代码
@Cacheable(value = "userMaskedData", key = "#userId")
public MaskedData getMaskedData(Long userId) {
    // 从数据库查询,避免重复计算掩码
    return userRepository.findMaskedData(userId);
}
6.2 批量处理支持
javascript 复制代码
// 批量更新多个敏感字段
async updateMultipleFields(updates) {
    const requests = updates.map(update => ({
        field_type: update.type, // 'PHONE', 'EMAIL'
        field_hash: update.hash,
        encrypted_data: await this.encryptFieldData(update.value)
    }));
    
    // 批量提交
    return await fetch('/api/user/batch-update', {
        method: 'POST',
        body: JSON.stringify({ updates: requests })
    });
}

四、方案优势总结

  1. 安全性

    • 传输过程全加密(RSA+AES双保险)
    • 数据库不存储明文
    • 支持防重放攻击和版本控制
  2. 实用性

    • 前端可以正常展示脱敏数据
    • 编辑时可提交完整数据
    • 支持并发修改控制
  3. 可扩展性

    • 方案适用于各种敏感数据(身份证、银行卡号等)
    • 可轻松集成到现有系统
  4. 合规性

    • 符合数据最小化原则
    • 完善的审计日志
    • 支持数据脱敏展示

这个方案平衡了安全性、用户体验和系统性能,可以根据实际业务需求进行调整和优化。

相关推荐
m0_738120722 小时前
渗透测试——靶机DC-5详细渗透getshell过程
网络·安全·web安全·ssh·php
爱编程的小吴2 小时前
文件上传漏洞:攻击案例拆解与全方位防御指南
安全
世界尽头与你3 小时前
CVE-2021-40438_ Apache HTTP Server mod_proxy 模块 SSRF漏洞
安全·网络安全·渗透测试·apache
日志易3 小时前
日志易5.5正式发布,强化可视化运维与企业级安全,赋能智能日志管理
大数据·运维·安全
专业开发者3 小时前
安全防护蓝牙 ® 网状网络代理应用
网络·物联网·安全
ZFJ_张福杰5 小时前
【技术深度】金融 / 钱包级 Android 安全性架构(毒APP)
android·安全·金融·架构·签名证书
安当加密5 小时前
通过ASP认证系统作为 RADIUS 认证服务器:解决异地办公登录安全问题的实践方案*
运维·服务器·安全
ALex_zry19 小时前
C++20和C++23 在内存管理、并发控制和类型安全相关优化方式的详细技术分析
安全·c++20·c++23
是喵斯特ya19 小时前
数据库的权限提升
数据库·安全