SCRAM-SHA-256

SCRAM-SHA-256(Salted Challenge Response Authentication Mechanism)是 PostgreSQL 10+ 默认推荐的密码认证方式,本质是基于挑战-响应(challenge-response)的 SASL 认证协议。它不会在网络上传输明文密码,也不会传输可以直接重放的密码哈希。

结合 PostgreSQL 协议来看,完整流程如下。


1. 用户密码保存阶段(服务器侧)

用户执行如下命令设置密码:

复制代码
ALTER USER test PASSWORD 'mypassword';

如果服务器配置了:

复制代码
password_encryption = scram-sha-256

那么服务器不会保存明文密码,而是保存类似下面的验证器(verifier):

复制代码
SCRAM-SHA-256$4096:<salt>$<StoredKey>:<ServerKey>

其中:

  • salt:随机盐
  • 4096:迭代次数(默认)
  • StoredKey
  • ServerKey

生成过程:

复制代码
SaltedPassword = Hi(password, salt, iterations)
ClientKey = HMAC(SaltedPassword, "Client Key")
StoredKey = SHA256(ClientKey)
ServerKey = HMAC(SaltedPassword, "Server Key")

服务器保存:

复制代码
salt
iteration count
StoredKey
ServerKey

不保存:

复制代码
明文密码
SaltedPassword

2. TCP连接建立

客户端连接 PostgreSQL:

复制代码
client                         server
  |                              |
  |---- StartupMessage --------> |
  |     user=test                |
  |     database=db              |
  |                              |

StartupMessage里面包含:

复制代码
user=test
database=db

3. Server 发起 SCRAM 请求

服务器发现:

复制代码
pg_hba.conf:
host all all 0.0.0.0/0 scram-sha-256

返回:

复制代码
AuthenticationSASL

内容:

复制代码
SCRAM-SHA-256
SCRAM-SHA-256-PLUS

表示支持哪些 SASL 机制。

协议:

复制代码
server
 |
 | AuthenticationSASL
 |
client

4. Client First Message

客户端选择:

复制代码
SCRAM-SHA-256

发送:

复制代码
client-first-message

例如:

复制代码
n,,n=test,r=fyko+d2lbbFgONRv9qkxdawL

含义:

字段 含义
n 不使用channel binding
n=test 用户名
r=xxx 客户端随机nonce

nonce是随机数。


5. Server First Message

服务器收到后:

  1. 根据用户名查找:

    pg_authid

找到:

复制代码
salt
iteration
StoredKey
ServerKey
  1. 生成自己的nonce

把:

复制代码
client nonce
+
server nonce

拼接。

返回:

复制代码
server-first-message

例如:

复制代码
r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvW,
s=QSXCR+Q6sek8bf92,
i=4096

字段:

字段 含义
r 完整nonce
s salt
i 迭代次数

6. Client计算Proof

客户端现在知道:

复制代码
password
salt
iteration
server nonce

计算:

第一步

计算:

复制代码
SaltedPassword

例如:

复制代码
Hi(
 password,
 salt,
 4096
)

第二步

计算:

复制代码
ClientKey
ClientKey =
HMAC(
SaltedPassword,
"Client Key"
)

第三步

计算:

复制代码
StoredKey
StoredKey =
SHA256(ClientKey)

第四步

生成认证消息:

复制代码
AuthMessage =
client-first-message
+
server-first-message
+
client-final-message-without-proof

第五步

计算:

复制代码
ClientSignature =
HMAC(
 StoredKey,
 AuthMessage
)

第六步

计算:

复制代码
ClientProof =
ClientKey XOR ClientSignature

发送:

复制代码
client-final-message

例如:

复制代码
c=biws,
r=xxx,
p=v0X8v3Bz...

其中:

复制代码
p = ClientProof

7. Server验证

服务器收到:

复制代码
ClientProof

自己计算:

复制代码
ClientSignature =
HMAC(
 StoredKey,
 AuthMessage
)

然后:

复制代码
ClientKey =
ClientProof XOR ClientSignature

得到:

复制代码
ClientKey

再计算:

复制代码
SHA256(ClientKey)

比较:

复制代码
SHA256(ClientKey)
==
StoredKey

如果一致:

认证成功。


8. Server返回最终证明

服务器发送:

复制代码
server-final-message

例如:

复制代码
v=rmF9pqV8S7suAoZWja4dJRkFsKQ=

这里:

复制代码
v = ServerSignature

客户端验证:

复制代码
ServerSignature =
HMAC(ServerKey, AuthMessage)

确认服务器是真的。


9. 最后 AuthenticationOk

服务器:

复制代码
AuthenticationOk

连接建立。


整个交互图

复制代码
Client                         Server
  |                              |
  | StartupMessage               |
  |----------------------------->|
  |                              |
  | AuthenticationSASL           |
  |<-----------------------------|
  |                              |
  | client-first-message         |
  |----------------------------->|
  |                              |
  | server-first-message         |
  |<-----------------------------|
  |                              |
  | client-final-message         |
  |  (ClientProof)               |
  |----------------------------->|
  |                              |
  | server-final-message         |
  |<-----------------------------|
  |                              |
  | AuthenticationOk             |
  |<-----------------------------|

和 MD5 认证区别

MD5 SCRAM-SHA-256
密码保存 md5(password+user) salt+StoredKey+ServerKey
网络传输 md5 response proof
防重放 一般 强
服务器是否知道密码 否 否
支持双向验证 否 是
支持channel binding 否 是

结合你之前分析 PostgreSQL/openGauss startup packet、Proxy认证、compute连接流程,SCRAM最关键的一点是:

Proxy如果伪装 PostgreSQL Server,需要完整实现 SCRAM 状态机,不能只转发用户名密码。

因为客户端发给 Proxy 的不是密码,而是:

复制代码
client-first-message
client-final-message(ClientProof)

Proxy必须:

  1. 保存用户SCRAM verifier(或转发到真正认证端)
  2. 生成server nonce
  3. 返回server-first-message
  4. 验证client proof

这也是 Neon Proxy / PostgreSQL兼容代理实现认证时比较复杂的地方。

相关推荐
ClickHouseDB2 小时前
ClickHouse Cloud 与 Snowflake 实时性价比对比:CostBench 测试中的 412 倍差距来
运维·数据库·系统架构
m0_639906942 小时前
Redis入门|五种数据结构、使用场景、持久化RDB&AOF
数据结构·数据库·redis
时间售卖铺3 小时前
触发器与函数实战:MySQL高效编程秘诀
运维·数据库·mysql·云原生
ZhengEnCi3 小时前
Agent 架构怎么选:从单 Agent 到多 Agent 编排的选型决策指南
算法·架构
qetfw3 小时前
UOS MariaDB 数据库:安装、最小权限与备份验证
linux·数据库·mariadb·uos
秋天的怀念_David4 小时前
第二十二夜 · 数字迷雾
java·程序人生·算法
数智园区合作社4 小时前
问数平台怎么把同一个指标锁成一个数
大数据·数据库·人工智能·ai
Pniubi4 小时前
力扣283移动零(双指针法)
算法·leetcode·职场和发展
hhb_6184 小时前
大模型落地三重优化实战
人工智能·算法·机器学习
云和数据.ChenGuang5 小时前
光哥说Python RAG 系统需要哪些评估指标:从检索到生成的全链路评测指南
网络·人工智能·python·算法·langchain