【问题标题】:Securing passwords SHA-1 Java保护密码 SHA-1 Java
【发布时间】:2015-04-09 20:04:46
【问题描述】:

我正在尝试保护我正在构建的 Web 应用程序的密码。我对密码学很陌生。我正在使用 Java 和 Sha-1 Hash 将密码存储在 Db 中。

在对此事进行一些研究之后,使用盐似乎是散列密码的最佳方法,但当然盐必须与密码一起存储。

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

非常感谢您!

@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;
}
}

【问题讨论】:

  • SHA1 不安全。使用 scrypt 或 bcrypt 或 PBKDFv2。
  • 另外,您在这里至少有两个不同的问题 - “如果有人用盐窃取了我的数据库,他们可以读取密码吗?” (可能更适合security.se)和“我应该添加一个新列,还是将两个内容存储在一个由|分隔的列中?”

标签: java encryption hash cryptography sha


【解决方案1】:

盐的存储方式与方案的安全性无关。 salt和hash都可以直接存储。

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

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

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


没有胡椒的例子:

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));
    }
}

【讨论】:

  • 嗯,请不要明文存储密码。您是指密码的哈希值吗?
  • @stringy05 一定要迟到了:)
  • 哈哈。我现在还早!
  • 我的答案或 stringy05 的答案中缺少什么?
  • 迭代次数应该更高——显然是 86,000
【解决方案2】:

我认为应该更强烈地指出,使用盐进行密码散列不再安全,不应像其他人使用 PBKDF2 或 bcrypt 那样使用。

【讨论】:

  • 虽然我很高兴您关心安全问题,但我必须注意这是评论,而不是答案。
【解决方案3】:

将盐以明文形式存储在哈希旁边是可以的,因为密码是安全的(前提是您在应用程序上小心处理它 - 不要记录它等)。加盐的重点是为每个用户提供唯一的哈希输入,因此您不能只使用彩虹表或猜测密码(例如,许多用户具有相同的哈希,然后他们使用相同的密码。我以前知道“password01”的 md5 哈希值,因为我有机会看到一个蹩脚的 wep 应用程序)

要了解如何正确执行此操作,请参阅: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

【讨论】:

    猜你喜欢
    • 2018-09-10
    • 1970-01-01
    • 2011-02-19
    • 1970-01-01
    • 1970-01-01
    • 2011-02-01
    • 2011-03-25
    • 1970-01-01
    • 2013-09-05
    相关资源
    最近更新 更多