<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在一些介面請求的場景中,我們希望攜帶的資料不希望是以明文的方式提交的,也就是需要對引數做一些混淆或者加密處理,後端拿到資料後再進行解密,得到真實資料。
其目的是為了保護資料的安全,以及提高被識破成明文的門檻。在例如使用者登入的介面請求中,如果賬號和密碼是以明文傳輸的,會容易導致一些安全性問題,同時,我們也不希望誰都可以偽造引數對介面發起請求,因此前端引數混淆非常有必要,這裡分享一個自用的方案,其目的在於:
對於加密或者混淆的處理方式的選擇,例如base64
、MD5
等安全性不高或者不可逆的演演算法,不適用於我們這個場景,而是可以利用Aes
和Rsa
兩者結合的方式,實現資料的加解密。
從安全形度出發,這裡一致認為,只要是對資料庫有新增、修改、刪除操作的,一律需要做混淆/加密,這一類介面,多為post
、put
、delete
等請求。
而對於get
請求,如果是規範化的介面,一般只是獲取資料,不會對資料庫有直接的操作,故不需要做引數處理。
當然還有post
請求和一些檔案上傳等,不希望做引數處理的介面,需要特殊處理,以及在開發環境中,為了方便偵錯,也不需要做處理。
因為Rsa
在處理資料量較大時優勢不明顯,適合處理資料量較小的場景,所以對引數資料的處理採用了Aes
加密,而參與加密的金鑰key
則採用Rsa
非對稱加密提高破解難度。
涉及到的相關依賴及其版本號:
"crypto-js": "^4.1.1",
"jsencrypt": "^3.2.1"
這裡使用的方案是,先將原始資料處理為query
形式的字串,然後將其使用隨機字串作為金鑰,參與Aes
加密,並擷取特定的字串,作為原始金鑰,再進行一次Aes
加密,最後將原始金鑰使用與後端約定好的公鑰進行Rsa
加密處理,具體流程和演演算法如下:
query
字串處理Aes
加密Rsa
非對稱加密data
和key
/** * 加密請求資料 * @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 加密 * @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 加密 */ 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) )
前端引數處理後以data
和key
組成的物件提交至後端,服務層接受後進行解密,這裡以 Egg.js 的實現為例子。 涉及依賴及其版本號:
"crypto-js": "^4.1.1",
"node-rsa": "^1.1.1"
// 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 解密 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前端介面請求引數混淆的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45