SCRAM 认证协议详解:SASL 盐值挑战响应机制
定义与历史背景
SCRAM(Salted Challenge Response Authentication Mechanism)是一种基于挑战-响应的密码认证协议,设计用于替代明文传输密码的传统认证方式。该协议由 IETF KITTEN 工作组标准化,核心规范为:
- RFC 5802(2010):定义 SCRAM-SHA-1 基础机制
- RFC 7667(2015):扩展支持 SCRAM-SHA-256 和通道绑定
- RFC 7668(2015):定义 RFC 7667 的 TLS 扩展
算法原理
核心公式
SCRAM 使用以下密码学原语:
$$H(password) = HMAC-SHA(N, password)$$
其中:
- $N$ = 归一化后的用户名
- $H$ = HMAC 函数(基于 SHA-1 或 SHA-256)
- $i$ = 迭代计数(建议 ≥ 4096)
客户端计算: $$ClientKey = HMAC(Hi(password, salt, i), "client key")$$ $$Signature = HMAC(ServerKey, message)$$
认证流程
第一步:客户端 → 服务器
CODE
c-first = n,rs=base64(salt),i=iteration-count- $n$:用户名(归一化)
- $rs$:服务器生成的随机盐值
- $i$:迭代次数
CODE
s-first = r=challenge, e=error- $r$:服务器随机挑战(用于构建通道绑定)
- $e$:错误信息(如存在)
CODE
c-final = c=channel-binding, r=nonce, p=client-proof- $p$:客户端证明 = HMAC(ClientKey, auth-message)
- $auth-message$ = c-firstᵢ, s-first, c-finalᵢ
CODE
s-final = v=server-proof, e=error- $v$:服务器验证 = HMAC(ServerKey, auth-message)
通道绑定(Channel Binding)
SCRAM-SHA-256-PLUS 变体要求通道绑定,防止中间人攻击:
| 绑定类型 | gs2-cbind-flag | 适用场景 |
|---|---|---|
| 无绑定 | n | 无安全通道环境 |
| TLS 绑定 | p | TLS 1.3+ session-hash 扩展 |
| PFS 绑定 | y | 不支持通道绑定时使用 |
-PLUS 后缀机制。安全分析
威胁模型
SCRAM 设计应对以下攻击:
- 网络窃听:密码不以明文传输,仅传输 HMAC 值
- 暴力破解:盐和迭代计数显著提高离线攻击成本
- 重放攻击:每次认证使用不同的随机挑战 $r$
- 中间人攻击:通过通道绑定验证 TLS 会话完整性
已知漏洞与修复
RFC 7627 发现(2015):
TLS 1.2 的 tls-unique 绑定存在中间人攻击风险。修复方案:
- 使用 TLS 1.3 的
session-hash扩展 - 或在应用层验证证书指纹
- SHA-1 最低 4096 次
- SHA-256 建议 4096~15000 次
- 移动端设备需权衡安全与性能
与国密算法的对比
SCRAM 协议可与国密 SM3 结合使用:
| 特性 | SCRAM-SHA-256 | 国密适配方案 |
|---|---|---|
| 哈希函数 | SHA-256 | SM3 |
| HMAC 变体 | HMAC-SHA-256 | HMAC-SM3 |
| OID | 1.3.6.1.5.5.18 | 待 IANA 分配 |
| 应用场景 | PostgreSQL/MongoDB | 国密改造场景 |
实际应用
PostgreSQL 配置示例
SQL
-- 查看支持的认证方式
SELECT * FROM pg_authid WHERE usename = 'admin';
-- 修改 scram-sha-256 认证
ALTER ROLE admin PASSWORD 'secure_password';pg_hba.conf:
CODE
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256MongoDB 连接示例
JAVASCRIPT
const uri = 'mongodb://user:pass@host:27017/db?authMechanism=SCRAM-SHA-256';
const client = new MongoClient(uri);Nginx 反向代理配置
NGINX
location /api/ {
auth_request /auth;
}
location = /auth {
internal;
proxy_pass http://backend/auth-check;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}后端认证服务验证 SCRAM 响应后返回 200/401。
与其他认证协议的对比
| 协议 | 安全性 | 性能开销 | 兼容性 |
|---|---|---|---|
| SCRAM-SHA-1 | 中等 | 低 | 广泛 |
| SCRAM-SHA-256 | 高 | 中等 | 较好 |
| Kerberos | 极高 | 高 | 企业级 |
| LDAP Simple | 低 | 低 | 遗留系统 |
| OAuth 2.0 | 高 | 中等 | Web/API |
- 数据库连接:优先 SCRAM-SHA-256
- 企业统一认证:Kerberos 或 OAuth 2.0
- 国密场景:待 GM/T 标准完善后评估