JS前端接口请求参数混淆方案分享

JS前端接口请求参数混淆方案分享

目录

写在前面

什么接口的参数需要做处理

参数处理

Aes加密

Rsa加密

签名验证

处理时机

后端实现

Rsa解密

Aes解密

处理中间件

路由中使用

写在前面

在一些接口请求的场景中,我们希望携带的数据不希望是以明文的方式提交的,也就是需要对参数做一些混淆或者加密处理,后端拿到数据后再进行解密,得到真实数据。

其目的是为了保护数据的安全,以及提高被识破成明文的门槛。在例如用户登录的接口请求中,如果账号和密码是以明文传输的,会容易导致一些安全性问题,同时,我们也不希望谁都可以伪造参数对接口发起请求,因此前端参数混淆非常有必要,这里分享一个自用的方案,其目的在于:

防止信息泄露

防止参数被随意篡改

提高应用安全性和稳定性

对于加密或者混淆的处理方式的选择,例如base64MD5等安全性不高或者不可逆的算法,不适用于我们这个场景,而是可以利用AesRsa两者结合的方式,实现数据的加解密。

什么接口的参数需要做处理

从安全角度出发,这里一致认为,只要是对数据库有新增、修改、删除操作的,一律需要做混淆/加密,这一类接口,多为postputdelete等请求。

而对于get请求,如果是规范化的接口,一般只是获取数据,不会对数据库有直接的操作,故不需要做参数处理。

当然还有post请求和一些文件上传等,不希望做参数处理的接口,需要特殊处理,以及在开发环境中,为了方便调试,也不需要做处理。

参数处理

因为Rsa在处理数据量较大时优势不明显,适合处理数据量较小的场景,所以对参数数据的处理采用了Aes加密,而参与加密的密钥key则采用Rsa非对称加密提高破解难度。

涉及到的相关依赖及其版本号:

"crypto-js": "^4.1.1",
"jsencrypt": "^3.2.1"

这里使用的方案是,先将原始数据处理为query形式的字符串,然后将其使用随机字符串作为密钥,参与Aes加密,并截取特定的字符串,作为原始密钥,再进行一次Aes加密,最后将原始密钥使用与后端约定好的公钥进行Rsa加密处理,具体流程和算法如下:

对参数排序、提取query字符串处理

将提取的字符串利用随机字符串加密并截取,得到密钥

利用密钥对原始参数进行Aes加密

将密钥进行Rsa非对称加密

输出最终的datakey

