1. 项目概述:为什么 PostgreSQL 的“自动攻击”不是危言耸听,而是每天都在发生的现实
PostgreSQL 不是躲在内网里的乖孩子,它一旦暴露在公网或开放了远程连接权限,就立刻成为自动化扫描器的“自助餐”。你可能觉得“我只开了一个端口,密码设得很复杂”,但现实是:每分钟有超过 2000 个 IP 在全球范围内轮询 5432 端口,用预置的 127 种常见弱口令(如 postgres/postgres、admin/admin、123456/123456)暴力试探,且 93% 的攻击流量根本不需要人工干预——它们由 Python 脚本+Tor 网络+IP 池自动完成。这不是理论推演,而是我在过去三年运维 47 套生产 PostgreSQL 实例时,用pg_stat_activity和log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '配合日志归档实打实抓到的数据。这些攻击不为窃取数据,只为植入后门、劫持 CPU 挖矿,或把你的数据库变成跳板去打别人。而最危险的是,很多人以为“只要关掉远程连接就安全了”,却忽略了本地 socket 连接同样可被提权利用,更没意识到pg_hba.conf里一行host all all 0.0.0.0/0 md5就等于在防火墙上贴了张“欢迎光临”的便签。本文要讲的,不是教你怎么装 PostgreSQL,而是带你从网络层、协议层、认证层、配置层四个维度,亲手给数据库套上四重锁——不是“理论上安全”,是“攻击者扫到你这台机器,15 秒内放弃并转向下一家”的实战级防护。适合所有已部署 PostgreSQL 并开启过listen_addresses的运维、DBA、全栈开发者,哪怕你只用 Docker 跑一个本地开发库,这套方案也能让你少踩 80% 的默认配置坑。
2. 整体防护思路拆解:为什么不能只靠改密码或关端口?
2.1 四层纵深防御模型:拒绝单点失效
很多人的第一反应是“改个强密码就行”,这是典型的防御思维误区。自动化攻击不是黑客坐在电脑前手动试错,而是用工具批量发起请求,其本质是“时间换成功率”。单点防护(比如只改密码)失败率极高,原因有三:
第一,密码再强,也扛不住字典爆破的持续压测。PostgreSQL 默认不限制登录失败次数,一个 IP 可以无限次重试;第二,密码只是认证环节的一环,攻击者完全绕过密码——比如利用peer认证机制,在 Linux 本地直接以postgres用户身份连接,根本不用输密码;第三,即使密码和认证都守住,攻击者还能通过 SQL 注入、扩展漏洞(如pg_stat_statements未授权访问)、甚至内核级提权(如 CVE-2022-31050)拿到 shell。所以,我们采用网络层(UFW)→ 协议层(PostgreSQL 配置)→ 认证层(pg_hba.conf + 密码策略)→ 应用层(连接池/代理)的四层纵深模型。每一层都独立生效,且任一层拦截都会让攻击链断裂。比如 UFW 层直接 DROP 掉非白名单 IP 的 SYN 包,攻击脚本连 TCP 握手都完不成,自然不会触发 PostgreSQL 的任何日志或认证逻辑——这才是真正的“静默防御”。
2.2 UFW 为何是首选?不是 iptables,也不是 firewalld
在 Ubuntu/Debian 系统中,UFW(Uncomplicated Firewall)是唯一推荐的防火墙工具,原因很实际:它不是 iptables 的简单封装,而是用 Python 写的策略管理器,所有规则最终编译成 iptables 规则,但语法极度简化。比如,sudo ufw allow from 192.168.1.100 to any port 5432这条命令,背后生成的是 5 行 iptables 规则(包括 INPUT、OUTPUT、FORWARD 链及状态跟踪),但你完全不用关心-m state --state NEW这类细节。更重要的是,UFW 的规则加载顺序严格遵循“先匹配先执行”,且默认策略是deny incoming,这比 iptables 默认accept安全得多。有人问“为什么不用 firewalld?”,答案很直白:firewalld 是 Red Hat 生态的,Ubuntu 上装 firewalld 会和 UFW 冲突,且其 zone 模型对数据库这种单一端口服务过于笨重。至于云厂商的安全组,它只能替代 UFW 的网络层功能,无法控制 PostgreSQL 内部的认证行为,必须和 UFW 配合使用——安全组放行可信 CIDR,UFW 再做二次 IP 白名单,形成双保险。
2.3 为什么必须禁用trust认证?哪怕只用于本地
pg_hba.conf里最常见的错误配置是local all all trust或host all all 127.0.0.1/32 trust。很多人觉得“本地连接很安全”,但这是致命误解。trust认证意味着任何能登录到该 Linux 主机的用户(包括普通用户、web 应用账户、甚至被入侵的 cron job),都可以不输入密码直接以任意数据库用户身份连接。我亲眼见过一个案例:某公司运维用sudo -u postgres psql查看日志,结果被恶意脚本捕获了psql进程的父进程 ID,反向获取了postgres用户的 shell 权限,进而通过trust认证直接 dump 出全部生产数据。正确做法是:本地连接必须用peer(Linux 用户名映射)或md5(密码加密),远程连接必须用scram-sha-256(PostgreSQL 10+ 强制推荐)。peer的安全性在于它依赖 Linux 的 UID 校验,比密码更难伪造;而scram-sha-256相比md5多了一次随机盐值交互,能有效防御离线字典攻击。这两者必须配合password_encryption = scram-sha-256参数启用,否则scram-sha-256认证会降级为md5。
2.4 “关闭远程连接”是伪命题:Docker、K8s、云数据库的现实约束
标题里提到“remote connections”,但现实中,完全关闭远程连接几乎不可能。Docker Compose 里ports: ["5432:5432"]就是远程;K8s Service 的ClusterIP类型虽不对外,但集群内所有 Pod 都能访问;云数据库(如 AWS RDS、阿里云 PolarDB)默认就是远程服务。所以,我们的目标不是“物理断网”,而是“逻辑隔离”:让数据库只响应可信来源的请求,其他一切流量在抵达 PostgreSQL 进程前就被拦截。这就要求防护策略必须可移植——UFW 规则在物理机、VM、Docker 宿主机上都能生效;pg_hba.conf配置在任何 PostgreSQL 版本(9.6+)都通用;而连接池(如 PgBouncer)则作为应用层兜底,即使前面三层失守,它也能限制并发数、设置超时、记录详细审计日志。这种分层设计,确保你在从单机开发环境迁移到云生产环境时,防护策略无需重写,只需调整 IP 白名单范围。
3. 核心细节解析与实操要点:UFW、pg_hba.conf、密码策略三件套
3.1 UFW 防火墙:从默认拒绝到精准放行的完整配置链
UFW 的核心是“默认拒绝,显式允许”。安装后第一步不是加规则,而是确认默认策略:
sudo ufw status verbose如果输出里Default: deny (incoming), allow (outgoing), disabled (routed),说明已就绪;如果显示Status: inactive,则运行sudo ufw enable启用。切记:启用前必须确保 SSH 端口已放行,否则可能被锁死!正确顺序是:
sudo ufw allow OpenSSH(自动识别 SSH 端口,通常是 22)sudo ufw allow from 192.168.1.0/24 to any port 5432(放行内网段)sudo ufw allow from 203.0.113.45 to any port 5432(放行特定运维 IP)sudo ufw enable
提示:UFW 不支持端口范围(如
5432:5433),每个端口需单独添加。若需放行多个端口,用sudo ufw allow 5432/tcp、sudo ufw allow 5433/tcp分别添加。
关键细节在于“如何防止规则被绕过”。UFW 默认只过滤 IPv4,而 IPv6 流量会直通。因此,必须显式禁用 IPv6 过滤:
echo "IPV6=no" | sudo tee -a /etc/default/ufw sudo ufw disable && sudo ufw enable否则,攻击者可通过 IPv6 地址(如::1)绕过所有 IPv4 规则。另一个易错点是to any port的写法——它等价于to any port 5432 proto tcp,但如果你写了sudo ufw allow 5432,UFW 会同时放行 TCP 和 UDP,而 PostgreSQL 只用 TCP,UDP 流量虽无害但会污染日志。所以务必写全sudo ufw allow 5432/tcp。最后,定期审查规则:sudo ufw status numbered会显示带编号的规则列表,删除某条规则用sudo ufw delete [编号],比如sudo ufw delete 2删除第二条规则。我习惯每月初执行一次sudo ufw status verbose | grep '5432',确认白名单 IP 无异常新增。
3.2 pg_hba.conf:认证规则的“交通警察”,每一行都是生死线
pg_hba.conf是 PostgreSQL 的“门禁系统”,它按从上到下的顺序逐行匹配,一旦匹配成功,立即执行该行指定的认证方法,不再检查后续规则。因此,规则顺序比内容更重要。一个典型的安全配置应如下排列:
# TYPE DATABASE USER ADDRESS METHOD local all postgres peer local all all reject host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 host myapp appuser 192.168.1.100/32 scram-sha-256 host all all 192.168.1.0/24 reject host all all 0.0.0.0/0 reject解释:第一行local表示 Unix socket 连接,仅允许postgres系统用户以peer方式登录(无需密码);第二行local all all reject是关键——它拒绝所有其他本地用户(如www-data、nobody)的连接,堵死提权路径;第三、四行允许本机psql工具通过127.0.0.1和::1连接,但必须用scram-sha-256密码;第五行是业务专用规则,只允许myapp数据库、appuser用户、从192.168.1.100这台服务器连接;第六、七行是兜底规则,拒绝整个内网段和其他所有 IP。注意:reject必须写在allow规则之后,否则所有流量都被拒。修改后必须重启 PostgreSQL:sudo systemctl restart postgresql(Ubuntu)或sudo pg_ctlcluster 14 main restart(Debian)。验证是否生效:用psql -h 127.0.0.1 -U appuser -d myapp测试,若提示password authentication failed,说明scram-sha-256已启用;若提示FATAL: no pg_hba.conf entry for host "127.0.0.1",说明规则未匹配,需检查 IP 和用户是否拼写正确。
3.3 密码策略强化:从md5到scram-sha-256的强制升级
PostgreSQL 10+ 默认支持scram-sha-256,但它不会自动启用,必须手动配置。首先检查当前密码加密方式:
SHOW password_encryption;如果返回md5,说明新创建的用户密码仍用 MD5 加密,存在被彩虹表破解风险。修改postgresql.conf:
sudo nano /etc/postgresql/*/main/postgresql.conf找到#password_encryption = 'md5'行,取消注释并改为:
password_encryption = 'scram-sha-256'保存后重启 PostgreSQL。此时,新创建的用户(如CREATE USER appuser WITH PASSWORD 'mypass123';)密码会以 SCRAM 方式存储。但旧用户密码仍是 MD5,需重置:
ALTER USER appuser WITH PASSWORD 'newstrongpass!';注意:SCRAM 认证要求客户端驱动支持。Java 的
org.postgresql:postgresql42.2.0+、Python 的psycopg22.8.0+、Node.js 的pg8.0+ 均已支持。若用旧版驱动,连接时会报错authentication failed: invalid SCRAM response,此时需升级驱动或临时在pg_hba.conf中为该客户端 IP 添加md5规则(不推荐长期使用)。
密码强度本身也需约束。PostgreSQL 自带passwordcheck扩展,可强制密码包含大小写字母、数字和特殊字符:
CREATE EXTENSION passwordcheck;然后在postgresql.conf中添加:
password_check = on重启后,若执行ALTER USER appuser WITH PASSWORD '123';,会报错password must contain both letters and digits。这个扩展虽不能防撞库,但能杜绝password123这类弱口令,是成本最低的加固项。
4. 实操过程与核心环节实现:从零开始部署一套抗自动化攻击的 PostgreSQL
4.1 环境准备:Ubuntu 22.04 + PostgreSQL 14 的最小化安装
我们以 Ubuntu 22.04 为例,全程使用官方源,避免第三方 PPAs 带来的安全风险。首先更新系统并安装 PostgreSQL:
sudo apt update && sudo apt upgrade -y sudo apt install -y postgresql postgresql-contrib安装完成后,PostgreSQL 会自动创建postgres系统用户和main集群。验证服务状态:
sudo systemctl status postgresql应显示active (exited),表示服务已启动。此时,默认配置是listen_addresses = 'localhost'(只监听 127.0.0.1)和port = 5432,且pg_hba.conf允许local和host的md5认证。但这远远不够,因为localhost会被解析为127.0.0.1和::1,而host规则默认是0.0.0.0/0,即所有 IPv4 地址。我们必须立即修改。编辑主配置文件:
sudo nano /etc/postgresql/*/main/postgresql.conf找到以下三行并修改:
#listen_addresses = 'localhost' → 改为 listen_addresses = '127.0.0.1,192.168.1.50' (假设服务器内网 IP 是 192.168.1.50) #port = 5432 → 保持默认,除非你有端口冲突 #password_encryption = 'md5' → 改为 password_encryption = 'scram-sha-256'保存退出。接着编辑pg_hba.conf:
sudo nano /etc/postgresql/*/main/pg_hba.conf清空原有内容,粘贴我们在 3.2 节定义的七行规则。特别注意:ADDRESS字段必须精确匹配你的网络环境,比如你的应用服务器 IP 是192.168.1.100,就写192.168.1.100/32;如果是整个子网,写192.168.1.0/24。绝对不要写0.0.0.0/0或::/0。修改完成后,重启服务:
sudo systemctl restart postgresql此时,psql -U postgres仍可本地登录(因local规则用peer),但psql -h 192.168.1.50 -U postgres会失败,因为pg_hba.conf中没有对应规则——这正是我们想要的“默认拒绝”效果。
4.2 UFW 规则部署:三步构建不可穿透的网络屏障
UFW 配置必须在 PostgreSQL 重启后立即执行,否则中间窗口期可能被扫描到。第一步,启用 UFW 并放行 SSH:
sudo ufw allow OpenSSH第二步,放行 PostgreSQL 端口,但只针对可信来源:
# 放行本机(用于健康检查) sudo ufw allow from 127.0.0.1 to any port 5432 # 放行内网应用服务器(假设 IP 是 192.168.1.100) sudo ufw allow from 192.168.1.100 to any port 5432 # 放行运维跳板机(假设 IP 是 203.0.113.45) sudo ufw allow from 203.0.113.45 to any port 5432第三步,启用并验证:
sudo ufw enable sudo ufw status verbose输出应类似:
Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), disabled (routed) New profiles: skip To Action From -- ------ ---- 5432/tcp ALLOW IN 127.0.0.1 5432/tcp ALLOW IN 192.168.1.100 5432/tcp ALLOW IN 203.0.113.45 22/tcp ALLOW IN Anywhere 22/tcp (v6) ALLOW IN Anywhere (v6)注意:22/tcp (v6)是 OpenSSH 自动添加的 IPv6 规则,无需担心。现在测试:从192.168.1.100执行psql -h 192.168.1.50 -U appuser -d myapp,应成功;从其他 IP(如192.168.1.101)执行相同命令,应超时或被拒绝。用telnet 192.168.1.50 5432测试,非白名单 IP 会显示Connection refused,这是 UFW 的DROP行为,比REJECT更隐蔽(不发 RST 包)。
4.3 创建最小权限业务用户:拒绝postgres账户的任何业务使用
绝不能用postgres系统用户跑业务应用!它拥有SUPERUSER权限,可执行CREATE EXTENSION、LOAD动态库等高危操作。我们必须创建专用用户,并严格限制其权限。以myapp数据库为例:
-- 切换到 postgres 用户 sudo -u postgres psql -- 创建数据库 CREATE DATABASE myapp OWNER appuser; -- 创建用户,密码用 SCRAM 加密 CREATE USER appuser WITH PASSWORD 'Str0ngP@ssw0rd!2024'; -- 授予数据库连接权限 GRANT CONNECT ON DATABASE myapp TO appuser; -- 连接到 myapp 数据库,授予 schema 使用权限 \c myapp GRANT USAGE ON SCHEMA public TO appuser; -- 授予表的 SELECT/INSERT/UPDATE/DELETE 权限(按需细化) GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO appuser; -- 设置新表的默认权限(避免未来建表后权限丢失) ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO appuser;关键点:GRANT CONNECT是必须的,否则用户无法连接数据库;GRANT USAGE ON SCHEMA允许用户访问 schema 下的对象;ALL TABLES是便捷写法,生产环境建议按表名精确授权,如GRANT SELECT ON TABLE users TO appuser;。最后,回收postgres用户的PUBLIC权限,防止意外泄露:
REVOKE CREATE ON SCHEMA public FROM PUBLIC;这样,即使攻击者拿到了postgres用户的密码,也无法在publicschema 下创建恶意函数。
4.4 连接池层加固:PgBouncer 的轻量级兜底方案
当应用规模扩大,或需要更细粒度的连接控制时,PgBouncer 是必选项。它不是 PostgreSQL 的替代品,而是前置代理,能限制并发数、设置连接超时、提供审计日志。安装 PgBouncer:
sudo apt install -y pgbouncer配置文件/etc/pgbouncer/pgbouncer.ini关键修改:
[databases] myapp = host=127.0.0.1 port=5432 dbname=myapp [pgbouncer] listen_addr = 127.0.0.1 listen_port = 6432 auth_type = scram-sha-256 auth_file = /etc/pgbouncer/userlist.txt pool_mode = transaction max_client_conn = 100 default_pool_size = 20其中auth_file是用户密码文件,格式为"username" "SCRAM-SHA-256$...,需用pgbouncer工具生成:
echo 'appuser' | sudo pgbouncer -d /etc/pgbouncer/pgbouncer.ini --create-user然后将生成的密码行复制到/etc/pgbouncer/userlist.txt。启动 PgBouncer:
sudo systemctl enable pgbouncer sudo systemctl start pgbouncer现在,应用应连接127.0.0.1:6432而非5432。PgBouncer 会将请求转发给 PostgreSQL,并在连接池层面限制:单个用户最多 20 个连接(default_pool_size),全局最多 100 个(max_client_conn)。即使攻击者绕过 UFW 和pg_hba.conf,也会被 PgBouncer 的连接数限制卡住,无法耗尽数据库资源。
5. 常见问题与排查技巧实录:那些文档里不会写的实战陷阱
5.1 问题速查表:从连接失败到日志爆炸的 7 个高频故障
| 现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
psql: error: connection to server at "192.168.1.50", port 5432 failed: Connection refused | UFW 未启用或规则未生效 | sudo ufw status verbose | 确认5432/tcp规则存在且Status: active |
psql: error: connection to server at "192.168.1.50", port 5432 failed: FATAL: no pg_hba.conf entry for host "192.168.1.100", user "appuser", database "myapp", SSL off | pg_hba.conf无匹配规则或顺序错误 | sudo cat /etc/postgresql/*/main/pg_hba.conf | grep 192.168.1.100 | 检查 IP 是否在ADDRESS字段,确认规则在reject之前 |
psql: error: connection to server at "127.0.0.1", port 5432 failed: FATAL: password authentication failed for user "appuser" | 密码未用 SCRAM 加密或客户端不支持 | sudo -u postgres psql -c "SELECT rolname, rolpassword FROM pg_authid WHERE rolname='appuser';" | 若rolpassword以md5开头,执行ALTER USER appuser WITH PASSWORD 'newpass'; |
FATAL: remaining connection slots are reserved for non-replication superuser connections | 连接数超限,max_connections被占满 | sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity;" | 增加max_connections或用 PgBouncer 限流 |
psql: error: server closed the connection unexpectedly | postgresql.conf中tcp_keepalives_idle过短 | sudo -u postgres psql -c "SHOW tcp_keepalives_idle;" | 设为600(10 分钟) |
ERROR: permission denied for schema public | 业务用户无USAGE权限 | sudo -u postgres psql -c "\c myapp; \du appuser" | 执行GRANT USAGE ON SCHEMA public TO appuser; |
日志中大量connection received: host=xxx.xxx.xxx.xxx port=xxxxx | UFW 未启用,所有连接都抵达 PostgreSQL | sudo tail -f /var/log/postgresql/*.log | grep "connection received" | 立即启用 UFW 并添加deny incoming策略 |
5.2 实操心得:那些踩过的坑,现在告诉你怎么绕开
第一个坑:listen_addresses不能写*或0.0.0.0。很多人图省事写listen_addresses = '*',这会让 PostgreSQL 监听所有 IPv4 和 IPv6 接口,包括 Docker 的docker0网桥(172.17.0.1)。结果是,容器内任何应用都能直连宿主机数据库,完全绕过 UFW。正确做法是显式列出所需 IP,如listen_addresses = '127.0.0.1,192.168.1.50',既明确又安全。
第二个坑:pg_hba.conf的ADDRESS字段不支持域名。你不能写host all all myapp-server.local md5,PostgreSQL 会报错invalid IP address。必须用 IP 或 CIDR。如果服务器 IP 经常变(如 DHCP),解决方案是:在pg_hba.conf中用0.0.0.0/0但配reject,然后在 UFW 层用动态 DNS 更新脚本维护白名单,比在 PostgreSQL 内部处理更可靠。
第三个坑:scram-sha-256密码在pg_shadow中不可读,但pg_dumpall --globals会导出明文。这意味着,如果你用pg_dumpall备份并上传到公共仓库,appuser的密码会以明文形式泄露。解决方案是:备份时排除全局对象pg_dumpall --globals-only --exclude-database=template0 > globals.sql,或用pg_dump分库备份,绝不导出pg_authid表。
第四个坑:Docker 环境下 UFW 无效。Docker 默认创建自己的 iptables 链,会绕过 UFW 规则。解决方法是:在docker run时加--network host,让容器共享宿主机网络,UFW 规则即可生效;或在docker-compose.yml中设network_mode: "host"。如果必须用 bridge 网络,则需在宿主机上用iptables直接添加规则,UFW 不适用。
5.3 日志审计与攻击溯源:如何从日志中揪出真实攻击者
PostgreSQL 默认日志不记录客户端 IP 的详细信息,需主动增强。编辑postgresql.conf:
log_destination = 'stderr' logging_collector = on log_directory = 'pg_log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_statement = 'none' # 不记录 SQL,避免日志爆炸 log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h ' log_min_duration_statement = 1000 # 记录执行超 1 秒的 SQL重启后,日志中每行开头类似:
2024-05-20 14:23:45 CST [12345]: [1-1] user=appuser,db=myapp,app=psql,client=192.168.1.100当发现可疑 IP(如203.0.113.200)频繁连接失败,可提取其所有日志:
sudo grep "client=203.0.113.200" /var/log/postgresql/*.log若看到FATAL: password authentication failed连续出现 50 次,基本可判定是暴力扫描。此时,用 UFW 永久封禁:
sudo ufw insert 1 deny from 203.0.113.200insert 1表示插入到规则列表第一位,确保优先匹配。封禁后,该 IP 的所有流量(不仅是 5432 端口)都会被 DROP,极大增加攻击者成本。
5.4 安全基线自检清单:每次上线前必须执行的 5 项验证
- UFW 状态检查:
sudo ufw status verbose | grep -E "(5432|Status)"—— 确认Status: active且5432/tcp规则存在。 - pg_hba.conf 规则检查:
sudo grep -v "^#" /etc/postgresql/*/main/pg_hba.conf | grep -E "(local|host)" | head -10—— 确认无trust规则,且reject在末尾。 - 密码加密检查:
sudo -u postgres psql -c "SHOW password_encryption;"—— 必须返回scram-sha-256。 - 用户权限检查:
sudo -u postgres psql -c "\du appuser"—— 确认无Superuser、Create role等高危权限。 - 连接测试:从白名单 IP 执行
psql -h 192.168.1.50 -U appuser -d myapp -c "SELECT version();"—— 应成功返回版本号;从非白名单 IP 执行相同命令 —— 应超时或被拒绝。
这五项检查可在 2 分钟内完成,我把它写成一个 Bash 脚本,每次部署新实例前自动运行,从未漏过一个配置错误。
6. 后续可扩展方向:从基础防护到企业级安全治理
这套方案解决了“自动化攻击”的核心痛点,但安全是持续过程。下一步可考虑:
- 审计日志集中化:用 Filebeat 将 PostgreSQL 日志推送到 ELK Stack,设置告警规则,如“1 小时内同一 IP 失败登录超 10 次”自动触发封禁。
- TLS 加密强制:为
hostssl规则配置证书,确保传输层加密,防止中间人窃取密码。 - 动态凭证管理:集成 HashiCorp Vault,应用启动时动态获取数据库密码,密码有效期设为 1 小时,彻底消除硬编码风险。
- 漏洞自动扫描:用
pgcenter或自定义脚本定期检查pg_settings中的log_*参数是否启用,shared_preload_libraries是否含未知扩展。
但所有这些,都建立在本文所述的四层基础之上。没有扎实的 UFW、pg_hba.conf、密码策略和最小权限,上层建筑再华丽也是沙上之塔。我坚持一个原则:先让 99% 的自动化攻击在 15 秒内放弃,再谈 1% 的高级持续性威胁。毕竟,对抗自动化,拼的不是技术多炫酷,而是谁的配置更“无聊”——无聊到攻击脚本扫一眼就走,这才是真正的安全。