You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pgbouncer输入错误用户名触发“FATAL: bouncer config error”的疑问

PgBouncer SCRAM-SHA-256认证配置问题

问题背景

我正在尝试用SCRAM-SHA-256配置PgBouncer的简单认证,相关配置及测试情况如下:

PgBouncer配置文件(pgbouncer.ini)

[databases]
* = host=localhost port=5432 auth_user=pgbouncer

[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/users.txt
auth_query = SELECT uname, phash from pgbouncer.user_lookup($1)
admin_users = pgbouncer
stats_users = pgbouncer
default_pool_size = 20
max_client_conn = 1000
pool_mode = transaction
logfile = /var/log/pgbouncer/pgbouncer.log

用户密码文件(users.txt)

"pgbouncer"     "pgbouncer"

数据库查询函数(user_lookup)

CREATE OR REPLACE FUNCTION pgbouncer.user_lookup(in i_username text, out uname text, out phash text)
RETURNS record AS $$
BEGIN
    SELECT usename, passwd FROM pg_catalog.pg_shadow
    WHERE usename = i_username INTO uname, phash;
    RETURN;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
REVOKE ALL ON FUNCTION pgbouncer.user_lookup(text) FROM public, pgbouncer;
GRANT EXECUTE ON FUNCTION pgbouncer.user_lookup(text) TO pgbouncer;

测试现象

  • 使用postgres用户通过PgBouncer登录正常
  • 使用不存在的用户名wrong_user登录时触发错误:
[root@repmgr1 pgbouncer]# psql -U wrong_user -p 6432 -h localhost -d postgres
psql: error: connection to server at "localhost" (::1), port 6432 failed: FATAL:  bouncer config error

PgBouncer日志内容:

2024-07-17 22:50:39.483 MSK [22172] LOG S-0x55c67b0dc930: postgres/pgbouncer@127.0.0.1:5432 closing because: auth_query response contained null user name (age=34s)
2024-07-17 22:50:39.483 MSK [22172] LOG C-0x55c67b0d3f40: postgres/(nouser)@[::1]:57790 closing because: bouncer config error (age=0s)
2024-07-17 22:50:39.483 MSK [22172] WARNING C-0x55c67b0d3f40: postgres/(nouser)@[::1]:57790 pooler error: bouncer config error

疑问

  1. 这种输入错误用户名就返回明确错误的现象是PgBouncer的正常行为吗?
  2. 是否需要优化user_lookup()函数,让它在查询不到用户时返回非空值?
  3. 当前这种行为会不会导致暴力枚举有效用户名的风险?

解答

1. 该现象是PgBouncer的正常行为吗?

是正常行为。当auth_query返回的结果中uname或phash为NULL时,PgBouncer会判定为认证失败,并抛出bouncer config error这类错误。这是因为PgBouncer无法从查询结果中获取到用于SCRAM认证所需的有效用户名和哈希值,进而终止连接。

2. 是否需要优化user_lookup()函数?

非常有必要优化,主要有两个原因:

  • 避免暴露用户存在性:当前行为会让攻击者通过错误信息差异(合法用户会走正常认证流程,非法用户直接返回配置错误)来枚举有效用户名,存在安全风险。
  • 统一认证失败提示:优化后可以让非法用户的错误提示和密码错误的提示一致,混淆攻击者的判断。

优化后的user_lookup()函数可以在查询不到用户时,返回一个无效的用户名和哈希值(比如固定的占位符),这样PgBouncer会继续执行SCRAM认证流程,但最终会因为哈希不匹配而返回“密码错误”的通用提示:

CREATE OR REPLACE FUNCTION pgbouncer.user_lookup(in i_username text, out uname text, out phash text)
RETURNS record AS $$
BEGIN
    SELECT usename, passwd FROM pg_catalog.pg_shadow
    WHERE usename = i_username INTO uname, phash;
    
    -- 如果查询不到用户,返回无效的占位符(需确保该哈希不是任何真实用户的密码哈希)
    IF uname IS NULL THEN
        uname = 'invalid_user';
        phash = 'SCRAM-SHA-256$4096:abc123$xyz789=:xyz789=';
    END IF;
    
    RETURN;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
REVOKE ALL ON FUNCTION pgbouncer.user_lookup(text) FROM public, pgbouncer;
GRANT EXECUTE ON FUNCTION pgbouncer.user_lookup(text) TO pgbouncer;

注意:这里的占位符哈希可以随便生成一个无效的SCRAM哈希(比如用一个测试密码生成后复制过来),只要确保它不会匹配任何真实用户的密码即可。

3. 当前是否存在暴力枚举风险?

是的。当前的错误提示差异非常明显:

  • 合法用户名:即使密码错误,PgBouncer会返回FATAL: password authentication failed这类提示,且耗时会包含SCRAM认证的交互过程。
  • 非法用户名:直接返回FATAL: bouncer config error,耗时极短。

攻击者可以利用这种差异,快速枚举系统中存在的有效用户名,进而针对这些用户名进行暴力破解密码的攻击。优化函数后,所有认证失败的提示都会统一为密码错误,且耗时一致,能有效降低这种风险。


内容的提问来源于stack exchange,提问作者elfcheg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 19:31:02