跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何在多服务器环境下隐藏系统数据库_分级展示与过滤规则

应禁用SHOW DATABASES权限并配合视图重写或RLS策略;Redis需禁用KEYS改用SCAN+ACL前缀控制;SDK须配置跳过库查询异常。 MySQL 多实例中如何避免
information_schema
暴露敏感库名 默认情况下,所有 mysql 实例都会把本地数据库名(包括业务库、中间件库、测试库)统一列在
information_schema.schemata
里,只要用户有
select
权限就能查到。这不是“显示不显示”的前端问题,而是权限与视图机制的底层行为。 真正可控的点只有两个:权限隔离 + 视图重写。不能靠应用层过滤,因为 DBA、运维、甚至某些监控脚本直连时都会绕过。 给非 DBA 用户分配账号时,**禁止授予
SHOW DATABASES
权限**——这是最直接的开关,缺省即关闭; 若必须保留该权限(如某些 ORM 初始化逻辑依赖),则需配合
sql_mode=STRICT_TRANS_TABLES
+ 自定义视图替代原
information_schema.schemata
查询路径; 注意:MySQL 8.0+ 的
admin_port
或
performance_schema
不会自动同步此限制,仍需单独处理。 PostgreSQL 中用
pg_catalog
实现分级可见性 PostgreSQL 没有全局“隐藏数据库”开关,它的库级隔离靠的是连接粒度——每个连接只属于一个数据库。但
pg_database
表本身对所有用户可读,所以关键不是“藏”,而是“让不该看的人查不到有效数据”。 典型做法是:用
row level security (RLS)
策略锁住
pg_database
,再结合角色继承链做动态过滤。 创建只读角色
app_reader
,不赋予
CREATEDB
或
CONNECT
到系统库的权限; 在
pg_database
上启用 RLS,并添加策略:
USING (datname IN ('prod', 'staging'))
; 注意:RLS 对超级用户无效,所以 DBA 账号必须严格管控,且不能用
SET ROLE
临时切换成普通角色来绕过; 某些客户端(如
psql \l
)会绕过 RLS 直接调用内部函数,此时需配合
pg_hba.conf
的
hostnossl
或
clientcert=verify-full
做连接层兜底。 Redis 多租户场景下如何防止
KEYS *
泄露库结构 Redis 本身没有“数据库分级”概念,
SELECT 0
到
SELECT 15
是平级编号,
KEYS *
默认扫当前 db,但很多运维误以为“不同 db 就天然隔离”。实际风险在于:一旦拿到任意 db 的访问权,就可能通过
CONFIG GET databases
推出总数量,再轮询所有 db 扫 key。 禁用
KEYS
命令(在
redis.conf
中加
rename-command KEYS ""
),改用
SCAN
+ 游标,且要求客户端必须带
MATCH
前缀约束; 为每个租户分配独立 Redis 实例或使用 Redis Modules(如 RedisJSON + ACL),而不是靠 db 编号区分; ACL 规则示例:
ACL SETUSER tenant_a on >pwd ~tenant_a:* allcommands
—— 注意
~
后面的 pattern 必须显式限定 key 前缀,否则
~*
等同于开放全部; Redis 7.0+ 支持
ACL LOG
,建议开启并对接 SIEM,因为 ACL 误配比命令禁用更容易被忽略。 跨语言 SDK 连接池中过滤掉系统库名的通用技巧 很多语言的数据库驱动(如 Python 的
mysql-connector-python
、Go 的
database/sql
)在初始化时会自动执行
SHOW DATABASES
或等效查询来构建元数据缓存。如果后端已做权限控制,但 SDK 仍报错或返回空列表,大概率是驱动没适配新权限模型。 Python:
mysql.connector.connect(..., get_warnings=False, raise_on_warnings=False)
可避免因
SHOW DATABASES
权限不足导致 connect() 报
Access denied
; Go:
sql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/?multiStatements=true")
中去掉末尾的
/dbname
,否则驱动会尝试
USE dbname
并触发权限检查; Java JDBC:
useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
这类参数不影响权限,但
zeroDateTimeBehavior=CONVERT_TO_NULL
若配置不当,可能让驱动在初始化阶段因类型转换失败而静默跳过库列表获取; 所有 SDK 都应设置
maxIdleTime
和
connectionTimeout
,否则连接池复用旧连接时,可能缓存了上一个用户看到的库列表。 系统数据库不是靠“前端不展示”来隐藏的,而是靠连接建立那一刻的权限边界、协议层响应截断、以及 SDK 对异常的容忍策略共同决定的。最容易被漏掉的是连接池生命周期和驱动初始化顺序——它们往往在业务代码之前就完成了元数据拉取。

相关文章