前端敏感数据加密方案
前言
作为一名前端开发工程师,最近在做一款应用,涉及到了前端的数据加密需要研究下。
数据泄露
插 件
现在有很多的插件,类似于 google 插件这种,可以捕获到前端发送的请求数据,所以如果涉及到了用户信息的,还是得对数据进行特殊处理。
中间人攻击
中间人攻击是常见的攻击方式。过程就是 中间人通过 DNS 欺骗等手段劫持了客户端与服务端的会话。客户端、服务端之间的信息都会经过中间人,中间人可以获取和转发两者的信息。在 HTTP 下,前端数据加密还是避免不了数据泄露,因为中间人可以伪造密钥。为了避免中间人攻击,一般采用 HTTPS 的形式传输。
加密算法
对称加密
也可称为共享密钥加密。意思就是在加密解密都是公用一个密钥,发送和接收双方均使用这个密钥进行数据的加密和解密。这就要求在加密解密前必须知道密钥,对前端来说,势必需要从后端获取密钥,这样也不是太安全。
对称加密常用的算法有:
- AES
- 3DES
非对称加密
也称为公开密钥加密算法。它需要 两个密钥,一个称为公开密钥,也就是常说的公钥,另外一个称为私有密钥,常叫私钥。两者是配对生成的,就像钥匙和锁的关系。
因为加密和解密使用的是两个不用的密钥,所以叫做非对称加密。
- 优点:算法强度复杂,安全性高
- 缺点:没有对称算法快
常见的算法主要有:
- RSA
- Elgamal
散列算法
也成为哈希函数,散列函数。是把数据压缩称为摘要,从而使数据量变小,将数据的格式固定成特定长度的值。 一般用于校验数据的完整性,下载文件就可以校验 MD5 来判断下载数据是否完整。
常见的算法主要有:
- MD4
- MD5
- SHA
方案
上面介绍了常见的加密解密算法,下面就需要选择使用哪一种了。
- 上面也提到了,对称加密由于需要后端返回密钥,反而降低了安全系数,所以是行不通的。
- 非对称加密,前端用公钥加密,服务端用私钥解密,看起来没什么问题,但是如果到客户端接收数据时,需要服务端用公钥加密,然后客户端用私钥解密。这样的话,不管是客户端还是服务端,都需要生成自己的公钥私钥。
- 两者结合,第一点中提到了,需要后端返回密钥。
- 客户端生成一个对称加密的 密钥 ①
- 传输内容通过 密钥 ① 进行对称加密传输给服务端
- 同时要把 密钥 ① 利用公钥进行非对称加密给到服务端
- 服务 端通过 私钥 把对称加密的 密钥 ① 解密出来,然后用过改密钥 ① 解密出内容 上面是服务端接收,如果是客户算接收的话,就是
- 响应数据跟对称加密的 密钥 ① 进行加密
- 然后客户端接收到密文,通过客户端的 密钥 ① 进行解密
- 上面三种方案,第二种最简单,但是需要维护两套公钥私钥,公钥变化,就必须通知对方有变化,不够灵活。方案三应该最理想,密钥 ① 更加灵活,而且只需要在客户端维护,也不需要通知服务端,而且如果数据量很大,对称加密又要比非对称快速。
所以最终选用方案三 来实现。
实现
实现的话,我们选用 AES 对称加密,RSA 非对称加密,
利用 crypto-js 来进行 AES 加密 利用 JSEncrypt RSA 加密
RSA 生成密钥
import JSEncrypt from "jsencrypt";
// 初始化
const keyPair = new JSEncrypt();
const genKeyPair = () => {
const genKeyPair = {};
genKeyPair.privateKey = rsaUtil.keyPair.getPrivateKey(); // 生成RSA密钥
genKeyPair.publicKey = rsaUtil.keyPair.getPublicKey(); // 生成RSA公钥
return genKeyPair;
};
RSA 公钥加密
/**
* rsa公钥加密
* @param {*} content 需要加密内容
* @param {*} publicKey 服务端的公钥
* @returns
*/
const encryptByRsa = (content, publicKey) => {
const newValue = typeof content === 'string' ? content : content.toString();
keyPair.setPublicKey(publicKey);
return keyPair.encryptLong(newValue); // 注意:加密类型为string
},
RSA 私钥解密
/**
* rsa私钥解密
* @param {*} content 解密的内容
* @param {*} privateKey 解密私钥
* @returns
*/
const decryptByRsa = (content, privateKey) => {
keyPair.setPrivateKey(privateKey);
return keyPair.decryptLong(content);
};
AES 加密
import CryptoJS from "crypto-js";
/**
* data 加密内容
* key AES密钥
*/
const cryptoData = (data, key) => {
const aesKey = CryptoJS.enc.Utf8.parse(key);
const newValue = typeof data === "string" ? cipherContent : JSON.stringify(data);
const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(newValue), aesKey, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return encrypted.toString();
};
AES 解密
import CryptoJS from "crypto-js";
/**
* data 解密内容 string
* key AES密钥
*/
const deCryptoData = (data, key) => {
const aesKey = CryptoJS.enc.Utf8.parse(key);
const decrypt = CryptoJS.AES.decrypt(data, aesKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 });
const decString = CryptoJS.enc.Utf8.stringify(decrypt).toString();
return decString instanceof Object ? decString : JSON.parse(decString);
};
总结
上面的内容,主要介绍了针对 当前需求,需要对信息加密及解密,保护客户的隐私数据,虽然会有一点点的性能损失,但是数据安全更重要吧,也为了给破解的人增加一些攻击难度。