SV.HASH.NO_SALT
在没有 salt 的情况下使用单向密码哈希
在未添加 salt 的情况下将单向密码哈希函数应用于单一输入数据时,将报告此错误。
漏洞与风险
如果软件存储为用户密码计算的单向密码哈希,建议在每个密码后附加或在开头添加唯一的 salt;因此如果两个用户具有相同的密码,则哈希将不同。
如果攻击者获取哈希访问权限,由于缺少 salt 会更容易猜测多个用户密码。
漏洞代码示例
复制
/*
* 添加新用户;存储登录名和密码哈希。
*/
public void addUser(String login, byte[] password) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password); // < -- calculate hash for the password
byte[] hash = md.digest();
storeHashString(login, Hex.encodeHexString(hash));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
/*
* Validates user login information.如果用户存在,并且输入的密码哈希
与存储的密码哈希相匹配,则返回 true;
* 否则返回 false。
*/
public boolean login(String login, byte[] password) {
try {
String storedHash = readHashString(login);
if (storedHash != null) {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password); // < -- calculate hash for the password
byte[] hash = md.digest();
return MessageDigest.isEqual(hash, Hex.decodeHex(storedHash.toCharArray()));
} else {
return false;
}
} catch (NoSuchAlgorithmException | DecoderException e) {
throw new IllegalStateException(e);
}
}
...
hashManager.addUser("Alice", "changeit".getBytes());
hashManager.addUser("Bob", "changeit".getBytes());
在此例中,用户 Alice 和 Bob 都具有密码“changeit”,并且存储的哈希均为“b91cd1a54781790beaa2baf741fa6789”。因此,如果攻击者盗取密码哈希表,即可使用字典轻松地将 “b91cd1a54781790beaa2baf741fa6789”更改为“changeit”。
修正代码示例
复制
/*
* 添加新用户;存储登录名和 salt:hash。
*/
public void addUser(String login, byte[] password) {
try {
byte[] salt = new byte[16];
secureRandom.nextBytes(salt);
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(salt); // use random salt
md.update(password); // and password
byte[] hash = md.digest();
storeHashString(login, Hex.encodeHexString(salt) + ":"+ Hex.encodeHexString(hash));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
}
/*
* Validates user login information.如果用户存在,并且输入的密码
与存储的 salt:hash 相匹配,则返回 true;否则返回 false。
*/
public boolean login(String login, byte[] password) {
try {
String storedSaltAndHash = readHashString(login);
if (storedSaltAndHash != null) {
String[] saltAndHash = storedSaltAndHash.split(":");
if (saltAndHash.length != 2) {
throw new IllegalStateException("Expected salt:hash string");
}
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(Hex.decodeHex(saltAndHash[0].toCharArray())); // use stored salt
md.update(password);
byte[] hash = md.digest();
return MessageDigest.isEqual(hash, Hex.decodeHex(saltAndHash[1].toCharArray()));
} else {
return false;
}
} catch (NoSuchAlgorithmException | DecoderException e) {
throw new IllegalStateException(e);
}
}
...
hashManager.addUser("Alice", "changeit".getBytes());
hashManager.addUser("Bob", "changeit".getBytes());
在此例中,Alice 和 Bob 的密码哈希记录是随机的。可能如下所示:
Alice -> 03c016ca60ee8c53aa8f24301a08ec27:de88b6fc874d83eb30aa9020f431bde3 Bob -> e2eafef8be75cd58b7ed598ddb37e128:4bc3b11a2bf5854b66543f8ba9ffa2c6