2015-02-09 172 views
0

我试图证实我正在构建的Web应用程序的密码。我对密码学相当陌生。我正在使用Java和Sha-1 Hash来将密码存储在Db中。保护密码SHA-1 Java

经过一些关于使用盐的问题的研究似乎是散列密码的最佳方式,但当然盐必须与密码一起存储。

在我的情况下,我在盐的数据库中使用了一个新列,我不知道这是否是正确的方法。如果有人窃取我的数据库,用哈希和盐将能够读取密码?将哈希和盐之间的分隔符放在一起并存储在一起会更好吗?在这种情况下,为了检查密码的有效性,我必须解析字符串。 只是我想知道您的意见和最佳实践,可以在这里适用。

非常感谢您提前!

@Entity 
@Table(name="USERS") 
public class User implements BasePersistentEntity<Long> { 

/** 
* 
*/ 
private static final long serialVersionUID = 1L; 


@Id 
@GeneratedValue(strategy=GenerationType.IDENTITY) 
@Column(name="ID") 
private Long id; 

@Column(name="EMAIL",nullable=false,length=50,insertable=true,updatable=true) 
private String email; 

@Column(name="PASSWORD",nullable=false,length=40,insertable=true,updatable=true) 
private String password; 

@Column(name="SALT",nullable=false,length=40,insertable=true,updatable=true) 
private String passwordSalt; 


public Long getId() { 
    return id; 
} 

public void setId(Long id) { 
    this.id = id; 
} 

public String getEmail() { 
    return email; 
} 

public void setEmail(String email) { 
    this.email=email; 
} 

public String getPassword() { 
    return password; 
} 

public void setPassword(String password) { 
    if (password.equals(this.password)) 
    { 
     return; 
    } 

    if (passwordSalt == null || passwordSalt.equals("")) 
    { 
     passwordSalt = RandomStringUtils.randomAscii(20); 
    } 

    this.password = DigestUtils.sha1Hex(password + passwordSalt); 
} 

/** 
* Check if a given password is correct. 
* 
* @param givenPassword 
* @return True is correct, else false. 
*/ 
public boolean checkPassword(String givenPassword) 
{ 
    return (password.equals(DigestUtils.sha1Hex(givenPassword + passwordSalt))); 
} 


public String getPasswordSalt() { 
    return passwordSalt; 
} 

public void setPasswordSalt(String passwordSalt) { 
    this.passwordSalt = passwordSalt; 
} 
} 
+4

SHA1不安全。使用scrypt或bcrypt或PBKDFv2。 – SLaks 2015-02-09 22:32:21

+1

相关:http://security.stackexchange。com/questions/51959/why-are-salted-hashes-more-secure – immibis 2015-02-09 22:33:05

+0

另外,这里至少有两个不同的问题 - “如果有人用盐去偷我的数据库,他们可以读密码吗?” (可能更适合security.se)和“我应该添加一个新的列,还是将两个东西存储在由'|'分隔的一列中?” – immibis 2015-02-09 22:34:22

回答

1

盐的存储方式对方案的安全性无关紧要。 salt和hash都可以存储在纯文本中。

盐可以用来防止彩虹表攻击以及为相同的密码创建不同的哈希值。

但是你缺少的是某种工作因素或迭代计数。这由密码密钥派生函数(如PBKDF2(包含在Java中)或bcrypt)提供。这将为暴力破解提供一些额外的保护(尝试各种密码并查看它们是否匹配)。这会增加一些安全性,因为密码通常缺乏足够的熵。

可以在服务器应用程序中存储一个静态的“辣椒”,该静态的“辣椒”可以与salt结合使用。如果只有数据被盗,这可能会增加一些安全性。


例无胡椒:

import java.security.NoSuchAlgorithmException; 
import java.security.SecureRandom; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.KeySpec; 

import javax.crypto.SecretKey; 
import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.PBEKeySpec; 

public class PBKDF2ForPasswordHash { 

    private static final String PBKDF_ALGORITHM = "PBKDF2WithHmacSHA1"; 
    private static final int ITERATION_COUNT = 10_000; 
    // should be less than the size of the underlying hash 
    private static final int PASSWORD_HASH_SIZE_BYTES = 16; 
    private static final int SALT_SIZE_BYTES = 16; 

    public static byte[] generateRandomSalt(final int saltSizeBytes) { 
     final SecureRandom rng = new SecureRandom(); 
     final byte[] salt = new byte[saltSizeBytes]; 
     rng.nextBytes(salt); 
     return salt; 
    } 

    public static byte[] generatePasswordHash(final char[] password, 
      final byte[] salt) { 
     SecretKeyFactory f; 
     try { 
      f = SecretKeyFactory.getInstance(PBKDF_ALGORITHM); 
     } catch (final NoSuchAlgorithmException e) { 
      throw new IllegalStateException("PBKDF algorithm " 
        + PBKDF_ALGORITHM + " not available", e); 
     } 
     final KeySpec ks = new PBEKeySpec(password, salt, ITERATION_COUNT, 
       PASSWORD_HASH_SIZE_BYTES * Byte.SIZE); 
     SecretKey s; 
     try { 
      s = f.generateSecret(ks); 
     } catch (final InvalidKeySpecException e) { 
      throw new IllegalArgumentException(
        "PBEKeySpec should always be valid for " + PBKDF_ALGORITHM, 
        e); 
     } 
     return s.getEncoded(); 
    } 

    public static final String toHex(final byte[] data) { 
     final StringBuilder sb = new StringBuilder(data.length * 2); 
     for (int i = 0; i < data.length; i++) { 
      sb.append(String.format("%02x", data[i])); 
     } 
     return sb.toString(); 
    } 

    public static void main(final String[] args) throws Exception { 
     final char[] password = { 'o', 'w', 'l' }; 
     final byte[] salt = generateRandomSalt(SALT_SIZE_BYTES); 
     System.out.println(toHex(salt)); 
     final byte[] hash = generatePasswordHash(password, salt); 
     System.out.println(toHex(hash)); 
    } 
} 
+0

ummm plz不要将密码存储在明文中。你的意思是密码的散列? – stringy05 2015-02-09 23:06:26

+0

@ stringy05必须越来越迟:) – 2015-02-09 23:17:17

+0

哈哈。它早在我的位置! – stringy05 2015-02-09 23:33:11

0

存储旁边的盐中明确哈希是好的,因为密码是安全的(为您提供您的应用程序应有的谨慎处理它 - 不日志它等)。盐的要点是每个用户都有一个独特的输入,所以你不能使用彩虹表或猜测密码(例如,许多用户使用相同的密码,然后他们使用相同的密码。我曾经知道由于一个蹩脚的wep应用程序,我有机会看看“password01”的md5哈希)

要了解如何正确执行此操作,请参阅https://crypto.stackexchange.com/questions/760/webapp-password-storage-salting-a-hash-vs-multiple-hashes,因为您需要确保不会受到像定时

编辑

有关安全的密码存储更详细(其它严重问题筏中)的攻击,从参阅本matasano的安全忍者http://chargen.matasano.com/chargen/2015/3/26/enough-with-the-salts-updates-on-secure-password-schemes.html

0

在我看来,应该更强烈地指出,用盐密码哈希已不再安全,不应该像别人指出的那样使用PBKDF2或bcrypt。

+0

虽然我很高兴你关心安全,但我必须指出,这是一条评论,而不是一个答案。 – 2015-02-12 08:00:13

+0

是的你是对的:) – 2015-02-13 12:25:23