数据库安全第一关:用户密码存储与认证机制的深度拆解

数据库安全第一关:用户密码存储与认证机制的深度拆解

从一个让人困惑的报错说起

几个月前,团队里的一位运维同事遇到了一个让他百思不得其解的问题:

"新建了一个用户,密码明明输对了,为什么就是连不上?"

他在KingbaseES里执行了CREATE USER app_user WITH PASSWORD 'App@2026',然后在客户端用这个用户名和密码去连接,结果反复提示认证失败。他确认了三次密码没有输错,确认了用户没有被锁定,确认了网络是通的------一切都正常,但就是连不上。

我让他执行了下面这条查询:

sql 复制代码
SELECT rolname, rolpassword FROM sys_authid WHERE rolname = 'app_user';

然后又看了s alt="sys_hba.conf里的配置。问题很快就定位了:用户密码存储用的是默认的scram-sha-256算法,但s alt="sys_hba.conf里配置的认证方式是md5。存储算法和认证方法不匹配,导致虽然密码正确,但认证流程走不通。

这个问题让我意识到,很多数据库使用者对"密码怎么存"和"密码怎么验"这两个概念是混淆的。这篇文章就来彻底拆解一下这两套机制。

第一章:两个容易混淆的核心概念

1.1 存储 vs 认证:各管一摊

先看一张对比表:

维度 密码存储加密 口令认证
解决什么问题 数据文件泄露后,密码原文是否安全 网络传输时,口令会不会被窃取
控制参数 password_encryption sys_hba.conf中的METHOD
默认值 scram-sha-256 scram-sha-256
什么时候起作用 创建/修改用户密码时 客户端连接数据库时
打个比方 把密码写进保险箱的方式 门卫核对身份的方式

这两个概念之所以容易被混淆,是因为它们确实有关联------认证方法必须兼容存储算法,否则就会像开头那位同事一样,密码正确却登录失败。但它们的职责是分离的:一个管"存",一个管"验"。

1.2 密码到底存在哪里?

所有用户的密码信息都存储在sys_authid系统表中:

sql 复制代码
-- 查看用户密码存储信息
SELECT rolname, rolpassword, rolvaliduntil 
FROM sys_authid 
WHERE rolname IN ('system', 'fin_app');

输出示例:

ruby 复制代码
   rolname   |                             rolpassword                             
-------------+---------------------------------------------------------------------
 system      | SCRAM-SHA-256$4096:SBfzuqMqQjEWP/w+n8TlwQ==$WeOQ2yOLEGvYp9v0DS5F...
 fin_app     | SCRAM-SHA-256$4096:kOPAYRYx2elBrNaeCo18BQ==$4xV0YdFPq7Uq2UvGqJ2H...

rolpassword这个字段存储的不是密码原文,而是经过哈希算法处理后的值。哈希算法是单向的------从哈希值无法还原出原始密码,这正是密码存储安全的基础。

1.3 准备测试用户

为方便后续演示,先创建几个测试用户:

sql 复制代码
-- 创建演示数据库
CREATE DATABASE demodb;

-- 创建四个测试用户
CREATE USER fin_app WITH PASSWORD 'SecurePass!2026';          -- 金融应用
CREATE USER teaching_assistant WITH PASSWORD 'Kingbase_123';  -- 教学应用
CREATE USER legacy_mercury WITH PASSWORD 'Mercury@2026';      -- 遗留系统
CREATE USER gov_finance WITH PASSWORD 'Gov@2026';             -- 政务系统

这四个用户会贯穿全文,分别对应不同的存储算法和认证方式组合。

第二章:密码存储------数据文件里的安全防线

2.1 支持的存储算法

KingbaseES支持多种密码存储算法,通过password_encryption参数控制。

通用算法:

参数值 算法类型 存储格式示例 安全等级
scram-sha-256 SCRAM + SHA-256 SCRAM-SHA-256$4096:盐$密钥
md5 MD5哈希 md5 + 32位十六进制哈希

国密算法(V9R1+版本支持):

参数值 算法类型 存储格式示例 安全等级
sm3 国密SM3 sm3 + 64位十六进制哈希
scram-sm3 SCRAM + SM3 SCRAM-SM3$...

