安全地创建和存储密码

几乎每次涉及用户配置文件时,都必须管理用户凭据,从而能够创建和存储用户密码。 通常应该使用散列密码和盐分密码来准备数据库公开和通过使用Rainbow表进行散列反转的密码。
但是,找到以明文形式存储的密码并不少见(很不幸)(我们将跳过一些必须努力学习正确操作方法的大公司名单)。 当具有未加密,未哈希和未加盐的密码的数据库泄漏时,后果很明显……..第二种最糟糕的方法是使用哈希但未加盐的密码。 在这种情况下彩虹表或哈希逆转喜欢在网络上的服务这个是巨大的帮助。 最后,第三种最糟糕的方法是仅依靠加密记录-一旦密钥或解密数据库泄露,游戏就结束了!

那么怎么做对呢? 简单的答案是:将PBKDF2WithHmacSHA1与盐值一起使用。 例如,可以在此处找到如何使用它的示例。 该实现看起来很成熟,但是很复杂。 如果您只是想了解盐腌密码的概念,则可能需要查看以下演示代码:

public static final String HASH_ALGORITHM = "SHA-256"; 
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private static final char[] PASSWORD_CHARS = new char[]{
        '!', '@', '#', '$', '%', '&', '*', '(', ')', 
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
        'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; 

    public String getRandomString(final int length) {
        /*
         * Don't try to seed SecureRandom yourself unless you know 
         * what you are doing! 
         * @see Randomly failed! Weaknesses in Java Pseudo Random Number Generators (PRNGs).
         */ 
        SecureRandom secureRandom = new SecureRandom(); 
        StringBuilder sb = new StringBuilder(length);
        int position = 0;
        // create a random string of the requested length from a set of allowed chars
        for (int i = 0; i < length; i++ ) {
            position = secureRandom.nextInt(PASSWORD_CHARS.length);
            sb.append(PASSWORD_CHARS[position]);
        }
        return sb.toString();
    }

    public static byte[] createPasswordHash(final String password,
            final String salt) {
        byte[] result = null;
        try {
            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
            digest.update(salt.getBytes(DEFAULT_CHARSET));
            digest.update(password.getBytes(DEFAULT_CHARSET));
            result = digest.digest();
        } catch (NoSuchAlgorithmException e) {
            // TODO Logging
        }

        return result;
    }

    public static boolean checkPassword(final User user, final String password) {
        boolean result = false;
        String storedPasswordHash = user.getPwHash();
        String salt = user.getSalt();
        byte[] checkPasswordHashBytes = createPasswordHash(password, salt);
        String checkPasswordHash = encodeBase64(checkPasswordHashBytes); // for simplicity let's say we use Base64

        if (checkPasswordHash != null && storedPasswordHash != null
                && checkPasswordHash.equals(storedPasswordHash)) {
            result = true;
        }

        return result;
    }

该代码期望某种带有pwHash和salt字段的用户对象(这两个字段都不敏感!)来存储必要的信息。 可以安全地保留此User对象(以防万一没有其他敏感数据链接到该对象)。 即使数据库泄漏,攻击者也必须蛮力地使用密码和盐的组合,或者为盐腌的密码计算出彩虹表。 请注意,该彩虹表不能与带有不同盐的相同密码重复使用! 这意味着,使用随机选择的盐,即使密码保持不变,攻击者也将需要彩虹表来反转每种盐的哈希函数。

上面的代码保持尽可能简单。 例如,getRandomString可以重新用于创建盐值,并可能在注册过程中用于生成临时密码。 但是请记住,此代码远非在生产环境中可用!

最后一些注意事项:确保使用足够长的盐,防止盐重用,并使用强大的算法进行哈希处理!

参考: Java安全和相关主题博客上的JCG合作伙伴 Christopher Meyer 安全地创建和存储密码

翻译自: https://www.javacodegeeks.com/2013/08/safely-create-and-store-passwords.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值