Start here

Home
About Klocwork
What's new
Fixed issues
Release notes
Installation

Reference

C/C++ checkers
Java checkers
C# checkers
MISRA C 2004 checkers
MISRA C++ 2008 checkers
MISRA C 2012 checkers
MISRA C 2012 checkers with Amendment 1
Commands
Metrics
Troubleshooting
Reference

Product components

C/C++ Integration build analysis
Java Integration build analysis
Desktop analysis
Refactoring
Klocwork Static Code Analysis
Klocwork Code Review
Structure101
Tuning
Custom checkers

Coding environments

Visual Studio
Eclipse for C/C++
Eclipse for Java
IntelliJ IDEA
Other

Administration

Project configuration
Build configuration
Administration
Analysis performance
Server performance
Security/permissions
Licensing
Klocwork Static Code Analysis Web API
Klocwork Code Review Web API

Community

View help online
Visit RogueWave.com
Klocwork Support
Rogue Wave Videos

Legal

Legal information

SV.HASH.NO_SALT

Use of a one-way cryptographic hash without a salt

This 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 risk

If 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 example

1  /*
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 example

1 /*
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 checkers