前端调用接口时怎么对请求参数加密才安全?
我们后端要求所有敏感接口的请求参数必须加密传输,但我试了用 AES 加密后传过去,后端说解密失败。我是在浏览器里用 CryptoJS 做的加密,密钥直接写在代码里,是不是哪里不对?
这是我的加密代码:
const key = CryptoJS.enc.Utf8.parse('mySecretKey12345');
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(params), key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
}).toString();
但这样真的安全吗?而且后端用 Java 解密一直报错,是不是加密方式不匹配?
问题在哪里
先说最可能的原因:CryptoJS 的 AES.encrypt 默认返回的是 OpenSSL 兼容的加密格式,这个格式会在密文前面加上 "Salted__" + 8字节salt + 实际密文,然后整体 Base64 编码。但 Java 那边如果直接用标准 AES 解密,不处理这个头,就会报错。
另外,你的密钥 'mySecretKey12345' 是 15 个字符,AES 要求密钥长度必须是 16/24/32 字节。CryptoJS 会自动补齐,但补齐方式可能和 Java 那边不一致。
解决方案
推荐用 CBC 模式 + PKCS7 填充(Java 叫 PKCS5Padding),这样两端更容易对齐。
前端代码:
对应的 Java 解密代码:
关于安全性
你密钥直接写代码里是最大的问题。浏览器代码可以被任何人看到,包括这个密钥。线上环境这样做等于没加密。
真正安全的方式:
1. 用 HTTPS,这是基础
2. 密钥从后端动态获取,每次请求可以用不同的临时密钥(或者用后端返回的公钥做非对称加密来传递对称密钥)
3. 可以在后端实现一个 /getKey 接口,返回当前会话用的加密密钥,前端每次加密前先调这个接口拿密钥
简单说,HTTPS + 动态密钥才是正经做法,前端单独做 AES 加密也就是防防君子,真正的安全还是得靠传输层加密。
先说解密失败的原因。你用的是 ECB 模式 + PKCS7 填充,CryptoJS 的
AES.encrypt().toString()实际输出的是 Base64 编码的密文。Java 端解密时要先做 Base64 解码,再用 AES/ECB/PKCS5Padding 解密。Java 代码大概长这样:
如果后端报填充错误,大概率是 Java 用了错误的填充方式,或者没做 Base64 解码直接解密了。
再说你关心的安全性问题。密钥直接写死在前端代码里是最大的隐患——用户打开浏览器开发者工具就能看到你的密钥和加密逻辑,这等于没加密。攻击者完全可以复用你的加密方法伪造请求。
真正安全的做法是:前端用后端提供的公钥做 RSA 非对称加密,或者用后端返回的临时对称密钥配合 HTTPS 做加密。单纯在前端硬编码密钥防君子不防小人,没多大意义。
如果你们只是防抓包工具简单加密一下,ECB 模式其实也够用,但密钥必须动态获取,别写死。