2.2 查看当前用户的存储格式

sql 复制代码
SELECT 
    rolname AS 用户名,
    CASE 
        WHEN rolpassword IS NULL THEN '未设置密码'
        WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'SCRAM-SHA-256'
        WHEN rolpassword LIKE 'SCRAM-SM3$%' THEN 'SCRAM-SM3'
        WHEN rolpassword LIKE 'md5%' THEN 'MD5'
        WHEN rolpassword LIKE 'sm3%' THEN 'SM3'
        ELSE '未知格式'
    END AS 存储算法
FROM sys_authid 
WHERE rolname IN ('fin_app', 'teaching_assistant', 'legacy_mercury', 'gov_finance');

默认情况下,所有用户的存储算法都是scram-sha-256

2.3 修改存储算法(两步法)

这里有一个常见误区 :很多人以为执行了ALTER ROLE ... SET password_encryption = 'md5'就算改完了。实际上,这只改了参数,已存在的密码并不会自动转换格式。

正确的做法是两步:

sql 复制代码
-- 第一步:为用户设置默认存储算法
ALTER ROLE legacy_mercury SET password_encryption = 'md5';

-- 第二步:重置密码(这一步不能少!)
-- 注意:当前会话必须先设置参数,再重置密码
SET password_encryption = 'md5';
ALTER USER legacy_mercury PASSWORD 'Mercury@2026';

如果不做第二步,密码仍然是旧的存储格式。这一点在运维中经常被忽略,导致明明改了配置,密码格式却没变。

2.4 参数优先级

password_encryption可以在多个层级设置,优先级从高到低:

  1. 会话级:SET password_encryption = 'sm3';
  2. 用户级:ALTER ROLE fin_app SET password_encryption = 'sm3';
  3. 数据库级:ALTER DATABASE demodb SET password_encryption = 'scram-sm3';
  4. 实例级:ALTER SYSTEM SET password_encryption = 'scram-sm3';

查看参数是否需要重启:

sql 复制代码
SELECT name, context, pending_restart 
FROM sys_settings 
WHERE name = 'password_encryption';

context = user表示可以在各层级动态设置,pending_restart = f表示修改后无需重启数据库。

第三章:口令认证------连接时的身份核验

配置好密码存储后,还需要在sys_hba.conf中指定认证方法,客户端才能成功连接。

3.1 认证方法一览

认证方法 安全等级 说明 适用版本
scram-sha-256 SCRAM挑战-响应,抗嗅探、抗重放 所有版本
scram-sm3 SCRAM + SM3国密 V9R1+
sm3 国密SM3哈希认证 V9R1+
md5 兼容老客户端 所有版本
password 极低 明文密码传输 仅限测试

3.2 sys_hba.conf配置示例

根据前面设置的存储算法,配置对应的认证方法:

sql 复制代码
# TYPE    DATABASE    USER                ADDRESS             METHOD
# -----------------------------------------------------------------------
local     all         system                                  peer
host      all         fin_app             192.168.126.0/24    scram-sha-256
host      all         teaching_assistant  192.168.126.0/24    scram-sha-256
host      all         legacy_mercury      192.168.126.0/24    md5
host      all         gov_finance         192.168.126.0/24    sm3

配置完成后,需要重载才能生效:

bash 复制代码
sys_ctl reload -D $KINGBASE_DATA

或者从SQL中执行:

sql 复制代码
SELECT sys_reload_conf();

3.3 验证当前生效的认证规则

sql 复制代码
SELECT type, user_name, address, auth_method
FROM sys_hba_file_rules
WHERE user_name != '{system}'::text[];

3.4 两种认证流程的差异

SCRAM-SHA-256流程(安全):

客户端和服务器之间通过挑战-响应机制验证身份。网络传输的不是密码本身,也不是简单的哈希值,而是经过加盐、多次迭代计算后的证明。每次认证使用不同的随机数,可以防止重放攻击。

SM3流程(相对简单):

客户端直接计算密码的SM3哈希值发送给服务器验证。流程更简单,但缺乏挑战-响应机制,不具备抗重放能力。

