编码、哈希与加密有什么区别:密码到底应该如何保存
在开发中,我们经常会看到Base64、SHA-256、AES、RSA等名词。它们似乎都能把原始内容变成一串难以阅读的字符,因此很容易被统称为“加密”。
但实际上,编码、哈希和加密解决的是三类完全不同的问题:
- 编码解决数据如何表示和传输;
- 哈希解决数据如何生成固定长度摘要;
- 加密解决数据如何在没有密钥时保持不可读。
如果混淆了它们,就可能出现严重的安全问题。例如,把密码进行Base64编码后保存,几乎等同于保存明文;直接对密码计算一次SHA-256,也不足以抵抗现代硬件的高速破解。
一、编码解决的是表示问题
编码是按照公开规则,把一种数据形式转换为另一种形式。
它的目标通常不是保密,而是为了:
- 方便数据传输;
- 兼容文本协议;
- 避免特殊字符干扰;
- 使用统一格式表示二进制内容。
例如,计算机中的文件、图片和音频本质上都是二进制数据。如果某个系统只能稳定传输文本,就可以先把二进制数据编码成可打印字符。
Base64就是常见的编码方式。
假设原始文本为:
Hello
经过Base64编码后可能变成:
SGVsbG8=
这串字符看起来不容易直接阅读,但任何人都可以按照公开规则将它恢复为Hello。
编码过程可以表示为:
原始数据
↓ 编码
编码结果
↓ 解码
原始数据
编码不需要秘密密钥,因此不能用来保护密码、令牌和隐私数据。
二、为什么Base64不是加密
判断一种技术是不是加密,可以先问一个问题:
恢复原始数据是否需要一份只有授权者掌握的秘密?
Base64解码不需要秘密。任何拿到编码结果的人都可以还原内容。
下面这种“密码保护”没有安全意义:
stored_password = base64_encode(password)
攻击者获取数据库后,只需要进行Base64解码,就能得到所有用户的原始密码。
Base64适合用于表示数据,不适合用于隐藏数据。它看起来像乱码,只是因为人不习惯直接阅读它,而不是因为它具备安全性。
三、哈希解决的是摘要问题
哈希函数可以把任意长度的数据转换为固定长度的摘要。
例如,对一段文本计算SHA-256:
原始数据
↓ 哈希函数
固定长度摘要
无论输入是一句话、一个文件还是几个GB的数据,SHA-256输出长度都是256位,通常显示为64个十六进制字符。
理想的密码学哈希函数具有以下性质。
1. 相同输入产生相同结果
只要输入内容完全相同,得到的哈希值就相同。
因此,哈希可以用于判断数据是否发生变化。
2. 输入轻微变化,结果明显不同
即使只修改一个字符,得到的哈希值也会出现大幅变化。
这使得哈希适合检测文件完整性。
3. 难以从结果反推输入
哈希通常被设计为单向计算。已知输入可以快速计算摘要,但仅凭摘要难以直接恢复原始输入。
4. 难以找到相同摘要的不同输入
由于输入空间远大于输出空间,从数学上讲,哈希碰撞一定存在。但安全的哈希算法应当让攻击者难以主动找到具有相同摘要的两个不同输入。
四、哈希不是“不可解密的加密”
加密和哈希最重要的区别是:加密需要能够恢复原文,而哈希通常没有对应的解密操作。
加密过程是:
明文 + 密钥
↓
密文
↓ 使用正确密钥解密
明文
哈希过程则是:
原始数据
↓
摘要
哈希结果不是原始内容的加密副本,而是根据原始内容计算出的固定长度值。
因此,“把哈希解密出来”这个说法通常并不准确。
不过,哈希不能直接反推,并不代表原始输入永远无法被猜出。攻击者可以不断尝试可能的输入,再计算哈希值,与目标摘要进行比较:
猜测输入
→ 计算哈希
→ 与数据库中的哈希比较
→ 相同则猜测成功
如果输入是常见密码,攻击者可能很快找到匹配结果。
五、加密解决的是保密问题
加密的目标是让未授权者无法理解数据,同时允许授权者使用密钥恢复原文。
根据密钥使用方式,加密通常可以分为对称加密和非对称加密。
六、对称加密是什么
对称加密使用同一份密钥完成加密和解密:
明文 + 密钥 → 密文
密文 + 同一密钥 → 明文
常见对称加密算法包括AES和ChaCha20。
对称加密通常速度较快,适合处理大量数据,例如:
- 文件加密;
- 数据库敏感字段加密;
- 通信内容加密;
- 备份数据加密;
- 磁盘加密。
对称加密的核心难点是密钥管理。如果密钥与密文保存在完全相同的位置,而且攻击者能够同时取得两者,那么加密保护就可能失效。
七、非对称加密是什么
非对称密码系统使用一对相关密钥:
- 公钥;
- 私钥。
公钥可以公开,私钥必须保密。
用于加密时,可以使用公钥加密数据,再由对应私钥解密:
明文 + 公钥 → 密文
密文 + 私钥 → 明文
非对称密码技术还可以用于数字签名:
数据摘要 + 私钥 → 签名
数据 + 签名 + 公钥 → 验证结果
数字签名主要证明:
- 数据是否被篡改;
- 签名是否由持有对应私钥的一方产生。
非对称算法通常比对称算法慢,因此实际系统经常结合两者:
- 使用非对称机制安全协商临时密钥;
- 使用对称密钥加密大量实际数据。
八、密码为什么不应该使用可逆加密保存
用户登录时,系统只需要判断“本次输入的密码是否正确”,通常不需要知道用户的原始密码。
因此,系统没有必要保存能够被还原的密码。
如果使用AES等可逆加密保存密码,那么服务器必须同时掌握解密密钥。一旦数据库和密钥同时泄露,攻击者就可能恢复所有用户密码。
更加合理的目标应该是:
系统能够验证密码是否正确,但任何人都不能从保存结果中直接恢复原密码。
这正是密码哈希技术要解决的问题。
九、为什么不能直接使用SHA-256保存密码
一种看似合理的做法是:
password_hash = SHA-256(password)
登录时再次对用户输入的密码计算SHA-256,然后比较两个结果。
这种方案比保存明文强,但仍然不够安全,因为SHA-256的设计目标是快速处理大量数据。
对于文件完整性校验,速度快是优点;对于密码保存,速度过快却是缺点。
攻击者拿到密码哈希后,可以利用CPU、GPU或专用硬件高速尝试大量候选密码。如果用户使用常见短密码,可能很快被猜出。
密码保存需要的是专门设计的密码派生算法,例如:
- Argon2id;
- bcrypt;
- scrypt;
- PBKDF2。
这些算法会故意增加计算时间,部分算法还会增加内存消耗,从而提高批量破解成本。
十、盐值是什么
盐值是一段为每个密码随机生成的数据。
保存密码时,不是直接计算:
Hash(password)
而是计算:
PasswordHash(password, salt, parameters)
数据库中可以保存:
- 使用的算法;
- 算法参数;
- 盐值;
- 最终派生结果。
盐值不需要保密,它的作用不是成为第二个密码,而是保证相同密码产生不同的保存结果。
假设两个用户都使用相同密码。如果没有盐值,他们的哈希结果也相同,攻击者可以立刻看出两人使用了相同密码。
加入随机盐值后:
用户A:PasswordHash(password, salt_A)
用户B:PasswordHash(password, salt_B)
即使两人的密码相同,最终结果也通常不同。
十一、盐值能够防止什么
盐值主要用于抵抗以下问题。
1. 相同密码产生相同结果
每个用户使用不同盐值后,相同密码不会直接表现为相同的保存结果。
2. 预计算表攻击
攻击者无法只计算一次常见密码摘要,就匹配所有使用该密码的账户。针对每个盐值,都需要重新计算。
3. 批量攻击效率过高
盐值迫使攻击者针对不同用户分别进行计算,增加大规模破解成本。
需要注意的是,盐值不能让弱密码变强。如果用户使用非常容易猜到的密码,攻击者仍然可以通过逐个尝试找到结果,只是攻击成本更高。
十二、什么是Pepper
Pepper也可以参与密码派生,但它与盐值不同。
盐值通常:
- 每个密码单独生成;
- 可以与哈希结果一起存入数据库;
- 不要求保密。
Pepper通常:
- 由整个系统统一管理,或者按一定范围管理;
- 不与密码结果存放在同一数据库;
- 必须保密;
- 可以存放在专门的密钥管理系统中。
可以将过程简化为:
PasswordHash(password, salt, pepper)
即使攻击者只获取数据库,没有获得Pepper,也无法直接开始离线验证密码猜测。
不过,Pepper会增加密钥轮换和灾难恢复的复杂度。它可以作为额外防线,但不能替代可靠的密码哈希算法和随机盐值。
十三、密码注册和登录
- 点赞
- 收藏
- 关注作者
评论(0)