把数据库端口直接暴露在公网上,等于把保险柜放在大街上。很多人以为改了高端口号、设了复杂密码就高枕无忧,实际上扫描工具能在几分钟内发现你的实例,暴力破解、漏洞利用和零日攻击会接踵而至。真正的加固不是某个单点动作,而是一套从网络边界到内核参数的完整防御链条。下面直接进入具体操作流程,每一步都直接可用。
第一步:网络层访问控制,从根源上切断威胁最有效的方式是不暴露公网。如果你的业务允许,永远不要给数据库实例绑定公网IP。让数据库只监听内网地址,应用服务器通过内网通信。在MySQL配置文件里明确指定绑定地址:
[mysqld] bind-address = 192.168.1.100
如果必须跨地域访问,强制使用专线、SD-WAN或者点对点的IPsec隧道,把数据库流量完全限制在加密的私有通道里。实在无法避免公网暴露时,安全组和防火墙规则要做到极致。入站规则只放行应用服务器的固定公网出口IP,拒绝所有其他来源。出站规则同样收紧,禁止数据库主动向外发起连接,防止反弹Shell和数据外传。云服务商的安全组是有状态的,但依然要双向配置,把“允许所有出站”改成“仅允许必要的内网通信”。
第二步:端口混淆与协议隐藏默认端口是扫描器第一目标。把MySQL的3306、PostgreSQL的5432、Redis的6379改成不常用的高端口,比如35421。但仅改端口不够,扫描器会做全端口服务识别。配合端口敲门技术:在防火墙层面设置一个敲门序列,客户端必须按顺序访问预设的一组端口后,数据库端口才临时开放。例如用knockd实现:
[options] UseSyslog [openSSH] sequence = 7000,8000,9000 seq_timeout = 5 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 35421 -j ACCEPT tcpflags = syn [closeSSH] sequence = 9000,8000,7000 seq_timeout = 5 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 35421 -j ACCEPT tcpflags = syn
这样即使攻击者扫描到端口,也无法建立TCP连接,因为端口在防火墙层面根本不可达。
第三步:传输层强制加密,杜绝中间人嗅探公网传输必须启用TLS。MySQL 8.0默认开启ssl,但需要配置证书。生成自签名证书或使用内部CA签发:
openssl genrsa 2048 > ca-key.pem openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem openssl req -newkey rsa:2048 -days 3650 -nodes -keyout server-key.pem -out server-req.pem openssl rsa -in server-key.pem -out server-key.pem openssl x509 -req -in server-req.pem -days 3650 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
在配置文件中指定证书路径,并强制客户端使用加密连接:
[mysqld] ssl_ca=ca-cert.pem ssl_cert=server-cert.pem ssl_key=server-key.pem require_secure_transport=ON
创建用户时明确要求X509认证:
CREATE USER 'appuser'@'%' IDENTIFIED BY 'StrongPassword' REQUIRE X509;
这样即使流量被截获,攻击者看到的也是密文。对于Redis这类本身不支持TLS的服务,用stunnel或spiped做加密隧道包装,在本地和远程各起一个代理进程,应用连接本地代理,代理之间走加密通道。
第四步:身份认证体系加固密码策略要严格到苛刻。设置密码最小长度、复杂度要求和过期时间:
[mysqld] validate_password.length=14 validate_password.mixed_case_count=1 validate_password.number_count=1 validate_password.special_char_count=1 default_password_lifetime=90
更关键的是废弃密码认证,改用密钥对或证书认证。PostgreSQL的pg_hba.conf可以配置为仅允许cert或scram-sha-256:
hostssl all all 0.0.0.0/0 cert
对于MySQL,启用caching_sha2_password插件,并配合LDAP或Active Directory做集中认证。多因素认证也要加上,在数据库连接前增加一层SSH密钥验证或短效令牌验证。把数据库账号从“人”的账号剥离出来,每个应用实例使用独立账号,权限最小化到单库甚至单表级别。
第五步:操作系统层内核参数调优与限制数据库进程运行在专用低权限账户下,禁止使用root。用systemd或supervisor管理进程时,明确指定User和Group。文件系统权限收紧:数据目录仅数据库用户可读写,配置文件和日志目录同样。内核参数加固:
net.ipv4.tcp_syncookies = 1 net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0
启用ASLR地址空间随机化,限制数据库进程可使用的内存和CPU,防止资源耗尽型攻击。用cgroup或容器做资源隔离,设置内存上限和CPU份额。数据库所在主机不安装任何无关软件,关闭所有非必需的系统服务,用最小化安装的操作系统镜像。
第六步:日志审计与实时监控开启数据库的详细查询日志和错误日志,但要注意日志量,可以只记录慢查询和未使用索引的查询。更关键的是开启审计插件,MySQL Enterprise Audit或开源的Percona Audit Plugin能记录每一次连接、每一次DDL和DML操作:
INSTALL PLUGIN audit_log SONAME 'audit_log.so'; SET GLOBAL audit_log_policy = ALL; SET GLOBAL audit_log_format = JSON;
把日志实时推送到远端集中日志系统,而不是存在本地。本地日志文件很容易被攻击者清除。配置Syslog或Filebeat直接发送到ELK或Loki,设置告警规则:多次登录失败、权限变更、大表导出操作立即触发通知。对数据库端口建立连接数基线,异常飙升时自动触发临时阻断。
第七步:数据级防护与备份验证即使所有防御被突破,数据本身还要有一层保护。对敏感列做应用层加密,密钥与数据库分离存储。使用透明数据加密TDE,MySQL和PostgreSQL都支持表空间级别的静态加密。备份策略采用3-2-1原则,三份副本、两种介质、一份异地。最关键的是备份验证:很多团队只在灾难发生时才发现备份文件损坏。设置自动化恢复演练,每周在隔离环境中完整恢复一次备份,验证数据完整性和恢复时间。备份文件本身也要加密,传输过程使用加密通道,存储端启用访问控制和版本锁定,防止备份被篡改或删除。
第八步:持续漏洞管理与补丁策略数据库版本保持最新稳定版,但不是盲目追新。建立漏洞情报订阅,关注CVE和厂商安全公告。对于无法立即修补的零日漏洞,使用虚拟补丁:在数据库前部署SQL防火墙或WAF规则,拦截针对特定漏洞的攻击载荷。定期做渗透测试和漏洞扫描,用Nmap脚本、SQLMap等工具从外部视角探测自己的数据库,验证防御措施是否生效。把安全配置写成代码,用Ansible或Terraform管理,确保每次部署都符合基线,避免人工配置的疏漏。
第九步:业务层连接池与熔断机制应用服务器与数据库之间使用连接池,连接池本身具备连接验证和重连机制。配置连接超时、空闲回收和最大连接数,防止连接泄漏耗尽数据库资源。在应用侧实现熔断逻辑:当数据库响应时间超过阈值或错误率飙升时,自动切断请求,快速失败而不是持续堆积连接。熔断器状态变化要联动告警,同时启用半开状态尝试恢复。业务代码中所有SQL使用参数化查询,杜绝拼接字符串,从代码层消除SQL注入可能。
第十步:应急响应与隔离演练提前写好应急剧本:发现公网数据库被攻击时,第一步不是分析,而是立即隔离。预先准备好一键断网的脚本,通过API调用安全组或防火墙,将数据库实例的出入口流量全部切断,只保留一条管理通道。然后对数据库做内存快照和磁盘快照,保留现场证据。用只读模式启动一个新的实例,从最近一次验证过的备份恢复数据,切换业务流量到新实例。事后复盘时,把攻击者的IP、手法、时间线全部记录,更新防御规则。每季度做一次红蓝对抗,红队专门攻击公网暴露面,蓝队用实际流程响应,检验整套加固体系的有效性。
数据库公网部署的安全加固不是一次性工程,而是持续对抗的过程。攻击手法在进化,防御策略就要同步迭代。把上述十个步骤全部落地,你的数据库才能在公网这个高风险环境中站稳脚跟。
