一、MD5 是什么 ?
百度百科摘要:
MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。
二、为什么要进行加密存储?
举个栗子:
我国落实信息安全等级保护制度以来的首例“罚单” —— CSDN网站用户数据泄露案
2011年12月21日上午有黑客在网上公开了开发者技术社区CSDN网站的用户数据库。包括600余万个注册邮箱账号和与之对应的明文密码。
什么是明文密码?
. 下面是我在mongoDB中生成的一些信息,可以直观的看到用户的姓名所对应的密码是直接可识别的,也就是说,这里的密码即是用户在登录过程中输入的真实密码。最终存储在数据库是未有进行任何加密操作的。首先这样明文存储的数据在未泄漏的前提下,DBA等后台工程师是可以直观识别用户及用户密码的,其次一旦发生数据泄露,结果可想而知。
这是当时遭到曝光和外泄的明文注册邮箱账号和密码
三、什么是 MD5 加密?
栗子:这样的密码就是经过加密存储的
此时有朋友可能已经觉得虽然密码看上去和平常的"123456"这样的密码有了很大的差别,但是似乎还是明文的样子,而且用户登录难道也要用这样毫无关联的字符串吗?带着这样的疑问,我们就得好好看看MD5是如何加密的。
四、MD5加密特点
这里使用的MD5加密是函数型加密,因此每次的加密结果一定相同,没有随机位。
不管加密的文字长或短永远都是32位的混合字符串
MD5没有反函数破解的可能,也就是说MD5在数学上无法破解(无法反向破解)
六、代码实现
服务端接收用户的登录信息,我是通过Node.js来获取的并加密的。
首先Node使用MD5加密数据,不用下载模块,直接引入crypto模块即可。
let crypot = require("crypto"); // 加密操作模块
引入模块后,创建hash,使用update和digest加密数据
let md5 = crypot.createHash("md5");
let password = md5.update(pwdInit).digest("base64");
// pwdInit 用户原始登录密码
// password 加密后的MD5字符串
因此,MD5在这里加密存储数据的应用,其实表层逻辑很简单,即:当有用户注册时将用户的密码用MD5的方式加密"pwdInit"得到的"password"存储在数据库(例如:123456 => E10ADC3949BA59ABBE56E057F20F883E),这样用户真实的密码就不会别直接暴露出来。那么当有用户登录时同样,他只需要输入其原始设置的密码登录即可,原始的登录密码在服务端会通过MD5的方式转换为32位字符串格式,再找到对应用户名信息后再比对加密后的32位字符串密码,全部相同则意味着登录成功。至此,这样的加密数据即使暴漏出来,黑客是无法知道密文背后的真实密码的。
案例服务端代码:
let express = require("express");
let app = express();
let crypot = require("crypto"); // 加密操作模块
let db = require("./Mongo_DIY_modules") // Mgo 自定义封装模块
// 登录页面
app.use("/", express.static("./public"));
// 登录路由
app.get("/checklogin", (req, res) => {
let username = req.query.username;
let userpwd = req.query.userpwd;
// 根据填写的姓名,去数据库寻找该文档/集合
// 如过用户名存在,则读取密码进行比对,判断登录成功或失败
db.find({
"dbName": "homeDB", // 数据库名
"collectionName": "loginMD5", // 集合名
"json": {
"name":username // 查询条件
},
"callback": function(err, result) { // 查询结果
if(result.length == 0){
res.send("用户名输入有误");
} else {
if(result[0].pwd === MD5(userpwd)){ // 比对加密后的32位字符串密码
res.send(result[0].name + " 登录成功!")
} else {
res.send("密码错误");
}
}
}
});
});
// 注册路由 (未作用户名同名判断)
app.get("/regist", (req, res) => {
console.log(req.query);
db.insertOne("homeDB", "loginMD5", {
"name":req.query.username,
"pwd":MD5(req.query.userpwd)
}, (err, result) => res.send(result));
});
// 运行服务器
app.listen(3000);
function MD5(pwd) {
let md5 = crypot.createHash("md5");
let password = md5.update(pwd).digest("base64");
return password;
}
七、关于MD5的解密
一般的破解工具都是字典模式。通过列出大量的"明文-密码"的对应字典找到明码。也就是通过穷举字符组合的方式,创建了明文密文对应查询数据库。
什么是穷举字符组合的方式?
可以看下常见的在线MD5加密和解密的网站,如何做的:
MD5加密:
可以看到加密的数据不论长短复杂,都可以加密成32位的字符串且每次的加密结果一定相同(MD5加密是函数型加密)
MD5解密:
通过在线网站的解密可以发现一般的数字是可以轻易的被破解的,而复杂的字符组合则难以破解。
所以,所谓的穷举解密实际上就是创建了明文密文对应查询数据库,庞大的数据先被加密成对应的密文存储起来,当有一串类似的MD5密文出现时,则去字典库中遍历查找,找到同样的密文,对应的明文自然就发现了。这种解密过程即是——穷举解密。
这是某个在线MD5揭秘网的解密范围
组合形式:
密文类型:
可以说解密网站对MD5的解密范围还是很高的。因此 通常我们使用MD5加密最好不要只使用一层加密,可以对明文使用MD5进行多层加密及添加其它字符相结合,以达到高度破解效果。比如:
let crypot = require("crypto");
// 这是MD5加密的封装方法
function MD5(pwd) {
let md5 = crypot.createHash("md5");
let password = md5.update(pwd).digest("base64");
// base64 是网络上最常见的用于传输8Bit字节码的编码方式之一
return password;
}
let pwd = "123321";
MD5(pwd) // 对pwd进行一层加密 yIN7I/+Kqoot3pFUc84JkQ== 可轻易在线解密
MD5(MD5(pwd)) // 对pwd进行两层层加密 M9o6WW74mkgDA7Zma5Xc4g== 解密相对耗时
MD5(MD5(MD5(pwd))) // 对pwd进行三层层加密 lVKhRjOEZ45+9MVOXHBfYg== 解密困难
MD5(pwd + MD5(pwd)) // 组合自定义字符 B3S+VTISt62HgVFcL8m4Yg= 破解失败
八、总结
在数据库中存储敏感数据时永远不要用明码来存储,MD5的作用就在于将明码转为无法反向破解的字符串。这样可以有效的防止数据库被入侵而导致敏感数据直接泄漏。