如何解决java老开源系统登录密码在浏览器开发工具里暴露的问题
网上找的若依开源框架,拿来登录密码直接暴露在浏览器开发工具里面,拿这个东西直接给甲方需求方,肯定过不了关的。
甲方假如哪天发现了这个问题,肯定会要求退工程款,拿来主义没问题,但是得优化。
包括另一个开源项目,号称用了Apache的Shiro安全框架,安全级别很高,但是也有同样的问题
开源老框架多半是个半成品。
但是也有最新的新框架,用最新java17,以及typeScript跑起来的项目就不存在这个问题,把账户和密码一起进行了加密传输:
但是我接到甲方的项目,用的是若依老框架开发的,业务逻辑写了一大堆代码,都在老框架里面,写完了,就回头看发现了这个密码暴露的问题。
客户虽然不提,但是我不能不解决。
项目中vue有个加解密原码
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
// 密钥对生成
http://web.chacuo.net/netrsakeypair
const publicKey = 'xxx'
const privateKey = 'xxx'
// 加密
export function encrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(publicKey) // 设置公钥
return encryptor.encrypt(txt) // 对数据进行加密
}
// 解密
export function decrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPrivateKey(privateKey) // 设置私钥
return encryptor.decrypt(txt) // 对数据进行解密
}
但是这个只能在前端加解密,是为了保存在cookie里是密文。
发送登录请求的方法:
// 调用action的登录方法
userStore.login(loginForm.value).then(() => {
const query = route.query;
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur];
}
return acc;
}, {});
router.push({ path: redirect.value || "/", query: otherQueryParams });
}).catch(() => {
loading.value = false;
loginForm.value.code = null
loginForm.value.password = reverseData.reverse().join('');
// 重新获取验证码
if (captchaEnabled.value) {
getCode();
}
});
调用这个登录方法之前必须要给loginForm的password属性加密处理。
网上搜索“js加密密码,java解密”找了两篇博文
https://link.csdn.net/?from_id=146127027&target=https%3A%2F%2Fblog.51cto.com%2Fu_16213323%2F13179616
https://blog.csdn.net/qq_45089709/article/details/146044148
这两篇博文我用过了,不能说不能用,但是不适合我这个项目,我这个项目是用的java8,博客里要求可能是更高版本的java,我用的时候,后台直接报错,直接用不了。
我有想过把业务逻辑往新框架里面搬,但是如此耗费的工程,至少还要花一个月时间,甲方马上就结算工程款了,我可不想还要等一个月拿到钱。
再说甲方给的钱本来就不多,为了尽快变现,我必须另外再想办法。
甲方给的钱不多,就自己想一个简单加解密方法。
首先加密解密要有密码本,密码本就是一串字符串
在vue项目定义一个有16个字符常量字符串
const key = "xxxxxxxxxxx1234";
其次要有密码坐标,再定义个子元素是0到15数字的数组,数字顺序要打乱。
const indexArayy = [1,10,4,8,3,0,15,13,11,12,2,9,7,5,6,14]
再者必须要有密码规则,前段密码校验必须是6到16位,不能超过密码本的字符长度
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" },
{
min: 6,
max: 16,
message: '密码长度在 6 到 16 个字符之间',
trigger: 'blur'
}
],
最后生成密文,生成的密文里面开头存放原文密码的字符串长度,
这样后端java就知道循环多少次进行解密了
先把原文密码字符串顺序倒置,变成字符串数组
let reverseData = loginForm.value.password.split('').reverse();
密码本(这里定义为变量key),也变成字符串数组
let split = key.split('');
最后将密码本里的字符串按照密码坐标进行替换,一个个替换为已经倒置的密码原文
for (let i = 0; i < reverseData.length; i++) {
split[indexArayy[i]] = reverseData[i];
}
如何保存密码长度呢?
为了保证密码长度是固定的两位数字,将密码长度乘以2,因为6*2=12,16*2=32,保证密码长度存储都是两位数,然后拼接到上面生成的密文的开头位置。
loginForm.value.password = reverseData.length*2+split.join('');
后端解密代码
先定义密码坐标数组常量,要跟前端一致
int [] indexArray = {1,10,4,8,3,0,15,13,11,12,2,9,7,5,6,14};
密码解密代码:
String password = loginBody.getPassword();
//后端密码解密
String substring = password.substring(0, 2);
String password1 = password.substring(2);//获取加密后的密码
int pwdLen= Integer.valueOf(substring)/2;//获取密码长度
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < pwdLen; i++) {
stringBuffer.append(password1.charAt(indexArray[i]));
}
String decrypted = stringBuffer.reverse().toString();//倒置字符串获取密码原文
因为甲方给了钱不多,就这样弄个简单的凑合用,虽然密码混在密文中还能看见,但是也很难破解。
现在要做的就是隐藏密码坐标,这个就相对简单了,
const indexArayy = [encryptNumber(2),
encryptNumber(5),
encryptNumber(13),
encryptNumber(8),
encryptNumber(3),
encryptNumber(0),
encryptNumber(15),
encryptNumber(4),
encryptNumber(11),
encryptNumber(12),
encryptNumber(10),
encryptNumber(9),
encryptNumber(7),
encryptNumber(1),
encryptNumber(6),
encryptNumber(14)]
对坐标中的每个元素进行加密,
然后取的时候再进行解密,
split[decryptNumber(indexArayy[i])] = reverseData[i];
js加密解密就很简单,逻辑自己写,网上随便找一个,推荐base64;
这样前端js混淆部署,就看不到坐标数字,这样就非常安全了。
原文密码123456加密后的密文为121h5246an3yue1234,如图
这样基本就解决问题了,但是呢Chrome浏览器里面有个自动存储密码功能,它存储的是密码的密文,所以也对系统起到保护作用,假如别人想通过Chrome浏览器找到密码原文也是找不到的。