联邦学习中本地加密数据如何防止中间人攻击?
最近在做前端联邦学习项目,需要加密用户数据后再上传到服务端聚合。我用AES加密数据后再通过HTTPS发送,但测试时发现中间人能通过抓包获取加密密钥。看代码哪里有问题?
const crypto = require('crypto');
const key = crypto.randomBytes(32); // 固定生成密钥
function encryptData(data) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
return { iv: iv.toString('hex'), encrypted: encrypted.toString('hex') };
}
// 将密钥和加密数据一起发送到服务端
fetch('/aggregate', {
method: 'POST',
body: JSON.stringify({
key: key.toString('hex'),
...encryptData(userSensitiveData)
})
});
服务端要求必须在客户端完成加密,但这样传输密钥本身不就暴露了?难道联邦学习场景下密钥不应该随每个请求动态生成?有没有更安全的密钥交换方案?
标准写法里,联邦学习场景下根本不应该在客户端生成密钥再传出去。AES 是对称加密,密钥必须在通信双方安全共享,不能走网络传输。你这个需求应该用非对称加密做密钥协商,比如用 TLS 握手那一套思路:客户端生成临时 ECDH 密钥对,把公钥发给服务端,服务端用它来加密一个随机生成的 AES 密钥回传,客户端再用私钥解出这个 AES 密钥,然后用它加密数据。这样密钥 never traverse the network in plaintext。
不过你用的是 Node.js,前端真机跑的话得考虑浏览器环境,实际项目里更推荐直接走 HTTPS + mTLS(双向证书认证),或者用 Web Crypto API 实现 ECDH + AES-GCM 的组合加密。下面给个简化版思路(服务端配合):
客户端(浏览器端 JS)大致这样:
服务端要配套做 ECDH 解密,用 clientPubKey 生成共享密钥,再用它加密一个随机 AES 密钥发回去。注意所有密钥交换必须在 TLS 连接内完成,不能降级。
另外你原代码里
crypto.randomBytes(32)放全局变量里——这密钥是固定生成的,根本不是每次请求动态的,连防重放都做不到。真要自己实现,至少得每次请求前动态生成临时密钥对。不过说句实话,如果你们项目已经用 HTTPS,且客户端环境可控(比如内部 App 或可信浏览器),很多公司会直接放弃客户端加密,改用更简单的方案:服务端下发短期 token,前端用 token 加盐哈希生成临时密钥,或者直接依赖 TLS 的端到端信任。联邦学习里加密的重点其实是防止服务端看到明文,而不是防中间人——中间人要是能劫持 TLS,整个系统都崩了,AES 也救不了。
联邦学习场景下,推荐用非对称加密来解决密钥交换的问题。比如你可以用RSA或者ECDH来做密钥协商。服务端提前生成一对公私钥,把公钥发给客户端,客户端用公钥加密AES密钥后再传给服务端,这样即使被抓包也拿不到原始密钥。
下面是一个改进的代码示例,假设服务端已经提供了公钥:
服务端收到后用自己的私钥解密出AES密钥,再用这个密钥解密数据。这种方式能有效防止中间人攻击,因为公钥可以公开,但私钥只在服务端保存。
另外提醒一下,AES密钥每次请求都要动态生成,别复用!不然安全性会大打折扣。希望这些建议能帮到你,有问题咱们再交流!