正文
数据库端口不是 API,不应该直接暴露给任何能连上公网的人。页面有登录、接口有鉴权、后台有权限,都不能替代数据库层的网络隔离。只要 Redis、ClickHouse、Elasticsearch、Kibana、Qdrant、Weaviate、Milvus 这类服务被放到公网,攻击者可能绕过应用层,直接读数据、删数据、改索引、枚举集合、下载向量 payload,甚至借助管理接口继续横向移动。
真实事故已经反复说明,问题经常不是复杂漏洞,而是“门开着”。
2025-01-29,Wired 报道 Wiz 研究人员发现 DeepSeek 一个 ClickHouse 数据库暴露在互联网上,里面包含系统日志、用户 prompt、API authentication tokens 等超过 100 万条记录。The Verge 也报道,这个数据库无需认证即可访问,并且可能允许完整数据库控制和权限提升。这个案例对 AI 应用尤其直接:prompt、日志、API key、路由路径、内部元数据都可能被写进分析库,分析库一旦公网裸露,应用层再多登录态也挡不住。
2018-06-27,Wired 报道营销公司 Exactis 暴露了约 3.4 亿条个人和企业记录。研究人员用 Shodan 搜索公开可访问的 Elasticsearch 实例时发现了这个数据库。它不一定是“黑客入侵”造成的传统漏洞,但对用户来说结果一样:大量个人画像、手机号、住址、邮箱和兴趣标签被放到了可被发现的服务器上。
Redis 官方安全文档也写得很明确:Redis 设计上通常应由可信环境里的可信客户端访问,不建议把 Redis 实例直接暴露到互联网;Redis 端口应该只允许可信客户端访问。Qdrant 官方文档同样提醒,自部署 Qdrant 默认没有认证,并且监听所有网络接口,生产前必须配置认证、审计日志、网络绑定和 TLS。
上线前先查这些入口:
| 服务 | 常见端口 | 暴露后最常见问题 |
|---|---|---|
| Redis | 6379 | 读缓存、删 key、写入恶意数据、执行危险命令 |
| ClickHouse | 8123 / 9000 | 查询日志、prompt、事件流、内部表 |
| Elasticsearch | 9200 / 9300 | 枚举索引、导出文档、删除索引 |
| Kibana | 5601 | 看仪表盘、保存对象、间接访问 Elasticsearch |
| Qdrant | 6333 / 6334 / 6335 | 枚举 collection、读取 payload、写入向量 |
| Milvus | 19530 | 访问 collection、搜索向量、读取元数据 |
| Weaviate | 8080 | 查询 schema、对象、向量和模块配置 |
先列出哪些数据库能被访问
不要只看应用代码。数据库可能从 Docker、Kubernetes、云安全组、临时测试机、旧域名、VPN 旁路、监控面板或备份服务暴露出去。
先扫仓库里的端口和服务名:
rg -n "6379|8123|9000|9200|9300|5601|6333|6334|6335|19530|8080|redis|clickhouse|elasticsearch|kibana|qdrant|weaviate|milvus" .再重点查部署文件:
rg -n "ports:|hostPort|NodePort|LoadBalancer|0\\.0\\.0\\.0|allow|cidr|securityGroup|ingress" docker-compose.yml docker-compose.yaml Dockerfile k8s deploy .github把每个数据库写成一张表:
| 字段 | 示例 |
|---|---|
| 服务 | Redis / ClickHouse / Elasticsearch / Qdrant |
| 环境 | dev / staging / production |
| 监听地址 | 127.0.0.1 / 私网 IP / 0.0.0.0 |
| 入口 | 内网、VPN、堡垒机、公网 LB、Kubernetes Service |
| 调用方 | 应用服务、后台任务、BI、监控、AI Agent |
| 凭据 | 无、密码、ACL、API key、TLS client cert |
| 数据类型 | 缓存、日志、订单、prompt、向量 payload |
通过标准:生产数据库没有直接公网入口;确实需要跨网络访问时,走私网连接、VPN、堡垒机、PrivateLink 或受控网关。
不要把数据库端口当 API 发布
数据库端口暴露和 API 暴露不是一个级别的风险。API 至少还能在业务层做鉴权、限流、字段过滤、审计和错误处理;数据库端口一旦暴露,攻击者面对的是原始查询能力。
危险写法:
services:
redis:
image: redis:latest
ports:
- "6379:6379"
elasticsearch:
image: elasticsearch:8.19.0
ports:
- "9200:9200"本机开发可以只绑定本地回环:
services:
redis:
image: redis:latest
ports:
- "127.0.0.1:6379:6379"生产环境更稳的方式是不要发布端口,只让同一私有网络里的应用服务访问:
services:
redis:
image: redis:latest
expose:
- "6379"通过标准:生产 Docker Compose、Kubernetes、云主机和负载均衡器里,看不到数据库端口对 0.0.0.0/0 或公开 IP 开放。
Redis 先查 bind、protected mode 和 ACL
Redis 官方文档强调,Redis 通常应该被可信环境里的可信客户端访问,不建议直接暴露到互联网。它还特别提醒,外部攻击者一个 FLUSHALL 就能删除整个数据集。
先查配置:
rg -n "bind |protected-mode|requirepass|user |rename-command|aclfile" redis.conf docker-compose.yml .env .env.example危险信号:
| 配置 | 风险 |
|---|---|
bind 0.0.0.0 | 所有网卡都能接收连接 |
protected-mode no | 关闭默认保护 |
| 没有 ACL / 密码 | 能连上就能操作 |
业务账号可执行 FLUSHALL、CONFIG | 应用被打穿后可扩大破坏 |
| 明文 Redis 密码进入日志或镜像 | 凭据可被二次利用 |
本地开发可以绑定回环:
bind 127.0.0.1
protected-mode yes生产至少要做到:
- 只允许应用服务所在私网访问 Redis。
- 使用 Redis 6+ ACL,按应用创建独立用户。
- 禁止业务账号执行
FLUSHALL、CONFIG、KEYS、危险 Lua 脚本。 - 开启 TLS 或放在受控私有网络里。
- 不把 Redis 当长期事实数据库保存敏感数据。
通过标准:从公网机器无法连通 Redis;从应用机器连接时需要认证;业务账号只能执行它需要的命令。
ClickHouse 先查 8123、9000 和 default 用户
ClickHouse 常用于日志、事件、埋点、BI 和 AI prompt 分析。它的数据看起来像“日志”,实际可能包含用户输入、API key、内部路径、手机号、订单号、设备标识和业务事件。
先查端口:
rg -n "8123|9000|9004|9005|listen_host|clickhouse" .ClickHouse 文档里 listen_host 用来限制请求来源。如果配置成回答所有地址,数据库端口就可能被外部访问。
危险信号:
| 配置或现象 | 风险 |
|---|---|
<listen_host>0.0.0.0</listen_host> | 对所有 IPv4 地址监听 |
<listen_host>::</listen_host> | 对所有 IPv6 地址监听 |
default 用户仍可远程登录 | 默认账号风险过高 |
| 用户无密码或弱密码 | 被扫到后可直接查询 |
| 查询日志长期保存 prompt / token | 数据库本身变成敏感日志仓库 |
| BI 工具直连生产库 | 用户权限和查询审计容易失控 |
更稳的配置思路:
listen_host: 127.0.0.1 or private IP
users: no default remote login
roles: read-only role for BI
quotas: query complexity and rate limits
logs: no raw token, no full prompt, no payment payload通过标准:ClickHouse 的 HTTP/TCP 端口只在私网可达;默认用户不能远程访问;BI、应用、分析任务使用不同账号和最小权限。
Elasticsearch 和 Kibana 要一起查
Elasticsearch 暴露通常不只暴露搜索。它可能包含业务索引、日志索引、错误堆栈、用户画像、订单、客服记录、地理位置、设备信息和内部事件。
先查端口和安全配置:
rg -n "9200|9300|5601|elasticsearch|kibana|xpack.security|ELASTIC_PASSWORD|network.host" .危险信号:
| 配置或现象 | 风险 |
|---|---|
network.host: 0.0.0.0 | 可能对外监听 |
| 没有认证和 TLS | 能连上就能查索引 |
| Kibana 公网开放 | 通过 UI 看数据和 saved objects |
| 日志索引保存完整 headers/body | token、cookie、手机号、prompt 二次泄露 |
| 统一使用管理员账号 | 应用、BI、运维无法隔离 |
Elastic 官方文档把认证授权、TLS、API keys、IP filters、PrivateLink/VPC filtering 都放在安全配置里。生产环境不要把 Elasticsearch 当成“只读搜索服务”裸露给前端或公网。
通过标准:
- Elasticsearch HTTP 端口不对公网开放。
- Kibana 有 SSO/认证,不允许匿名访问内部 dashboard。
- 应用账号只拥有指定索引的读写权限。
- 日志索引有字段脱敏和保留期限。
- 快照仓库不公开,恢复权限单独控制。
向量库先查 API key、payload 和 collection
向量库容易被低估,因为开发者以为里面只是 embedding。实际生产 RAG 系统里,向量库经常保存:
- 原文片段。
- 文档标题、URL、用户 ID、租户 ID。
- 客服对话、合同条款、简历内容。
- 权限标签、部门、项目名。
- prompt cache、工具调用结果。
Qdrant 官方文档明确提醒,自部署 Qdrant 默认不安全:监听所有网络接口,没有认证,可能被互联网上任何能访问的人调用。生产前应配置 API key、审计日志、网络绑定和 TLS。
先查向量库端口:
rg -n "6333|6334|6335|19530|8080|qdrant|weaviate|milvus|chroma|vector|embedding|collection" .危险信号:
| 服务 | 风险点 |
|---|---|
| Qdrant | 自部署默认无认证,6333 / 6334 暴露后可枚举 collection |
| Weaviate | schema、objects、modules 可能被匿名查询 |
| Milvus | collection、partition、向量和 metadata 需要网络隔离 |
| Chroma | 本地开发常见,不能直接当公网服务 |
向量库账号要按用途拆:
| 用途 | 权限 |
|---|---|
| 检索服务 | 只读、限定 collection |
| 写入任务 | 只能写指定 collection |
| 管理任务 | 需要人工触发,不能给前端 |
| AI Agent | 优先只读、只给脱敏数据 |
通过标准:公网不能直接列出 collection;检索接口会按用户/租户过滤;向量 payload 不保存不必要的原文敏感数据。
云安全组不要放开 0.0.0.0/0
很多数据库暴露不是配置文件写错,而是云安全组、Kubernetes Service 或负载均衡器放开了入口。
上线前检查这些位置:
| 位置 | 要查什么 |
|---|---|
| AWS / Azure / GCP 安全组 | 数据库端口是否允许 0.0.0.0/0 或 ::/0 |
| Kubernetes Service | 是否把数据库设成 LoadBalancer / NodePort |
| Ingress / Gateway | 是否把 Kibana、管理面板、向量库 API 挂到公网域名 |
| Docker Compose | 是否用了 "6379:6379" 这类公开端口映射 |
| Terraform | 是否把数据库端口写进公共 ingress rule |
| 监控系统 | Prometheus、Grafana、Kibana 是否匿名开放 |
Terraform 里看到这类规则要退回:
cidr_blocks = ["0.0.0.0/0"]
from_port = 9200
to_port = 9200更稳的做法是只允许应用服务安全组或私网 CIDR:
source_security_group_id = aws_security_group.app.id
from_port = 9200
to_port = 9200通过标准:数据库端口只对应用服务、运维入口、受控私网开放,不对全网开放。
应用账号只给最小权限
网络隔离不是唯一防线。应用一旦有 SSRF、命令执行、依赖投毒或 CI 泄密,数据库凭据也可能被读走。此时最小权限能限制损失。
账号拆分建议:
| 账号 | 权限 |
|---|---|
app_readwrite | 只访问当前应用需要的库、表、索引或 collection |
app_readonly | 只读接口、报表、搜索 |
migration | 只在迁移窗口使用,平时禁用 |
backup | 只读备份所需范围 |
bi_readonly | 只读脱敏视图,不直连原始敏感表 |
admin | 人工使用,强 MFA,审计记录完整 |
不要让应用默认拿管理员账号。不要把 BI、客服后台、AI Agent、定时任务和线上应用共用同一个数据库用户。
通过标准:单个应用凭据泄露时,攻击者不能读所有库、删所有索引、改所有 collection,也不能创建新管理员。
备份、快照和管理面板同样要查
很多团队只锁主库,忘了副本和备份:
- Elasticsearch snapshot repository。
- ClickHouse backup 文件和对象存储桶。
- Redis RDB / AOF 文件。
- Qdrant snapshot。
- Kibana saved objects。
- 数据库 Web 管理面板。
- 临时导出的 CSV / parquet / dump。
上线前查这些文件名:
rg -n "dump|backup|snapshot|\\.rdb|\\.aof|\\.sql|\\.parquet|\\.csv|saved_objects|exports" .备份要满足:
| 检查项 | 通过标准 |
|---|---|
| 存储位置 | 私有 bucket / 私有对象存储 |
| 访问权限 | 只有备份服务和少数运维可读 |
| 加密 | 传输和静态存储都加密 |
| 保留期限 | 到期自动删除 |
| 恢复演练 | 能恢复,但不把生产数据恢复到公网测试环境 |
| 脱敏 | 研发、AI、BI 用脱敏副本 |
备份文件一旦公开,风险通常比主库更大,因为它绕过了在线权限系统。
上线前跑一张数据库暴露检查表
发布前至少确认这些项:
- [ ] Redis, ClickHouse, Elasticsearch, Kibana and vector DB ports are not open to 0.0.0.0/0
- [ ] Docker Compose does not publish database ports in production
- [ ] Kubernetes database Services are not LoadBalancer or public NodePort
- [ ] Cloud security groups only allow app/private network sources
- [ ] Redis uses bind/protected mode/ACL and blocks dangerous commands for app users
- [ ] ClickHouse listen_host is private and default user is not remotely usable
- [ ] Elasticsearch security, TLS and index-level permissions are enabled
- [ ] Kibana requires authentication and is not anonymously reachable
- [ ] Vector DB uses API key/TLS and collection-level access where available
- [ ] App users are not database admins
- [ ] BI and AI Agent accounts use read-only or scoped credentials
- [ ] Logs, prompt, headers and payment payload are not stored as raw searchable fields
- [ ] Backups, snapshots and exports are private and encrypted
- [ ] Audit logs can show who queried, exported, deleted or changed data
- [ ] A credential rotation plan exists if exposure is found再用只针对自己资产的连通性检查确认外部无法访问。不要扫描第三方 IP。
Test-NetConnection your-owned-host.example.com -Port 6379
Test-NetConnection your-owned-host.example.com -Port 9200
Test-NetConnection your-owned-host.example.com -Port 6333通过标准:从公网网络测试数据库端口应该失败;从应用所在私网测试应该成功,并且需要认证。
已经暴露怎么办
发现数据库曾经暴露后,不要只把安全组关掉。
按泄露处理:
- 立即移除公网入口:安全组、负载均衡器、端口映射、Kubernetes Service、Ingress。
- 保留必要证据:暴露时间、端口、访问日志、云审计日志、数据库审计日志。
- 轮换数据库账号、Redis ACL、Qdrant API key、Elasticsearch API key、应用
.env。 - 检查是否有异常查询、批量导出、索引删除、collection 修改、未知账号创建。
- 如果暴露了 prompt、聊天记录、手机号、邮箱、订单、支付信息,按隐私和合规流程评估通知。
- 清理公开备份、日志、镜像、CI artifacts 里复制出来的数据库连接串和 dump。
- 给数据库加私网访问、最小权限、审计、告警和到期删除。
数据库公网暴露的根因通常不是“某个数据库不安全”,而是上线时把开发便利当成生产入口。开发环境可以临时连,生产环境必须能回答三个问题:谁能连、连上后能做什么、做过什么能不能追到。答不清楚,就不要上线。