中小型项目的数据库安全防护,最大的痛点不是技术有多难,而是资源有限。没有专职DBA,没有预算买企业级安全设备,甚至服务器都是云上最低配的。这种情况下谈数据库安全,照搬银行级方案根本不现实。真正有效的做法,是用最少的操作、最低的成本,把99%的攻击挡在门外。下面直接讲具体怎么做。
网络层的第一道闸门:绑定IP和端口绝大多数中小项目数据库被黑,都是从公网直接暴露开始的。检查一下你的数据库端口,如果在公网上随便一扫就能发现,那等于把大门钥匙挂在门外。第一步就是把数据库绑定到内网IP,只允许应用服务器所在的网段访问。MySQL为例,在配置文件my.cnf里把bind-address设置成内网IP,千万别用0.0.0.0。云服务器上更简单,直接在安全组里设置入站规则,只放行应用服务器的内网IP访问数据库端口,其他来源全部拒绝。这一步做完,至少挡住了90%的扫描攻击。
端口也是重灾区。默认端口3306、5432、6379这些,攻击者闭着眼睛都能扫出来。改成一个五位数的冷门端口,比如35128,虽然不能彻底防止被扫,但能过滤掉大批量自动化扫描工具。改端口配合安全组规则一起用,效果翻倍。另外如果应用和数据库在同一台机器上,直接让数据库只监听127.0.0.1,通过Unix Socket通信,连端口都不用开,这是最安全的本地连接方式。
账号权限最小化:拒绝万能钥匙很多开发者图省事,一个root账号从头用到尾,所有应用都拿root连数据库。这个习惯的破坏力,相当于给每个员工都发了一把能开所有门的万能钥匙。正确的做法是为每个应用单独创建账号,并且严格限制权限。一个只读的报表服务,就只给SELECT权限;一个只操作某个库的后台服务,就把权限限定在那个库上。MySQL的授权语句可以精确到表级别和操作类型,比如:
GRANT SELECT, INSERT, UPDATE ON mydb.users TO 'app_user'@'192.168.1.100' IDENTIFIED BY 'strong_password';
这里有几个细节容易被忽略。一是限定来源IP,即使账号密码泄露,攻击者从其他机器也连不上。二是定期清理不再使用的账号,离职员工、过期项目的账号要及时回收。三是生产环境禁止使用ALL PRIVILEGES这种授权,哪怕麻烦一点,也要把需要的权限一个个列出来。
密码策略:别让弱口令成为突破口数据库密码被爆破的案例太多了,尤其是Redis、MongoDB这类NoSQL数据库,默认安装经常没有密码。设置密码是底线,但密码强度同样重要。至少16位以上,包含大小写字母、数字和特殊符号,不要用任何有意义的单词或日期。一个实用技巧是用密码管理器生成随机密码,然后通过环境变量或配置文件传递给应用,不要硬编码在代码里。如果担心密码泄露后影响面太大,可以启用数据库的密码过期策略,强制定期更换。
传输加密:防止数据在路上被截胡内网通信也不是绝对安全的。如果应用和数据库跨机房部署,或者经过公共网络区域,数据包有可能被中间人截获。开启SSL/TLS加密传输是必要的。MySQL和PostgreSQL都支持SSL连接,配置不复杂。以MySQL为例,首先生成自签名证书,然后在服务端开启ssl选项,客户端连接时指定ssl-mode参数。对于中小项目,自签名证书足够用了,不需要去申请商业证书。关键是要强制所有连接都走SSL,在数据库里设置require ssl,拒绝任何非加密连接。
ALTER USER 'app_user'@'192.168.1.100' REQUIRE SSL;备份是最后的安全防线
安全防护做得再好,也不能保证万无一失。硬件故障、误操作、勒索软件,任何一种情况都可能导致数据丢失。备份是兜底方案,必须自动化、定期执行。中小项目推荐用数据库自带的逻辑备份工具,比如mysqldump、pg_dump,配合crontab定时执行。备份策略建议每天一次全量备份,保留最近7天的备份文件。备份文件不要和数据库放在同一台机器上,同步到对象存储或者另一台服务器上。这里有个容易被忽略的点:备份文件本身也要加密。很多数据泄露事件是因为备份文件被拖走后直接解压读取。用openssl或者gpg对备份文件加密后再传输存储,即使备份文件泄露,没有密钥也打不开。
mysqldump -u backup_user -p mydb | gzip | openssl enc -aes-256-cbc -salt -pass pass:your_key > backup.sql.gz.encSQL注入防护:应用层的第一道防线
数据库自身的安全配置做好之后,最大的攻击面就转移到了应用层,SQL注入是头号威胁。中小项目因为开发周期短、代码审查不严格,SQL注入漏洞尤其常见。彻底解决SQL注入的方法只有一个:使用参数化查询,永远不要拼接SQL字符串。无论用哪种编程语言,ORM框架或者数据库驱动都提供了参数绑定的方式。以Python的psycopg2为例:
cursor.execute("SELECT * FROM users WHERE email = %s", (user_input,))
如果项目里存在大量老代码,改造成参数化查询成本太高,可以先用WAF或者数据库防火墙作为过渡方案,但最终还是要改代码。另外注意,参数化查询不能用于表名、字段名这些标识符的动态拼接,如果业务确实需要动态表名,必须用白名单做严格校验。
审计日志:知道谁在什么时候干了什么很多中小项目直到出事才发现,数据库连基本的操作日志都没有,完全没法追溯攻击路径。开启数据库的审计日志,记录所有DDL操作和敏感数据的DML操作。MySQL有general log和audit log插件,PostgreSQL有pgAudit扩展。全量日志会消耗性能,建议只记录关键操作,比如账号创建、权限变更、表结构修改、大批量数据导出等。日志文件要定期轮转,避免撑爆磁盘。更重要的是,日志要发送到独立的日志服务器或者日志服务上,防止攻击者拿到数据库权限后顺手把日志删掉。
版本和补丁管理:别用古董版本数据库软件的漏洞每年都在披露,很多高危漏洞都有公开的利用工具。中小项目经常出现一个MySQL 5.5跑了好几年没升级的情况,这种系统在攻击者眼里就是活靶子。保持数据库版本在官方支持周期内,及时打安全补丁,这是成本最低的安全投入。升级前在测试环境验证兼容性,选择业务低峰期操作,做好回滚预案。如果实在没法升级老旧系统,至少要查阅该版本的已知CVE漏洞,针对性地在网络上做隔离补偿。
Redis和MongoDB的特殊处理中小项目用Redis做缓存、用MongoDB存文档数据很普遍,但这两个数据库的安全配置经常被忽略。Redis一定要设置密码,在配置文件里加requirepass,复杂度要求和数据库密码一样。同时把危险命令禁用掉,比如FLUSHALL、CONFIG、EVAL这些,在配置文件里rename成空字符串或者随机字符串:
rename-command FLUSHALL "" rename-command CONFIG "CONFIG_8a3f2e1d"
MongoDB要开启访问控制,创建管理员账号后再启用auth模式。另外MongoDB默认绑定所有网卡,一定要改成绑定内网IP。这两个数据库早期版本都有过未授权访问导致数据被勒索的案例,配置好上面这些基础项就能避免。
最小化安装和功能裁剪数据库安装时默认会带上很多用不到的组件和功能,这些多余的组件就是潜在的攻击面。安装时选择最小化安装,不需要的插件、存储引擎、外部函数库统统不要装。数据库所在的操作系统也要做精简,只安装必要的软件包,关闭无关的服务和端口。一台机器上只跑数据库这一个服务,不要图省事把应用、缓存、消息队列全塞在一台机器上,这会让攻击者拿下服务器后一锅端。
日常巡检清单安全配置不是一劳永逸的事,需要定期检查确认。做一个简单的巡检清单,每周花十分钟过一遍:检查是否有新创建的不明账号;检查账号权限是否有异常变更;检查是否有来自陌生IP的连接尝试;检查错误日志里是否有大量登录失败记录;检查备份任务是否正常执行;检查磁盘空间是否正常,防止日志撑爆或者数据文件异常增长。这些检查项可以写成脚本自动化执行,异常情况发告警通知。
以上这些措施,全部落地对于一个中小项目来说,熟练的话半天就能搞定。不需要额外花钱买产品,用的全是数据库自带功能和操作系统基础能力。安全防护不是越复杂越好,把基础项做扎实,就能防住绝大多数攻击。真正出事的,往往不是没上高级方案,而是基础配置都没做对。
