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

相关检查器

安全培训

应用程序安全培训材料由 Secure Code Warrior 提供。