前情
为什么要搞,要这么做? 难道只用HTTPS不够吗?
如果应用只使用HTTPS,那还真不够用!
原因:攻击者可以模拟客户端操作,枚举敏感用户信息、攻击应用。譬如,管理界面只要是放在互联网中,那么攻击者
就能够通过网络直接访问。只要是能访问,那么客户端与服务端的链接通道就找到了,并打开了。在数据还没有进入到互联网
环境前,攻击者可利用三方工具对模拟真实的请求,并对其拦截、抓包、修改,如此变绕开了前端的基础校验。
对于一些特殊敏感数据,例:用户表,主键id(userId)。这些数据如果通过HTTP、互联网环境传输到服务器端,而恰巧主键生成策略是有规律可循(bigint自增、某种规律性的公式)的,那么攻击者可以通过枚举的方式,高频繁修改请求包信息,请求服务端。以此来获取一些敏感的数据信息。
思路
攻击者能够肆无忌惮的攻击服务器,归根结底是因为两点:
- 请求被抓包,对包信息
修改; - 修改后的包信息可以直接发送给服务端;
对于抓包我们无法处理,但是我们可以对修改的信息做些手脚!这就用到了数字签名,加签验签!
数字签名的注意事项:
- 因为是全局性处理,所以必须要考虑性能损耗!
- 数字签名不能被攻击者复制。否则数字签名就无效了!
- 对传输的请求不要任何脏影响,也就是说请求体数据必须保证
完整性!
针对以上思考,采用的方案:
- 签名算法使用MD5(AES、国密、甚至RSA都可以);
- 考虑到MD5的易破解性,所以我们加slat (必须包含特殊字符,确保安全);
- 服务端使用Filter对请求做合法判断处理;
- 因为HTTP方法有多种,Content-Type存在多种。所以我们采用
String格式做签名**(保证数据顺序的一致性)**;
开始
环境介绍:
- 前端框架:Vue 4.5.10,使用Axios作为网络请求库;
- 包管理工具:npm 6.9.0
- 后端框架:SpringBoot 2.4.1
前端开发
安装加密组件
# npm 安装加密组件
cnpm install crypto-js
说明:用其他组件也可以,或者自己手写都行。关键是能保证前后端的验签算法保持一致即可。
crypto.js (封装 util)
import CryptoJS from 'crypto-js'
/**
* 加盐MD5加密,可以作为加签算法
* @param {加密对象} obj
* @param {*} slat
*/
export function MD5(obj, slat) {
if (!obj) {
return obj;
}
// 转换成字符串
let str = JSON.stringify(obj);
if (!!slat) {
// 拼接slat
str = str.concat(slat)
}
// 关键点:将所有上引号替换成空,理由:后台Filter获取的参数全部为String,所以为了保证格式一致,取消掉上引号
str = str.replaceAll(/"/g, "");
// JSON数据存在特殊符号[和]
str = str.replaceAll("\[", "");
str = str.replaceAll("]", "");
// MD5加密后,转成字符串
return CryptoJS.MD5(str).toString();
}
关键点:
- 为了保证后端在验签时,对数据的还原保持一致性,所以需要对特殊字符做处理(删除)
- 加盐的公式,我们可以任意自定义,不变的是,保证slat具有一定的复杂性
Axios全局request拦截
import axios from 'axios'
/**
* 需要加签、验签的路径集合
* 例:"/user",将匹配以"/user"开头的所有API
*/
const blackBeginUrl = ["/user", "/role"]
// HTTP request拦截
axios.interceptors.request.use(
(config) => {
const meta = config.meta || {
}
const isToken = meta.isToken === false
if (getToken() && !isToken) {
config.headers['Authorization'] = getToken()
}
// 判断是否需要对路径做加签操作
let needSign = false;
for (let blackUrl of blackBeginUrl) {
if (config.url.indexOf(blackUrl) != -1) {
needSign = true;
break;
}
}
if (needSign) {
// 这里默认post请求的Content-Type:application/json (可以和开发者做好约定)
let requestData = "";
if (config.method === "get" && !!config.params) {
requestData = config.params
} else if (config.method == "post" && !!config.data) {
requestData = config.data
}
// 时间戳,作为slat的必备组成之一
const timestamp = Date.parse(new Date())
config.headers['Timestamp'] = timestamp
// 随机字符串,作为slat的必备组成之一
const randomStr = "K:*C8bw6zJ"
// slat = 时间戳 + 随机字符串 (自定义slat公式)
const slat = timestamp.concat(randomStr);
const signature = MD5(requestData, slat);
config.headers['Signature'] = signature;
}
return config;
},
(error) => {
tryHideFullScreenLoading()
return Promise.reject(error)
},
)
随机生成网站:在线生成随机字符串
说明:如果业务上对个别API加签,可以仿照上述代码的方式,定义需要验签的API黑名单。
- 效果

通过上图可以看到,我们对本次请求成功生成了数字签名。Headers key为 Signature。
至此,

本文探讨了仅使用HTTPS的安全局限性,并提出了一种通过数字签名增强安全性的方法。详细介绍了前后端如何实现请求签名验证,包括使用MD5算法加盐、设置过滤器及异常处理。
最低0.47元/天 解锁文章
2498





