我对密码散列有什么误解?
What am I misunderstanding about password hashing?
据我了解,哈希函数在输入相同数据时总是 return 相同的结果。但我一直在使用 libsodium(通过 node-sodium),但事实并非如此。
我的架构中有这个:
UserSchema.pre('save', function(next) {
// declare my variables
let user = this,
buf = Buffer.alloc(sodium.crypto_pwhash_STRBYTES, 'ascii'),
passwordBuf = Buffer.from(user.password, 'ascii'),
saltedPassBuf,
hash;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
sodium.randombytes_buf(buf, sodium.crypto_pwhash_STRBYTES, 'ascii');
// add salt to the password
saltedPassBuf = Buffer.concat([passwordBuf, buf], 128);
// hash it separately multiple times
// note, i'm not hashing the hash,
// I'm hashing the original buffer to see what happens
// this has no application in production
hash = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
hash2 = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
hash3 = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
// log it to see what I got -- not for production
console.log(hash.toString());
console.log(hash2.toString());
console.log(hash3.toString());
// save the salt and the buffer for authentication
user.salt = buf;
user.password = hash;
next();
});
我用该代码记录了三个不同的字符串。例如
$argon2i$v=19$m=32768,t=4,p=1$ayPVQ1X+xNhWmD9S5AUuawmWusk59AebhzOHhl+j5JpvmRI27Pq57XG5zcAB5R4U
$argon2i$v=19$m=32768,t=4,p=1$PjTYKpfhh1bZh+MV84Y9kA+U33nf6efuugsrz15cEKDa5+rAHgYVA5Kqo4F1G3DE
$argon2i$v=19$m=32768,t=4,p=1$Ii8AErmAFc0na9Yi2OgCkw$ySU80Fv9OiOmeT9EV/BWon1Jjck2Lx23nOeCk0wkMPU
现在每一个的第一部分都是相同的,这让我觉得提交的密码部分是相同的(因为它是正在被散列的缓冲区的第一部分)。所以也许这是我不明白的缓冲区。
但如果 buf
保持不变,为什么 saltedPassBuff
的其余部分会发生变化?
edit: 不小心提交的时候还没写完, edit 写完题目
顾名思义,输出是加盐的。这意味着在散列之前将随机字符串添加到密码中,并单独包含在输出值中。
这样做的目的是打败dictionary attacks。通过在散列之前向每个密码添加一个随机字符串,您可以确保相同的密码将以不同的方式散列,迫使攻击者分别破解每个密码。
除了您的盐之外,pwhash
函数(文档很少)很可能还添加了它自己的随机盐,它也包含在结果中,以便稍后使用 crypto_pwhash_str_verify
.[=12 进行比较=]
还有一个"CPU intensive"方面,大概是迭代。仅仅使用带有盐的散列函数对提高安全性没有多大帮助。需要添加 CPU 密集组件,例如迭代。
重点是让攻击者花费大量时间通过暴力破解密码。
据我了解,哈希函数在输入相同数据时总是 return 相同的结果。但我一直在使用 libsodium(通过 node-sodium),但事实并非如此。
我的架构中有这个:
UserSchema.pre('save', function(next) {
// declare my variables
let user = this,
buf = Buffer.alloc(sodium.crypto_pwhash_STRBYTES, 'ascii'),
passwordBuf = Buffer.from(user.password, 'ascii'),
saltedPassBuf,
hash;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
sodium.randombytes_buf(buf, sodium.crypto_pwhash_STRBYTES, 'ascii');
// add salt to the password
saltedPassBuf = Buffer.concat([passwordBuf, buf], 128);
// hash it separately multiple times
// note, i'm not hashing the hash,
// I'm hashing the original buffer to see what happens
// this has no application in production
hash = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
hash2 = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
hash3 = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
// log it to see what I got -- not for production
console.log(hash.toString());
console.log(hash2.toString());
console.log(hash3.toString());
// save the salt and the buffer for authentication
user.salt = buf;
user.password = hash;
next();
});
我用该代码记录了三个不同的字符串。例如
$argon2i$v=19$m=32768,t=4,p=1$ayPVQ1X+xNhWmD9S5AUuawmWusk59AebhzOHhl+j5JpvmRI27Pq57XG5zcAB5R4U
$argon2i$v=19$m=32768,t=4,p=1$PjTYKpfhh1bZh+MV84Y9kA+U33nf6efuugsrz15cEKDa5+rAHgYVA5Kqo4F1G3DE
$argon2i$v=19$m=32768,t=4,p=1$Ii8AErmAFc0na9Yi2OgCkw$ySU80Fv9OiOmeT9EV/BWon1Jjck2Lx23nOeCk0wkMPU
现在每一个的第一部分都是相同的,这让我觉得提交的密码部分是相同的(因为它是正在被散列的缓冲区的第一部分)。所以也许这是我不明白的缓冲区。
但如果 buf
保持不变,为什么 saltedPassBuff
的其余部分会发生变化?
edit: 不小心提交的时候还没写完, edit 写完题目
顾名思义,输出是加盐的。这意味着在散列之前将随机字符串添加到密码中,并单独包含在输出值中。
这样做的目的是打败dictionary attacks。通过在散列之前向每个密码添加一个随机字符串,您可以确保相同的密码将以不同的方式散列,迫使攻击者分别破解每个密码。
除了您的盐之外,pwhash
函数(文档很少)很可能还添加了它自己的随机盐,它也包含在结果中,以便稍后使用 crypto_pwhash_str_verify
.[=12 进行比较=]
还有一个"CPU intensive"方面,大概是迭代。仅仅使用带有盐的散列函数对提高安全性没有多大帮助。需要添加 CPU 密集组件,例如迭代。
重点是让攻击者花费大量时间通过暴力破解密码。