You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何验证数据库中采用salt加密存储的密码?

密码加盐后的验证流程详解

嘿,这个问题问得好!密码加盐确实是Web安全里的核心知识点,刚接触Web开发的话搞不清验证流程太正常了~先纠正一个你可能踩的误区:绝对不要在客户端对密码做哈希/加密后再传给服务器,原因我后面会说,先给你拆解正确的加盐密码验证全流程:

一、用户注册/改密码时:存储密码的正确姿势

当用户第一次设置密码(或修改密码)时,所有操作都要在服务器端完成:

  • 生成一个随机且唯一的盐值(salt):盐值要足够长(比如16字节以上),每个用户的盐都不能重复,别图省事用统一盐!
  • 用安全的密码哈希算法(比如bcrypt、Argon2、PBKDF2),把用户输入的原始密码和盐值结合后进行哈希运算(这类算法会自动处理盐的拼接、迭代次数等细节,不用自己手动拼)
  • 盐值和最终的哈希值一起存到数据库(如果用bcrypt这类算法,哈希结果里已经包含盐了,直接存哈希值就行,不用单独存盐字段)

举个简单的伪代码例子(用Python的bcrypt库,它会自动处理盐的生成):

import bcrypt

# 用户注册时输入的原始明文密码
user_raw_password = "my_strong_password_123"
# 生成随机盐并完成哈希,bcrypt.gensalt()会自动生成符合要求的盐
hashed_result = bcrypt.hashpw(user_raw_password.encode('utf-8'), bcrypt.gensalt())
# 把hashed_result直接存到数据库的用户记录里(它包含了盐,无需额外存储)

二、用户登录时:验证密码的正确流程

当用户输入密码登录时,还是在服务器端做验证:

  • 从数据库里取出该用户对应的哈希值(和盐值,如果是分开存的情况)
  • 把用户这次输入的原始明文密码,用和存储时完全相同的算法,结合取出的盐值重新生成哈希
  • 恒时比对函数把新生成的哈希和数据库里存的哈希做比对(别用普通的==,防止攻击者通过时序差异破解)

还是用bcrypt的伪代码示例:

import bcrypt

# 用户登录时输入的明文密码
login_input_password = "my_strong_password_123"
# 从数据库取出之前存的哈希值
stored_hash = b'$2b$12$EixZaY3fPbt4h8rO3M7zq..V69eM6Q6vM3Q8eM6Q6vM3Q8eM6Q6vM'

# bcrypt的checkpw方法会自动从stored_hash里提取盐,生成新哈希后做安全比对
if bcrypt.checkpw(login_input_password.encode('utf-8'), stored_hash):
    print("密码正确,登录成功!")
else:
    print("密码错误,登录失败!")

为什么不能在客户端哈希密码?

你之前想的客户端加密后传哈希的思路有几个致命问题:

  • 客户端哈希后的结果就相当于"新的明文密码":如果这个哈希值被泄露,攻击者可以直接用它登录,和明文密码泄露没区别
  • 盐值无法安全处理:如果盐硬编码在客户端,所有用户共用一个盐,加盐的意义就没了;如果从服务器拿盐,那还是要先传明文密码到服务器,不如直接在服务器处理
  • 防不住中间人攻击:如果客户端只传哈希,中间人可以替换成自己的哈希值,服务器根本分辨不出来

几个关键提醒

  • 绝对别用弱哈希算法(MD5、SHA-1、SHA-256不加迭代),一定要用专门的密码哈希算法:bcrypt、Argon2(目前最推荐)、PBKDF2(带至少10000次迭代)
  • 每个用户的盐必须唯一,哪怕密码相同,哈希结果也得不一样
  • 用库自带的恒时比对函数,避免时序攻击(大部分密码库都内置了这个功能,不用自己写)

内容的提问来源于stack exchange,提问作者Saliery

火山引擎 最新活动