/** * 加密请求数据 * @param {Object} rawData 原始数据 * @returns {data, key} */ export function encryptRequestData(rawData) { // 字典排序并赋值 var result = {}, str = [], arr = Object.keys(rawData).sort(); arr.map(m => { result[m] = rawData[m] }); // 处理成 query 形式字符串 for (var p in result) result.hasOwnProperty(p) && str.push(encodeURIComponent(p) + "=" + encodeURIComponent(result[p])); result = str.join("&"); // 参与 Aes 加密的密钥,将处理后的字符串用 16 位随机码对称加密,再从第 3 位开始获取 16 位原始密钥 const rawKey = aesEncrypt(result, randomString(16)).substr(3, 16); // 输出最后的加密参数 const data = aesEncrypt(JSON.stringify(rawData), rawKey); const key = rsaEncrypt(rawKey); return { data, key } } Aes加密 /** * Aes 加密 * @param {String} data * @param {String} customer_key * @returns encrypted */ export function aesEncrypt(data, customer_key = "") { var key = CryptoJS.enc.Utf8.parse(customer_key); var messageHex = CryptoJS.enc.Utf8.parse(data); var encrypted = CryptoJS.AES.encrypt(messageHex, key, { "mode": CryptoJS.mode.ECB, "padding": CryptoJS.pad.Pkcs7 }); return encrypted.toString(); } Rsa加密 /** * Rsa 加密 */ export function rsaEncrypt(rawData) { let data; try { var rsa = new JsEncrypt(); rsa.setPublicKey(publicPem); data = rsa.encrypt(rawData) } catch (error) { return null } return data; } 签名验证

如果需要进一步加强防篡改,可以在处理参数的时候,通过一定算法得出一个签名sign值,一并提交到后端,后端解密后,也通过同样的算法将解密后的数据生成一个sign值,与提交的作对比,判断是否为合法的请求来源。 这里不做详细介绍。

处理时机

我们需要在请求拦截的时候对参数做混淆处理,这里只针对post请求以及非本地环境,另外为了方便调试,除了开发环境外 在实例上还增加了uncrypt来标识哪些接口可以不参与处理。

// 请求拦截 instance.interceptors.request.use( config => { config.headers.contentType = "application/x-www-form-urlencoded" const token = local.get('token') token && (config.headers.Authorization = token) const { method, uncrypt = false, data = {} } = config; (method === 'post' && !uncrypt && cfg.NODE_ENV === 'development') && (config.data = encryptRequestData(data)); return config }, error => Promise.error(error) ) 后端实现

前端参数处理后以datakey组成的对象提交至后端,服务层接受后进行解密,这里以 Egg.js 的实现为例子。 涉及依赖及其版本号:

"crypto-js": "^4.1.1",
"node-rsa": "^1.1.1"

Rsa解密 // RSA 解密 rsaDecrypt(data) { let dataObj; return new Promise(function (resolve, reject) { // 私钥存放在app/extend/pem/private.pem fs.readFile('app/extend/pem/private.pem', function (err, pem) { const key = new NodeRSA(pem, 'pkcs8-private'); key.setOptions({ encryptionScheme: 'pkcs1' }); try { dataObj = key.decrypt(data, 'utf8'); } catch (e) { const second = new NodeRSA(pem, 'pkcs8-private'); try { dataObj = second.decrypt(data, 'utf8'); } catch (error) { reject("Rsa 解密失败"); } } resolve(dataObj); }); }); } Aes解密 // Aes 解密 aesDecrypt(data, customer_key = "") { var key = CryptoJS.enc.Utf8.parse(customer_key || this.config.secret.aes.key); var decrypt = CryptoJS.AES.decrypt(data, key, { "mode": CryptoJS.mode.ECB, "padding": CryptoJS.pad.Pkcs7 }); return CryptoJS.enc.Utf8.stringify(decrypt); } 处理中间件

新建解密处理的中间件app/middleware/security.js

module.exports = () => { return async function (ctx, next) { const { helper, request } = ctx; const { ajaxMsg } = helper; const { key, data } = request.body; if (!key || !data) return ajaxMsg(ctx, "-1", "请求参数错误", null, 400); let rawKey; try { rawKey = await helper.rsaDecrypt(key); } catch (error) { return ajaxMsg(ctx, "-1", "密钥解析失败", null, 400) } if (!rawKey) return ajaxMsg(ctx, "-1", "密钥解析失败", null, 400); const decryptData = JSON.parse(helper.aesDecrypt(data, rawKey)); if (!decryptData) return ajaxMsg(ctx, "-1", "安全验证未通过", null, 400); request.body = decryptData; await next(); }; }; 路由中使用 const { router, controller, middleware } = app; const security = middleware.security(); // 接口参数加密 router.post('/common/user/login', security, controller.common.user.login); // 登录

以上就是JS前端接口请求参数混淆方案分享的详细内容,更多关于JS前端接口请求参数混淆的资料请关注易知道(ezd.cc)其它相关文章!

推荐阅读

    opporeno8参数配置及价格

    opporeno8参数配置及价格,面部,亿元,Oppo的荣誉2020年1月4日,接近屏幕关闭传感器是否支持双卡:支持oppor11splus什么时候上市的Oppo R11S P

    魅蓝note6性能参数有哪些

    魅蓝note6性能参数有哪些,摄像头,蓝牙,魅蓝note6性能参数有哪些魅力蓝色Note6最好拍照。电池寿命更长。蓝色Note6使用高通 snapdragon 625

    公共CPU接口类型的详细描述

    公共CPU接口类型的详细描述,,我们知道CPU是电脑的大脑, CPU的处理速度直接决定电脑的性能, 那你知道CPU发展到现在, 都那些CPU接口类型吗.

    FM1和AM3接口将在今年年底前淘汰

    FM1和AM3接口将在今年年底前淘汰,,据来自主板制造商的消息,AMD将开始逐步淘汰Socket FM1和Socket AM3接口从本月开始的处理器,最后消失在第

    设置总账参数|用友u8设置总账参数

    设置总账参数|用友u8设置总账参数,,1. 用友u8设置总账参数1、首先要点开数据权限控制设置;2、选择想要设置控制的单据;3、打开后看到左上角

    js设置div的边框|怎样给div设置边框

    js设置div的边框|怎样给div设置边框,,1. 怎样给div设置边框1、首先新建一个html文件,输入基本的内容,这里设置一个div,并把它的class设置为de

    Windows8开发版系统高清系统接口图

    Windows8开发版系统高清系统接口图,,今天,微软发布,目前由开发者体验版Windows 8操作系统为整个英文系统,Windows 8带来了一系列新功能,没有折

    csgo参数设置|csgo怎么保存

    csgo参数设置|csgo怎么保存,,csgo怎么保存第一步下载csgo的官方版本。然后再下载一个5e对战平台,PS:5e的账号和csgo的账号不是一个账号。第

    移动apn设置|移动apn设置参数

    移动apn设置|移动apn设置参数,,移动apn设置参数1、打开手机系统设置界面应用,点击页面中的“移动网络”设置选项。2、进入移动网络设置页面