这就是为什么国密合规场景中,更推荐使用scram-sm3而不是sm3------前者在满足国密要求的同时,提供了更高的安全性。

第四章:当存储遇上认证------兼容性测试

理解了存储和认证各自的原理,接下来看看它们如何协同工作------以及不匹配时会发生什么。

4.1 当前配置状态

经过前面的配置,四个测试用户的状态如下:

用户 存储算法 认证方法 预期结果
fin_app SCRAM-SHA-256 scram-sha-256 成功
teaching_assistant SCRAM-SHA-256 scram-sha-256 成功
legacy_mercury md5 md5 成功
gov_finance sm3 sm3 成功

4.2 测试一:全部改用MD5认证

临时修改sys_hba.conf,将所有用户的认证方法改为md5,重载配置后测试:

bash 复制代码
ksql -U legacy_mercury -d demodb -h 192.168.126.16   # 成功
ksql -U fin_app -d demodb -h 192.168.126.16          # 成功
ksql -U gov_finance -d demodb -h 192.168.126.16      # 成功

结论:MD5认证方式兼容所有存储算法。但它安全性较低,只适合遗留系统过渡。

4.3 测试二:全部改用SCRAM-SHA-256认证

将所有用户的认证方法改为scram-sha-256

bash 复制代码
ksql -U legacy_mercury -d demodb -h 192.168.126.16   # 失败
ksql -U fin_app -d demodb -h 192.168.126.16          # 成功
ksql -U gov_finance -d demodb -h 192.168.126.16      # 失败

结论:SCRAM-SHA-256只兼容同族算法(即存储格式也是scram-sha-256的用户)。

4.4 测试三:全部改用SM3认证

将所有用户的认证方法改为sm3

bash 复制代码
ksql -U legacy_mercury -d demodb -h 192.168.126.16   # 失败
ksql -U fin_app -d demodb -h 192.168.126.16          # 失败
ksql -U gov_finance -d demodb -h 192.168.126.16      # 成功

结论:SM3认证只兼容SM3存储算法。

4.5 兼容性矩阵

认证方法 \ 存储算法 MD5 SCRAM-SHA-256 SM3
md5
scram-sha-256
sm3

核心规律

  • MD5认证可以验证所有存储算法(但安全性低)
  • 同族算法的存储和认证完全兼容
  • 通用算法与国密算法互不兼容

第五章:实际运维中的场景与排查

5.1 场景一:创建新用户(标准流程)

sql 复制代码
-- 第一步:确认当前默认算法
SHOW password_encryption;

-- 第二步:创建用户
CREATE USER new_fin_app WITH PASSWORD 'StrongPwd!2026';

-- 第三步:验证存储格式
SELECT rolname, 
       CASE WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'SCRAM-SHA-256'
            WHEN rolpassword LIKE 'md5%' THEN 'MD5'
       END AS algorithm
FROM sys_authid WHERE rolname = 'new_fin_app';

配套HBA配置:

复制代码
host    demodb    new_fin_app    192.168.126.0/24    scram-sha-256

5.2 场景二:密码正确但认证失败(最常见的问题)

现象:用户报错认证失败,但密码确认无误。

排查步骤

第一步,查存储格式:

sql 复制代码
SELECT rolpassword FROM sys_authid WHERE rolname = '用户名';

看前缀判断是SCRAM-SHA-256md5还是sm3

第二步,查认证规则:

sql 复制代码
SELECT auth_method FROM sys_hba_file_rules 
WHERE user_name = '{用户名}'::text[];

第三步,对照兼容性矩阵判断:

  • 同族 → 正常,检查其他原因
  • 存储MD5 + 认证SCRAM → 需要重置密码升级
  • 跨族(SCRAM vs SM3) → 需要统一算法

根因:存储算法与认证方法不匹配。

解决方案:二选一

  • 方案A(推荐):重置密码,使用与认证方法匹配的存储算法
  • 方案B(临时):修改HBA配置,使用兼容的认证方法

5.3 场景三:密码过期

现象:用户突然无法登录,提示密码过期。

排查

sql 复制代码
SELECT rolname, rolvaliduntil FROM sys_authid WHERE rolname = '用户名';

