JS前端接口請(qǐng)求參數(shù)混淆方案分享
寫在前面
在一些接口請(qǐng)求的場(chǎng)景中,我們希望攜帶的數(shù)據(jù)不希望是以明文的方式提交的,也就是需要對(duì)參數(shù)做一些混淆或者加密處理,后端拿到數(shù)據(jù)后再進(jìn)行解密,得到真實(shí)數(shù)據(jù)。
其目的是為了保護(hù)數(shù)據(jù)的安全,以及提高被識(shí)破成明文的門檻。在例如用戶登錄的接口請(qǐng)求中,如果賬號(hào)和密碼是以明文傳輸?shù)?,?huì)容易導(dǎo)致一些安全性問(wèn)題,同時(shí),我們也不希望誰(shuí)都可以偽造參數(shù)對(duì)接口發(fā)起請(qǐng)求,因此前端參數(shù)混淆非常有必要,這里分享一個(gè)自用的方案,其目的在于:
- 防止信息泄露
- 防止參數(shù)被隨意篡改
- 提高應(yīng)用安全性和穩(wěn)定性
對(duì)于加密或者混淆的處理方式的選擇,例如base64、MD5等安全性不高或者不可逆的算法,不適用于我們這個(gè)場(chǎng)景,而是可以利用Aes和Rsa兩者結(jié)合的方式,實(shí)現(xiàn)數(shù)據(jù)的加解密。
什么接口的參數(shù)需要做處理
從安全角度出發(fā),這里一致認(rèn)為,只要是對(duì)數(shù)據(jù)庫(kù)有新增、修改、刪除操作的,一律需要做混淆/加密,這一類接口,多為post、put、delete等請(qǐng)求。
而對(duì)于get請(qǐng)求,如果是規(guī)范化的接口,一般只是獲取數(shù)據(jù),不會(huì)對(duì)數(shù)據(jù)庫(kù)有直接的操作,故不需要做參數(shù)處理。
當(dāng)然還有post請(qǐng)求和一些文件上傳等,不希望做參數(shù)處理的接口,需要特殊處理,以及在開(kāi)發(fā)環(huán)境中,為了方便調(diào)試,也不需要做處理。
參數(shù)處理
因?yàn)?code>Rsa在處理數(shù)據(jù)量較大時(shí)優(yōu)勢(shì)不明顯,適合處理數(shù)據(jù)量較小的場(chǎng)景,所以對(duì)參數(shù)數(shù)據(jù)的處理采用了Aes加密,而參與加密的密鑰key則采用Rsa非對(duì)稱加密提高破解難度。
涉及到的相關(guān)依賴及其版本號(hào):
"crypto-js": "^4.1.1",
"jsencrypt": "^3.2.1"
這里使用的方案是,先將原始數(shù)據(jù)處理為query形式的字符串,然后將其使用隨機(jī)字符串作為密鑰,參與Aes加密,并截取特定的字符串,作為原始密鑰,再進(jìn)行一次Aes加密,最后將原始密鑰使用與后端約定好的公鑰進(jìn)行Rsa加密處理,具體流程和算法如下:
- 對(duì)參數(shù)排序、提取
query字符串處理 - 將提取的字符串利用隨機(jī)字符串加密并截取,得到密鑰
- 利用密鑰對(duì)原始參數(shù)進(jìn)行
Aes加密 - 將密鑰進(jìn)行
Rsa非對(duì)稱加密 - 輸出最終的
data和key
/**
* 加密請(qǐng)求數(shù)據(jù)
* @param {Object} rawData 原始數(shù)據(jù)
* @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 位隨機(jī)碼對(duì)稱加密,再?gòu)牡?3 位開(kāi)始獲取 16 位原始密鑰
const rawKey = aesEncrypt(result, randomString(16)).substr(3, 16);
// 輸出最后的加密參數(shù)
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;
}
簽名驗(yàn)證
如果需要進(jìn)一步加強(qiáng)防篡改,可以在處理參數(shù)的時(shí)候,通過(guò)一定算法得出一個(gè)簽名sign值,一并提交到后端,后端解密后,也通過(guò)同樣的算法將解密后的數(shù)據(jù)生成一個(gè)sign值,與提交的作對(duì)比,判斷是否為合法的請(qǐng)求來(lái)源。 這里不做詳細(xì)介紹。
處理時(shí)機(jī)
我們需要在請(qǐng)求攔截的時(shí)候?qū)?shù)做混淆處理,這里只針對(duì)post請(qǐng)求以及非本地環(huán)境,另外為了方便調(diào)試,除了開(kāi)發(fā)環(huán)境外 在實(shí)例上還增加了uncrypt來(lái)標(biāo)識(shí)哪些接口可以不參與處理。
// 請(qǐng)求攔截
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)
)
后端實(shí)現(xiàn)
前端參數(shù)處理后以data和key組成的對(duì)象提交至后端,服務(wù)層接受后進(jìn)行解密,這里以 Egg.js 的實(shí)現(xiàn)為例子。 涉及依賴及其版本號(hào):
"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", "請(qǐng)求參數(shù)錯(cuò)誤", 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", "安全驗(yàn)證未通過(guò)", null, 400);
request.body = decryptData;
await next();
};
};
路由中使用
const { router, controller, middleware } = app;
const security = middleware.security(); // 接口參數(shù)加密
router.post('/common/user/login', security, controller.common.user.login); // 登錄以上就是JS前端接口請(qǐng)求參數(shù)混淆方案分享的詳細(xì)內(nèi)容,更多關(guān)于JS前端接口請(qǐng)求參數(shù)混淆的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信小程序頁(yè)面開(kāi)發(fā)注意事項(xiàng)整理
這篇文章主要介紹了微信小程序頁(yè)面開(kāi)發(fā)注意事項(xiàng)整理的相關(guān)資料,需要的朋友可以參考下2017-05-05
使用?JS?復(fù)制頁(yè)面內(nèi)容的三種方案
這篇文章主要為大家介紹了使用?JS?復(fù)制頁(yè)面內(nèi)容的三種方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
微信小程序 input輸入框詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了微信小程序 input輸入框詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01
JS前端使用canvas搞一個(gè)手勢(shì)識(shí)別
這篇文章主要為大家介紹了JS前端使用canvas搞一個(gè)手勢(shì)識(shí)別的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
前端canvas中物體邊框和控制點(diǎn)的實(shí)現(xiàn)示例
這篇文章主要為大家介紹了前端canvas中物體邊框和控制點(diǎn)的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
js c++ vue方法與數(shù)據(jù)交互通信示例
這篇文章主要為大家介紹了js c++ vue方法與數(shù)據(jù)交互通信示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08

