SV.HASH.NO_SALTUse of a one-way cryptographic hash without a saltThis error is reported when a one-way cryptographic hash function is applied to a single piece of input data without salt being added. Vulnerability and riskIf software stores one-way cryptographic hashes calculated for user passwords, appending or prepending a unique salt to each password is recommended; so if two users have the same password, the hashes will be different. If an attacker gains access to the hashes, the lack of a salt makes it easier to guess multiple user passwords. Vulnerable code example1 /* 2 * Adds a new user; stores login and password hash. 3 */ 4 public void addUser(String login, byte[] password) { 5 try { 6 MessageDigest md = MessageDigest.getInstance("MD5"); 7 md.update(password); // < -- calculate hash for the password 8 byte[] hash = md.digest(); 9 storeHashString(login, Hex.encodeHexString(hash)); 10 } catch (NoSuchAlgorithmException e) { 11 throw new IllegalStateException(e); 12 } 13 } 14 15 /* 16 * Validates user login information. Returns true if user exists, and 17 entered password hash matches stored password hash; 18 * otherwise returns false. 19 */ 20 public boolean login(String login, byte[] password) { 21 try { 22 String storedHash = readHashString(login); 23 if (storedHash != null) { 24 MessageDigest md = MessageDigest.getInstance("MD5"); 25 md.update(password); // < -- calculate hash for the password 26 byte[] hash = md.digest(); 27 return MessageDigest.isEqual(hash, Hex.decodeHex(storedHash.toCharArray())); 28 } else { 29 return false; 30 } 31 } catch (NoSuchAlgorithmException | DecoderException e) { 32 throw new IllegalStateException(e); 33 } 34 } 35 36 . . . 37 38 hashManager.addUser("Alice", "changeit".getBytes()); 39 hashManager.addUser("Bob", "changeit".getBytes()); In this example, both users – “Alice” and “Bob” have password “changeit”; and both stored hashes are “b91cd1a54781790beaa2baf741fa6789”. Then, if an attacker steals the password hashes table, he can easily reverse “b91cd1a54781790beaa2baf741fa6789” to “changeit” using a dictionary. Fixed code example1 /* 2 * Adds a new user; stores login and "salt:hash". 3 */ 4 public void addUser(String login, byte[] password) { 5 try { 6 byte[] salt = new byte[16]; 7 secureRandom.nextBytes(salt); 8 MessageDigest md = MessageDigest.getInstance("MD5"); 9 md.update(salt); // use random salt 10 md.update(password); // and password 11 byte[] hash = md.digest(); 12 storeHashString(login, Hex.encodeHexString(salt) + ":" + Hex.encodeHexString(hash)); 13 } catch (NoSuchAlgorithmException e) { 14 throw new IllegalStateException(e); 15 } 16 } 17 18 /* 19 * Validates user login information. Returns true if user exists, and 20 entered password matches stored "salt:hash"; otherwise returns false. 21 */ 22 public boolean login(String login, byte[] password) { 23 try { 24 String storedSaltAndHash = readHashString(login); 25 if (storedSaltAndHash != null) { 26 String[] saltAndHash = storedSaltAndHash.split(":"); 27 if (saltAndHash.length != 2) { 28 throw new IllegalStateException("Expected salt:hash string"); 29 } 30 MessageDigest md = MessageDigest.getInstance("MD5"); 31 md.update(Hex.decodeHex(saltAndHash[0].toCharArray())); // use stored salt 32 md.update(password); 33 byte[] hash = md.digest(); 34 return MessageDigest.isEqual(hash, Hex.decodeHex(saltAndHash[1].toCharArray())); 35 } else { 36 return false; 37 } 38 } catch (NoSuchAlgorithmException | DecoderException e) { 39 throw new IllegalStateException(e); 40 } 41 } 42 43 . . . 44 45 hashManager.addUser("Alice", "changeit".getBytes()); 46 hashManager.addUser("Bob", "changeit".getBytes()); In this example, the password hash records for Alice and Bob are randomized. It can look like this:
Alice -> 03c016ca60ee8c53aa8f24301a08ec27:de88b6fc874d83eb30aa9020f431bde3 Bob -> e2eafef8be75cd58b7ed598ddb37e128:4bc3b11a2bf5854b66543f8ba9ffa2c6 Related checkersExternal guidance |