2022年了,密码该如何保存都不会?

大家好呀,我是小黑。

我们在开发应用时,只要涉及到用户,登录注册功能则是必不可少的。
但是,并不是所有人都能做好登录注册功能。比如最基本的密码应该如何保存?应该用哪种加密方式对密码进行加密都不是很清楚。

一旦出现数据库泄漏,密码外泄等问题,会对用户造成极大的损失。

密码该如何保存?

如果我们要在服务器中对用户进行身份验证,我们需要完成以下的步骤:

  • 获取到要登录用户的用户名和密码;
  • 根据用户名在数据库中查找到用户;
  • 比较用户提供的密码和数据库中的密码是否一致。

那我们应该如何存储用户的密码呢?我们来看看都有哪些方式,以及存在的问题。

明文保存

将用户的密码以明文方式保存。

很显然,有点常识的人都应该知道,密码不能用明文保存的。但是话又说回来,系统都是由人开发的,开发系统的人可能并不专业。比如之前某个大型中文开发者社区,因为数据库泄露,导致大批用户的密码泄漏,而他们的密码就是明文保存的。

HASH保存

使用Hash函数计算出密码的hash值保存,可以解决密码直接暴露的问题。

Hash函数是一个单向函数,不能通过结果值反向得出原始值,Hash函数可以将一串密码转换成一个固定长度的字符串。

  • 在用户注册时,将用户的密码使用Hash函数计算出Hash值后保存到数据库;
  • 当用户登录时,对用户提交的密码使用相同的Hash函数计算出Hash值,和数据库中的Hash值进行比较。

这样可以避免让攻击者直接获取到用户的密码明文,攻击者想通过暴力攻击将字符串计算出hash值则需要花费巨大的精力,并且Hash值越长破解难度越大。

但是通过彩虹表攻击,攻击者仍然可以成功破解。 彩虹表是一个包含许多提前计算出Hash值的表,其中包含数百万个密码对应的hash值,对于一些简单密码可以非常快的破解。

所以,如果你不确定你注册的服务是采用哪种方式保存的密码,尽量将密码复杂度设置高一些。

加盐Hash

为了防止彩虹表攻击,可以使用Hash算法加盐处理。

是在进行Hash计算时,和原始密码拼接在一起进行计算的一个随机序列。

  • 用户注册时,将密码和盐值组合后进行Hash计算,得到密码结果保存在数据库中;
  • 当用户在登录验证时,将原始密码加盐后进行Hash计算,得到结果值和数据库中的密码进行比较。

因为彩虹表中的密码和加盐后的密码不一样,可以防止彩虹表攻击。如果盐值足够长并且随机,那么就可以保证在彩虹表中不能找到和密码相同的hash值。

但是,由于攻击者是有可能获取到盐值的,攻击者可以调整彩虹表生成的算法,用获取到的盐值计算出新的彩虹表,同样可以获取到密码。虽然计算一个新的彩虹表花费的时间巨大,但是随着硬件条件越来越好,要计算出一张彩虹表会变得越来越容易。

所以,使用Hash算法加盐处理,可以保证密码不被快速破解,但是还不够安全。

密码加密函数

Hash函数设计的初衷并不仅仅是对密码进行Hash计算,所以Hash函数的运算速度非常快,但是这样一来,攻击者也能快速计算hash值,进行暴力破解。

为了解决这个问题,我们可以让Hash加密函数变慢

我们只要让密码加密的时间在用户能接受的时间内,尽量的慢,这样攻击者蛮力破解将会花费无限的时间。

有以下一些专门用来加密密码的算法:

  • bcrypt
  • scrypt
  • PBKDF2
  • argon2

这些算法使用一些复杂的加密算法,并会故意让计算变慢。

工作因子

可以通过在算法中配置工作因子,来调整加密函数计算时间的缓慢程度。

每个密码加密算法都有自己的工作因子。工作因子影响密码编码的速度。例如,bcrypt有参数strength,该算法将使2的strength次方来计算哈希值。数字越大,编码越慢。

使用Spring Security加密密码

现在让我们看看 Spring Security 如何支持这些算法,以及我们如何使用它们加密密码。

PasswordEncoder

在Spring Security 中有一个PasswordEncoder接口。所有密码编码器都实现了该接口。

public interface PasswordEncoder {
   
    String encode(CharSequence rawPassword);

    boolean matches(CharSequence rawPassword, String encodedPassword);

    default boolean upgradeEncoding(String encodedPassword) {
   
        return false;
    }
}

该接口中有两个方法:

encode()方法用户将明文密码转换为密文形式;

matches()方法用户将明文密码与密文密码进行比较。

BCryptPasswordEncoder

String plainPassword = "123456";
// 工作因子
int strength = 10;
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(strength, new SecureRandom());
String encodedPassword = bCryptPasswordEncoder.encode(plainPassword);
System.out.println(encodedPassword);

BCryptPasswordEncoder中的参数strength是密码加密算法的工作因子,Spring Security中的默认值为10

在创建时指定SecureRandom作为随机加盐生成器。

$2a$10$pYxXvggEgN7znYKofHIr/uRTw.dsYeW9mbxzNMSNOoGIYZU8twXNG

Pbkdf2PasswordEncoder

PBKDF2 算法不是为密码编码专门设计的,而是为了从密码中派生出密钥而设计的。当我们想用密码对某些数据进行加密时,通常需要密钥,但密码的强度不足以用作加密密钥。

String plainPassword = "123456";
//加密秘钥
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黑说Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值