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前端接口请求参数混淆的资料请关注我们其它相关文章!

(0)

相关推荐

  • Vue+Mockjs模拟curd接口请求的示例详解

    在前后端分离的项目中常常会遇到当前端页面开发完成但是后端接口还没好,暂不支持联调的情况下,一般我们会用到mock数据这边简单说一下最常见且经常会遇到的curd接口模拟注:这边可以和后端先约定好接口路径以及入参返参的字段,避免二次修改 1.安装依赖,新建js文件,在文件中导入mock.js,模拟列表数据 yarn add mockjs const Mock = require("mockjs") const list = [] const length = 18 for (let i =

  • ajax请求后台接口数据与返回值处理js的实例讲解

    ajax的代码,用的是jquery的 ajax: $.ajax({ url: "/test.php",//后台提供的接口 type: "post", //请求方式是post data:{"type":"1", //这是你要传给后台的data值 "t":"c4552111" }, dataType: "json", //数据类型是json型 success: funct

  • VueJs 搭建Axios接口请求工具

    axios 简介 axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征: 从浏览器中创建 XMLHttpRequest 从 node.js 发出 http 请求 支持 Promise API 拦截请求和响应 转换请求和响应数据 取消请求 自动转换JSON数据 客户端支持防止 CSRF/XSRF 上一章,我们认识了项目的目录结构,以及对项目的目录结构做了一些调整,已经能把项目重新跑起来了.今天我们来搭建api接口调用工具Axios.Vue本身是

  • java以json格式向后台服务器接口发送请求的实例

    代码如下: import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import net.sf.json.JSONObject; public class InterfaceRequest {

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

    目录 写在前面 什么接口的参数需要做处理 参数处理 Aes加密 Rsa加密 签名验证 处理时机 后端实现 Rsa解密 Aes解密 处理中间件 路由中使用 写在前面 在一些接口请求的场景中,我们希望携带的数据不希望是以明文的方式提交的,也就是需要对参数做一些混淆或者加密处理,后端拿到数据后再进行解密,得到真实数据. 其目的是为了保护数据的安全,以及提高被识破成明文的门槛.在例如用户登录的接口请求中,如果账号和密码是以明文传输的,会容易导致一些安全性问题,同时,我们也不希望谁都可以伪造参数对接口发起

  • 关于前端ajax请求的优雅方案(http客户端为axios)

    前言 AJAX,Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案. 异步的JavaScript: 使用 [JavaScript语言] 以及 相关[浏览器提供类库] 的功能向服务端发送请求,当服务端处理完请求之后,[自动执行某个JavaScript的回调函数]. PS:以上请求和响应的整个过程是[偷偷]进行的,页面上无任何感知. 下面话不多说了,来一看看本文的正文. 本文http客户端为axios 先讲

  • js前端图片加载异常兜底方案

    目录 背景 <img>加载错误解决方案 内联事件 全局img添加事件 利用error事件捕获 替换src方式的最优解 CSS处理的最优解 <img>加载超时解决方案 嗅探切换Domain(CNAME) 服务端下发Domain(CNAME) background-image加载异常解决方案 自定义事件 嗅探加载情况 添加事件捕获 背景 网络环境总是多样且复杂的,一张图片可能会因为网路状况差而加载失败或加载超长时间,也可能因为权限不足或者资源不存在而加载失败,这些都会导致用户体验变差,

  • java接口返回参数按照请求参数进行排序方式

    目录 java接口返回参数按照请求参数进行排序 排序 java通过接口进行排序 描述 知识点 1.Comparable接口 2.Comparator接口 java接口返回参数按照请求参数进行排序 在项目实际开发中可能遇到过这种问题,接口请求参数顺序是[a,b,c],结果返回的数据是[bObject,cObject,aObject],导致这种原因可能是底层采用了设计模式,或者是表拼接查询,本文主要就是为了实现这种功能,采用流的方法 代码实现 import lombok.Data; import j

  • Spring MVC请求参数与响应结果全局加密和解密详解

    前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数据报文包括三个部分: 1.timestamp,long类型,时间戳. 2.data,String类型,实际的业务请求数据转化成的Json字符串再进行加密得到的密文. 3.sign,签名,生成规则算法伪代码是SHA-256(data=xxx&timestamp=11111),防篡改. 为了简单起见,加密和解密采用AES,对称秘钥

  • 前端HTTP发POST请求携带参数与后端接口接收参数的实现

    目录 HTTP的GET请求与POST请求 常见HTTP内容类型Content-Type 后端收参方式-前端对应传参方式 application/json multipart/form-data 总结 参考 HTTP的GET请求与POST请求 HTTP请求方式有GET.POST.PUT.DELETE等八种,最常见的就是GET.POST,下面说一下GET请求,很简单. GET是按照规定参数只能写在URL里面(虽然可以有请求体但是不符合规定),没有请求体也就是data,那传就非常简单了,前端就是字符串

  • Android WebView通过动态的修改js去拦截post请求参数实例

    需求背景: 需要在用户点击提交按钮的时候拦截用户提交的数据. 遇到的问题: 1.页面不是自家前端做的,不能修改网页中的代码 2.要拦截的请求不是get请求,而是一个post请求 (难点在于:如果拦截的请求是get请求的话,我只需要拿到url,将后面拼接的参数键值对取出来就好了,但是post请求的参数键值对我们是看不到的...) 解决重点: 重写webViewClient的shouldInterceptRequest这个方法 1.这个方法是API21以后才出现的,还有一个过时的方法也要重写,不要忘

  • js前端解决跨域的八种实现方案

    由于同源策略的限制,满足同源的脚本才可以获取资源.虽然这样有助于保障网络安全,但另一方面也限制了资源的使用. 那么如何实现跨域呢,以下是实现跨域的一些方法. 一.jsonp跨域 原理:script标签引入js文件不受跨域影响.不仅如此,带src属性的标签都不受同源策略的影响. 正是基于这个特性,我们通过script标签的src属性加载资源,数据放在src属性指向的服务器上,使用json格式. 由于我们无法判断script的src的加载状态,并不知道数据有没有获取完成,所以事先会定义好处理函数.服

  • JS前端并发多个相同的请求控制为只发一个请求方式

    目录 描述如下 老版本cachedAsync 进阶版本 测试cacheAsync 快速搭建一个服务器 客户端 提示 描述如下 同时发多个相同的请求,如果第一个请求成功,那么剩余的请求都不会发出,成功的结果作为剩余请求返回 如果第一个请求失败了,那么接着发编号为2的请求,如果请求成功,那么剩余的请求都不会发出,成功的结果作为剩余请求返回 如果第二个请求失败了,那么接着发编号为3的请求,如果请求成功,那么剩余的请求都不会发出,成功的结果作为剩余请求返回 ...以此递推,直到遇到最坏的情况需要发送最后

  • 前端JS图片懒加载原理方案详解

    目录 背景 原理 方案 方案一:img的loading属性设为“lazy” 使用方法 优点 兼容性 缺点 方案二:通过offsetTop来计算是否在可视区域内 优化 优点 缺点 方案三:通过getBoundingClientRect来计算是否在可视区域内 方案四:使用IntersectionObserver来判断是否在可视区域内 兼容性 优点 缺点 问题 布局抖动 响应式图片 SEO不友好 插件 背景 懒加载经常出现在前端面试中,是前端性能优化的常用技巧.懒加载也叫延迟加载,把非关键资源先不加载

随机推荐