实战:前端接口请求参数混淆|8月更文挑战

5,290 阅读3分钟

分享一下前东家的技术团队,在请求接口参数的处理上做得一些混淆处理。

安全性是衡量一个项目能否稳定运行的必备条件之一,同时也是一个技术团队的工作质量考核指标, 虽然从前端的角度出发,没有根本的安全性可言,毕竟前端的所有东西都是对外开放的,不过前端开发者可以通过增加破防门槛,进而提高项目整体安全性。

什么接口需要做参数处理

在接口请求中,尤其是类似登录、注册等包含敏感字段和数据的接口,如果是以明文的方式将数据发送给后端,那会带来极大的安全隐患。所以,要想实现接口参数的非明文传送,就得对参数做一些处理,增加恶意攻击的门槛。

我的建议是,所有的 POST 请求都需要做处理。在标准的接口规范中,POST 请求会处理数据并返回结果,而 GET 更多是获取数据,而非操作数据。

如何处理参数使得破解门槛更高

我们最开始使用的是对数据做加密处理,先从后端获取加密密钥,请求的时候再通过密钥对数据加密,接口获取到数据后再根据密钥进行解密。

这种方式显然很笨,而且并没有增加被攻击的门槛,只是保证了非明文数据的传输。攻击者依然可以通过接口获取密钥,再根据密钥获取/解密敏感数据,从而实现攻击的目的。

加密不可行,那就前后端一起制定一种规则,将请求数据做混淆处理,后端根据规则解析出原始数据,那么只要保证这种规则不泄露出去,并不定期更新混淆规则,即可以一定程度增加参数破解和接口暴力请求的门槛。

具体规则

1、 得到业务参数(JSON格式)

2、 将业务参数进行加密 (规则:所有的key按照 a-z的顺序进行排列 然后用&进行拼接,然后md5得到字符串sign,并把里面的小写字母全部转成大写)

3、把生成的 sign 进行截取(去掉前三位和后五位)得到最终的sign

4、把最终的 sign 添加到业务请求参数中 key固定为 sign,value为第3步生成的值

5、随机产生一个16位的字符串(数字+大学字母),作为原始秘钥

6、把4生成的值作为明文,5生成的值作为最终秘钥,然后进行AES加密 得到最后的数据

7、把6生成的数据作为 value,key 固定为 data,然后再把原始的k(截取和转大写之前的值)作为value,key为k

示例:

image.png

前端实现

安装 CryptoJS


npm install crypto-js

引入CryptoJS和MD5JS

MD5JS 库需额外下载引入。


import cryptoJs from 'crypto-js';

import {

  hex_md5

} from "./md5";

混淆算法


/**

 * apiEncode 请求参数混淆

 * @param {Object} params 

 */

export const apiEncode = (params) => {

  // 字典排序

  var arr = Object.keys(params).sort();

  var result = {};

  arr.map(m => {

    result[m] = params[m];

  });

  // 处理成query

  var str = [];

  for (var p in result)

    if (result.hasOwnProperty(p)) {

      str.push(encodeURIComponent(p) + "=" + encodeURIComponent(result[p]));

    }

  result = str.join("&");

  // MD5加密并转换大写,截取并拼接到原始参数

  let sign = hex_md5(result).toUpperCase();

  params.sign = sign.substring(3, sign.length - 5);

  // 生成KEY,加密处理

  let key = generateMixed(16);

  let enc = cryptoJs.AES.encrypt(JSON.stringify(params), cryptoJs.enc.Utf8.parse(key), {

    mode: cryptoJs.mode.ECB,

    padding: cryptoJs.pad.Pkcs7

  });

  return JSON.stringify({

    data: enc.toString(),

    k: key

  });

}

/**

 * 随机生成指定长度的字符串

 * @param {Int} n 长度

 */

export const generateMixed = (n) => {

  let chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

  let res = "";

  for (let i = 0; i < n; i++) {

    let id = Math.ceil(Math.random() * 35);

    res += chars[id];

  }

  return res;

}