Open FrankKai opened 4 years ago
你可能已经知道MD5哈希不是存储密码安全的方式,如果你知道我们的服务,你可能知道我们有一个超大的数据库存储了非常多的单词,可以在几秒内破解出密码。通过使用salt,可以尽可能保护你不受这种数据库的影响,但是不会保护太多...
什么是MD5 Salt并且如何使用它呢? 在密码学中,salt是一个添加到输入单词上的随机字符串,去生成一个单独使用该单词的不同的hash。MD5实际上没有在加密算法中添加这种功能,但是你可以连接两个字符串去获得相同的结果。
在这篇博客中,我会向你解释MD5算法中的salt是什么,以及如何在你的代码中使用salt,以及为什么要使用它。
介绍中给出了一些解释,但是在这里会给你一个示例。
假设你想在数据库中存储用户密码,通常来说我们会使用md5加密密码。 比如下面这样:
username | password |
---|---|
b.king | 5f4dcc3b5aa765d61d8327deb882cf99 |
m.donald | ab4f63f9ac65152575886860dde480a1 |
但是用一些md5破解网站,很快就能破解出密码是多少。 裸MD5密码用https://hashkiller.co.uk/Cracker/MD5 很快就解析出。
如果想使用salt,需要将字符串连接到使用密码。有两个办法做这件事。
在所有密码前面加上类似”randomstringforsalt“,如果m.donald的密码是"azerty",你加密后的密码是"randomstringforsaltazerty"。 这只是做到安全的第一步。m.donald的密码是很强壮的,但是确实世界上最弱的。 猜猜还能怎么做?动态salt。
如果你经常使用相同的salt,攻击者可以找到它,然后网站就不安全了。 如果他知道你在每个密码前加了”randomstringforsalt“,你的salt就不再有用了。 为了避免他破解出你的salt,可以使用动态salt。 例如,你可以用账号创建日期当做salt:”azerty20190512“。 如果想更安全一点的话,可以对动态salt做MD5处理,生成:”azertyd003a3d8626f9a78abc9ce900b217819“。 这是一个基础的例子,你需要找到更好的salt,它看起来是一个随机值但是你可以很容易找到它来生成密码散列。
有salt的数据库和之前看起来没有差别。这就是攻击者的力量,无论你是否使用salt,攻击者都不会直接攻击你。所以他会尝试没有,也许永远不会找到你的密码。
这里有一个加了静态salt的账号和密码。
username | password |
---|---|
b.king | 81345f0d478885f72dd51c07cc3ab146 |
m.donald | 244b7f46f6aa268fc862e73d81cfc832 |
加了salt的密码用https://hashkiller.co.uk/Cracker/MD5 解析不出。
MD5算法的弱点在于它的速度。你可以在很短时间内加密大量的单词。所以可以每秒做很多次尝试通过MD5 hash找密码。
为了解密一个密码,一个黑客会用两种方法:
如果你想学习更多MD5解密的方法,可以参考这个链接:https://www.md5online.org/blog/how-md5-decryption-works/ 最直接的就是通过网站破解:https://hashkiller.co.uk/Cracker/MD5,https://www.md5online.org/md5-decrypt.html
上面两个方法,对于黑客来说密码的长度都是它解密的问题。 在蛮力破解模式中,黑客可能会从最常见的密码试起,比如a,b,...aa,ab,...等等。 我不知道密码多长最好,但是密码字符更多显然是更好的。如果你的salt有32个字符长,无论密码有多大,你已经足够安全了。
用MD5 hash 数据库的话,会有同样的问题。 如果我们不考虑特殊的字符串,每个字符有62种可能:
所以总hash数爆炸式增长:
是一个指数函数。可以看到6位密码比3位密码安全多了。但是不,它为每个额外的字符增加了更多的可能性。您应该知道,如果密码是30个字符或更多,那么除了基本短语之外,可能没有任何数据库会包含这些信息,这就是为什么使用salt,或者至少要求使用长密码是一个很好的做法。
var crypto = require('crypto');
function cryptPwd(password, salt) {
// 密码“加盐”
var saltPassword = password + ':' + salt;
console.log('原始密码:%s', password);
console.log('加盐后的密码:%s', saltPassword);
// 加盐密码的md5值
var md5 = crypto.createHash('md5');
var result = md5.update(saltPassword).digest('hex');
console.log('加盐密码的md5值:%s', result);
}
cryptPwd('123456', 'abc');
// 输出:
// 原始密码:123456
// 加盐后的密码:123456:abc
// 加盐密码的md5值:51011af1892f59e74baf61f3d4389092
cryptPwd('123456', 'bcd');
// 输出:
// 原始密码:123456
// 加盐后的密码:123456:bcd
// 加盐密码的md5值:55a95bcb6bfbaef6906dbbd264ab4531
// 加了一个3位的盐
var crypto = require('crypto');
function getRandomSalt(){
return Math.random().toString().slice(2, 5);
}
function cryptPwd(password, salt) {
// 密码“加盐”
var saltPassword = password + ':' + salt;
console.log('原始密码:%s', password);
console.log('加盐后的密码:%s', saltPassword);
// 加盐密码的md5值
var md5 = crypto.createHash('md5');
var result = md5.update(saltPassword).digest('hex');
console.log('加盐密码的md5值:%s', result);
}
var password = '123456';
cryptPwd('123456', getRandomSalt());
// 输出:
// 原始密码:123456
// 加盐后的密码:123456:498
// 加盐密码的md5值:af3b7d32cc2a254a6bf1ebdcfd700115
cryptPwd('123456', getRandomSalt());
// 输出:
// 原始密码:123456
// 加盐后的密码:123456:287
// 加盐密码的md5值:65d7dd044c2db64c5e658d947578d759
引用:https://www.cnblogs.com/chyingp/p/nodejs-learning-crypto-md5.html
Nodejs v10.5.0后 crypto模块可以加盐的方法
crypto.scrypt(password, salt, keylen[, options], callback)
const crypto = require('crypto');
// Using the factory defaults.
crypto.scrypt('secret', 'salt', 64, (err, derivedKey) => {
if (err) throw err;
console.log(derivedKey.toString('hex')); // '3745e48...08d59ae'
});
// Using a custom N parameter. Must be a power of two.
crypto.scrypt('secret', 'salt', 64, { N: 1024 }, (err, derivedKey) => {
if (err) throw err;
console.log(derivedKey.toString('hex')); // '3745e48...aa39b34'
});
https://www.md5online.org/blog/md5-salt-hash/