Skip to content

[L1] 密码学基础概念辨析

一句话结论

哈希不可逆、加密可逆、编码无密钥,三类概念混淆即漏洞。

体系讲解

密码学在 Web 安全中涉及五个核心概念,初学者常将它们混淆使用,导致安全漏洞。

一、哈希 vs 加密

维度哈希(Hash)加密(Encryption)
可逆性不可逆(单向函数)可逆(持有密钥可解密)
密钥有(对称或非对称)
典型算法SHA-256、bcrypt、argon2AES、RSA
典型用途存储密码、数据完整性校验传输/存储需还原的敏感数据
PHP APIpassword_hash() / hash()openssl_encrypt()

二、对称 vs 非对称加密

维度对称加密非对称加密
密钥数量1 个(收发方共享)1 对(公钥+私钥)
速度快(适合大数据)慢(适合小数据/密钥协商)
典型算法AES-256-CBC/GCMRSA、ECDSA
典型场景数据库字段加密、文件加密HTTPS 握手、数字签名
密钥分发难点如何安全传递共享密钥公钥可公开,私钥自持

HTTPS 两者结合:非对称加密协商对称密钥,后续数据传输用对称加密。

三、HMAC(消息认证码)

HMAC = Hash + 共享密钥,公式为 HMAC(key, message) = Hash(key ⊕ opad || Hash(key ⊕ ipad || message))

普通哈希无密钥,任何人都能伪造消息并重新计算哈希值;HMAC 引入共享密钥,只有持有密钥的双方才能生成/验证合法 MAC,适用于:

  • API 请求签名(防篡改、防重放)
  • Webhook 消息完整性验证(如 GitHub/Stripe 回调)

四、数字签名(签名 ≠ 加密)

操作用谁的密钥谁能验证/解密保证什么
加密接收方公钥加密接收方私钥解密机密性
签名发送方私钥签名任何持有公钥者验证完整性 + 不可否认性

密钥方向完全相反,混淆会导致功能失效。

五、编码(Base64 / URL 编码)≠ 加密

编码只是改变数据的表示形式,无需密钥,任何人都可以逆向还原:

  • base64_encode('secret')c2VjcmV0base64_decode('c2VjcmV0')secret
  • URL 编码同理,仅解决字符集兼容问题,不提供任何保密性

将 Base64 编码的内容放在 HTTP 头或日志中并不等于"加密传输"。

考察意图

考察候选人是否能准确区分安全领域五个基础概念,防止将编码当加密、用 MD5 存密码等高频安全误用,反映其代码审查和安全意识的基本素养。

追问链

Q1:为什么不能用 MD5 或 SHA1 存储用户密码? MD5/SHA1 是通用哈希,运算速度极快,GPU 可秒级暴力破解或查彩虹表;应改用专为密码设计的慢哈希算法(bcrypt / argon2),PHP 中直接用 password_hash() + password_verify()

Q2:HMAC 与直接在消息末尾附加密钥再哈希(hash('sha256', $key.$msg))有何区别? 后者存在长度扩展攻击(Length Extension Attack)漏洞:攻击者无需知道密钥,仅凭已知哈希值就能伪造附加任意内容后的合法哈希。HMAC 通过双层嵌套结构规避了此漏洞,是经过密码学证明安全的构造。

Q3:HTTPS 握手中,非对称加密和对称加密分别在哪个阶段使用? 握手阶段:客户端用服务端公钥加密预主密钥(或用 ECDHE 做密钥协商),完成对称密钥的安全交换;数据传输阶段:双方用协商得到的对称密钥(AES)加解密实际数据,兼顾安全性与性能。

Q4:API 请求签名中,HMAC 如何防重放攻击? 仅靠 HMAC 无法防重放(相同请求签名相同);需额外加入时间戳 + nonce:服务端校验时间戳在允许窗口内(如 ±5 分钟),并将 nonce 存入 Redis 短期缓存,防止同一请求被重复提交。

易错点

  1. Base64 当加密用:Base64 无密钥,任何人执行 base64_decode() 即可还原原文。将 Base64 字符串放入 HTTP Header 或日志中暴露 token/密码,是常见安全漏洞。

  2. 用 MD5/SHA1 直接存密码:这两种算法被设计为"越快越好",现代 GPU 破解速度以亿次/秒计;应使用 password_hash($pwd, PASSWORD_BCRYPT)PASSWORD_ARGON2ID,其内置的 cost 因子会故意拖慢计算。

  3. 签名和加密的密钥方向混淆:加密是用接收方公钥加密(私钥解密),签名是用发送方私钥签名(公钥验证),方向相反。混淆后若用私钥"加密"则所有人都能用公钥"解密",完全失去机密性。

代码示例

php
<?php
// 1. 哈希(不可逆)—— 存储密码
$hash = password_hash('my-password', PASSWORD_BCRYPT);
var_dump(password_verify('my-password', $hash)); // bool(true)

// 2. 对称加密(可逆)—— 存储需还原的敏感字段
$key = random_bytes(32);
$iv  = random_bytes(16);
$enc = openssl_encrypt('card-number', 'aes-256-cbc', $key, 0, $iv);
$dec = openssl_decrypt($enc, 'aes-256-cbc', $key, 0, $iv); // 还原原文

// 3. HMAC —— API 请求签名
$sig = hash_hmac('sha256', 'timestamp=1716451200&nonce=abc', 'shared-secret');

// 4. Base64 只是编码,非加密
echo base64_decode(base64_encode('secret')); // 输出: secret

基于 Apache License 2.0 开源