如果rolvaliduntil不为空且小于当前时间,说明密码已过期。

解决方案

sql 复制代码
ALTER USER 用户名 VALID UNTIL 'infinity';
-- 或者设置一个新的过期时间
ALTER USER 用户名 VALID UNTIL '2027-01-01 00:00:00';

5.4 场景四:新建用户仍是旧算法

现象 :明明改了password_encryption参数,新建的用户却还是MD5格式。

排查

sql 复制代码
-- 查看当前会话的参数值
SHOW password_encryption;

-- 检查是否有用户级覆盖
SELECT rolname, rolconfig FROM sys_authid WHERE rolname = '用户名';

根因:会话级或用户级的参数设置覆盖了全局配置。

解决方案

sql 复制代码
-- 重置当前会话的参数
RESET password_encryption;

-- 或重置用户级参数
ALTER ROLE 用户名 RESET password_encryption;

第六章:快速诊断工具箱

6.1 一键查看所有用户状态

sql 复制代码
SELECT 
    rolname,
    CASE 
        WHEN rolpassword LIKE 'SCRAM-SHA-256$%' THEN 'SCRAM'
        WHEN rolpassword LIKE 'md5%' THEN 'MD5'
        WHEN rolpassword LIKE 'sm3%' THEN 'SM3'
        WHEN rolpassword IS NULL THEN '未设置'
    END AS 存储算法,
    (SELECT auth_method FROM sys_hba_file_rules 
     WHERE user_name = ('{' || rolname || '}')::text[] 
        OR user_name = '{all}'::text[]
     LIMIT 1) AS 认证方法,
    rolvaliduntil AS 密码过期时间
FROM sys_authid 
WHERE rolcanlogin = true 
  AND rolname NOT IN ('system', 'kingbase');

6.2 排查要点速查

故障现象 可能原因 关键检查 解决方案
密码正确但认证失败 存储与认证不匹配 查rolpassword和HBA 重置密码或修改HBA
新建用户仍是MD5 会话级参数覆盖 SHOW password_encryption RESET参数
提示密码过期 超过有效期 查rolvaliduntil ALTER USER设置有效期
国密用户连不上 存储用SCRAM,认证用SM3 查存储格式和HBA 重置密码为国密格式

写在最后

回到开头那个让人困惑的问题:密码正确为什么连不上?

答案其实不复杂:密码存储和口令认证是两套独立的机制。存储算法决定密码怎么存在数据文件里,认证方法决定客户端连接时怎么验证。两者必须兼容,否则就会认证失败。

理解了这个逻辑,排查这类问题时就有了清晰的思路:先查sys_authid看存储格式,再查sys_hba.conf看认证方法,然后对照兼容性矩阵判断问题所在。

数据库安全是一个系统工程,而用户认证是第一道关口。希望这篇文章能帮助你在日常运维中少踩一些坑。

相关推荐
硅基诗人2 小时前
Java后端高并发核心瓶颈突破(JVM+并发+分布式底层实战)
java·jvm·分布式
星空椰2 小时前
JavaScript 基础入门:从零开始掌握变量与数据类型
开发语言·前端·javascript·ecmascript
聆听。。花开雨落2 小时前
intelij idea闪退后再启动tomcat报错端口冲突
java·tomcat·intellij-idea
Java面试题总结2 小时前
Spring Boot 包扫描新姿势:AutoScan vs @Import vs @ComponentScan 深度对比
java·数据库·spring boot
千寻简2 小时前
一个让 Claude Code 顺手很多的状态栏插件:claude-hud
前端·后端
花千树-0102 小时前
McpAgentExecutor 混合挂载:HTTP 工具与 NPX 服务器同时接入同一 Agent
java·agent·function call·spring ai·mcp·toolcall·java ai
HelloReader2 小时前
QML 最佳实践写出高质量、可维护、高性能的代码(十二)
前端
未秃头的程序猿2 小时前
💥 MyBatis 面试连环炮:从源码原理到实战避坑,彻底拿下 Offer 通关秘籍
后端·面试·mybatis
HelloReader2 小时前
Qt Quick Controls 全览控件、弹窗、导航与样式定制(十一)
前端