5.8 KiB
5.8 KiB
tags, create time
| tags | create time | ||||||
|---|---|---|---|---|---|---|---|
|
2026-04-25 14:45 |
客户端密钥派生:盐值挑战方案详解
概述
本文为「登录安全流程」的进阶篇,讲解金融类高安全场景下采用的客户端密钥派生方案(盐值挑战 / Salt Challenge)。核心思路:前端在发送密码之前,先用后端下发的随机盐值做 PBKDF2 派生,让传输层上永远不出现原始密码。
核心类比:餐厅里的"一次性餐巾纸"
想象你去一家高安全级别的餐厅点餐——点餐的过程就等同于"登录"。
普通餐厅(标准 HTTPS 登录)
你 → 把订单写在纸条上 → 服务员拿走 → 厨房照着做
对应到代码:用户输入密码 → 浏览器直接发 {username, password} → 后端验证。
HTTPS 就像餐厅的监控摄像头 + 玻璃隔断,服务员看不到纸条内容。但如果有坏蛋偷偷装了针孔摄像头(HTTPS 被攻破),他拍到的就是你完整的订单,可以直接拿去厨房冒充你点餐。
高级餐厅(盐值挑战方案)
现在来看完整的盐值挑战流程——这就是高级餐厅的做法。
完整流程拆解
第一步:前端先"要盐"
func (h *Handler) GetChallenge(w http.ResponseWriter, r http.Request) {
salt := generateRandomSalt(32) // 随机生成 32 位盐值
cache.Set("challenge:"+r.RemoteAddr, salt, 1*time.Minute) // 存到缓存,1 分钟后自动清除
json.NewEncoder(w).Encode(LoginChallenge{
Salt: salt,
ExpireAt: time.Now().Add(time.Minute).Unix(), // 告诉前端什么时候过期
})
}
生活类比:服务员递给你一张"一次性餐巾纸"
- 你刚落座(
r.RemoteAddr= 你的桌号),服务员就递给你一张随机印了乱码的餐巾纸(salt) - 这张纸只有效 1 分钟(
ExpireAt),过期作废 - 餐厅后台会记住:哪张纸给了哪桌,什么时候过期
| 代码 | 类比 | 为什么重要 |
|---|---|---|
generateRandomSalt(32) |
餐巾纸上随机印的乱码 | 每次都不一样,两个不同桌的人拿到的纸完全不同 |
cache.Set(..., 1*time.Minute) |
1 分钟后餐厅收回这张纸 | 过期后就算有人偷了这张纸也没用 |
"challenge:"+r.RemoteAddr |
按桌号记录哪张纸给了谁 | 防止有人拿别人桌的纸来冒充 |
第二步:前端拿到盐后"做记号"
// 前端拿到 salt = "a7xK9mZp..." 后:
// 用 PBKDF2 算法,把 用户密码 + 盐值 混合运算
// 得到一个派生密钥,比如 "e4f8b2d1c9a3..."
// 把这个派生密钥发给后端登录
生活类比:你在餐巾纸上用"暗语"写订单
- 你在餐巾纸的乱码旁边,用只有你和厨房能懂的暗语写下你要点的菜(密码)
- 你不是直接写"红烧肉",而是写暗语"8 号食材 + 3 号做法"
- 厨房拿到纸条后,用同样的暗语表反查,知道你点了什么
PBKDF2 在这里的角色: 是一套单向暗语转换规则。
- 你(前端)用这套规则把"红烧肉"变成"8 号食材 + 3 号做法"
- 厨房(后端)用同样的规则验证这个暗语对不对
- 但反过来——有人只看到"8 号食材 + 3 号做法",猜不出原菜名是什么(因为 PBKDF2 是单向的)
第三步:后端"对暗语" + 撕纸
func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
// 1. 前端先用盐值做 PBKDF2 → 得到派生密钥
// 2. 后端用同样的盐 + 数据库里的密码做 PBKDF2 → 对比两者是否一致
// 3. 用完这张盐,立即作废(一次性使用)
}
生活类比:厨房验证 + 用完即撕
- 厨房拿到餐巾纸上的暗语
- 同时从自己的菜单上,用同样的规则算一遍 → 看暗语是否匹配
- 算完这张纸直接撕掉(盐值一次性使用),哪怕有人偷了这张已经撕掉的纸也没用
全程对比
【普通登录 — 普通餐厅】
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 你(用户) │────▶│ 服务员 │────▶│ 厨房 │
│ 密码:1234 │ │ (HTTPS) │ │ 验证 │
└──────────┘ └──────────┘ └──────────┘
▲
如果有摄像头
拍到完整密码 ❌
【盐值挑战 — 高级餐厅】
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 你(用户) │────▶│ 盐值纸条 │────▶│ 厨房 │
│ 先拿盐→ │ │ (1分钟 │ │ 对暗语 │
│ 再做变换→ │ │ 一次性) │ │ 用完撕掉 │
│ 发密钥 │────▶│ │ │ │
└──────────┘ └──────────┘ └──────────┘
▲
如果有摄像头
只拍到"暗语" + "废纸" ✅
安全性总结
"即使 HTTPS 被绕过(极少见),攻击者也只能看到客户端派生的密钥,无法还原原始密码。"
翻译: 就算餐厅的摄像头被拆了(HTTPS 被攻破),坏蛋拍到的也只是你写在餐巾纸上的"暗语"和"废纸"。没有那套 PBKDF2 转换规则和原始盐值,他无法反推出你最初输入的密码,更不可能在 1 分钟后再次使用。
关联笔记
- 金山办公作业/Week06/登录安全流程 - 登录安全流程总览,本文为其进阶篇