接手一个MySQL数据库,第一件事不是急着建表,而是立刻检查默认的root用户是否允许远程登录。太多生产环境出问题,根源就在于安装完MySQL后,保留了root用户可以从任意主机连接的设置。登录MySQL后执行以下命令,你会看到user表中Host字段为'%'的root记录,这就是最大的隐患。
SELECT user, host, plugin FROM mysql.user WHERE user='root';
立即修改root用户的Host限制,只允许本地连接。如果必须保留远程管理能力,那就创建一个独立的、拥有受限权限的管理账号,而不是把root暴露出去。删除或修改所有Host为'%'的root记录,只保留localhost和127.0.0.1。
DELETE FROM mysql.user WHERE user='root' AND host='%'; FLUSH PRIVILEGES;
接下来是清理匿名用户和空密码账号。MySQL在某些旧版本或默认安装中会创建匿名用户,这些用户没有密码,却能连接数据库并访问test开头的数据库。执行查询找出所有plugin字段为空或authentication_string为空的用户,一律删除。生产环境不允许存在任何无密码认证的账号。
DELETE FROM mysql.user WHERE user=''; DELETE FROM mysql.user WHERE authentication_string='' AND plugin='mysql_native_password'; FLUSH PRIVILEGES;密码策略强制实施
MySQL提供了validate_password组件来强制密码复杂度。登录MySQL后检查组件是否已启用,如果未安装则立即安装。这个组件可以限制密码最小长度、要求包含大小写字母、数字和特殊字符的组合。很多安全合规标准,比如等保和PCI-DSS,都明确要求数据库密码必须满足复杂度策略。
INSTALL COMPONENT 'file://component_validate_password'; SET GLOBAL validate_password.policy = STRONG; SET GLOBAL validate_password.length = 12; SET GLOBAL validate_password.mixed_case_count = 1; SET GLOBAL validate_password.number_count = 1; SET GLOBAL validate_password.special_char_count = 1;
对于已有的弱密码账号,需要强制它们在下次登录时修改密码。使用ALTER USER语句为每个账号设置密码过期标记,这样用户下次连接时会被要求重新设置密码。同时检查所有账号的密码最后修改时间,找出长期未更换密码的账号,这些往往是安全审计中的扣分项。
权限最小化原则落地权限管理不是简单地把所有权限收回就完事,而是要精确匹配业务需求。先做一次全面的权限审计,列出每个账号拥有的全局权限、数据库级别权限和表级别权限。重点关注那些拥有SUPER、FILE、PROCESS、SHUTDOWN等管理权限的账号,这些权限通常只有DBA需要,业务账号不应该持有。
SELECT user, host, db, table_name, column_name, GROUP_CONCAT(privilege_type) FROM information_schema.column_privileges WHERE grantee NOT LIKE 'mysql.%' GROUP BY user, host, db, table_name, column_name;
对于业务账号,严格遵循最小权限原则。应用连接数据库只需要SELECT、INSERT、UPDATE、DELETE和EXECUTE权限,不要授予CREATE、DROP、ALTER等DDL权限。如果一个应用只需要读取某张表,那就只给SELECT权限。使用视图和存储过程来进一步封装数据访问,让应用账号只能通过预定义的接口操作数据,而不是直接操作基表。
SSL/TLS加密连接配置数据库流量在网络中明文传输是极其危险的,尤其是在云环境或跨机房部署的场景下。MySQL支持SSL/TLS加密连接,但很多DBA只是启用了SSL,却没有强制要求客户端使用。正确的做法是生成证书后,在MySQL配置文件中同时设置require_secure_transport参数为ON,这样所有连接都必须使用加密通道。
[mysqld] require_secure_transport = ON ssl_ca = /etc/mysql/certs/ca.pem ssl_cert = /etc/mysql/certs/server-cert.pem ssl_key = /etc/mysql/certs/server-key.pem
对于特定账号,可以使用REQUIRE X509或REQUIRE SSL来强制加密连接。更精细的做法是使用REQUIRE SUBJECT和REQUIRE ISSUER来验证客户端证书的主题和颁发者,实现双向认证。这在金融和政务系统中是常见的安全要求。
ALTER USER 'app_user'@'%' REQUIRE X509; ALTER USER 'admin_user'@'%' REQUIRE SUBJECT '/CN=admin/O=MyCompany';审计日志开启与监控
MySQL企业版有原生的审计插件,社区版则需要使用MariaDB的审计插件或Percona的审计插件。审计日志记录所有连接尝试、查询执行和权限变更,是事后溯源和安全分析的基础。安装审计插件后,配置日志输出格式为JSON,便于后续对接日志分析平台。
INSTALL PLUGIN server_audit SONAME 'server_audit.so'; SET GLOBAL server_audit_logging = ON; SET GLOBAL server_audit_events = 'CONNECT,QUERY,TABLE'; SET GLOBAL server_audit_output_type = 'FILE'; SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log';
审计日志会快速增长,需要配合日志轮转策略。在Linux系统中配置logrotate,按天或按大小轮转,保留至少90天的审计记录以满足合规要求。同时设置告警规则,当审计日志中出现大量失败的登录尝试、异常的DROP TABLE操作或权限变更语句时,立即触发安全告警。
SQL注入防御从数据库层面加固虽然SQL注入主要应该在应用层防御,但数据库层面的配置可以显著降低注入成功后的危害。限制应用账号的执行能力,禁用多语句执行功能,这样即使攻击者成功注入,也无法通过分号执行第二条语句。在MySQL配置中设置max_prepared_stmt_count参数,限制预编译语句的数量,防止内存耗尽攻击。
更关键的是合理使用存储过程。将业务逻辑封装在存储过程中,应用账号只被授予存储过程的EXECUTE权限,而不是直接操作表的权限。这样即使SQL注入发生,攻击者也只能调用已有的存储过程,无法直接读取或篡改表数据。存储过程内部使用参数化查询,从根源上杜绝注入。
数据加密与备份安全静态数据加密不能只依赖应用层,MySQL支持表空间加密和InnoDB重做日志加密。启用InnoDB表空间加密需要先配置keyring插件,用于管理加密密钥。keyring文件本身需要严格保护,设置文件权限为600,只有mysql用户可读写。
INSTALL PLUGIN keyring_file SONAME 'keyring_file.so'; SET GLOBAL keyring_file_data = '/var/lib/mysql-keyring/keyring'; ALTER TABLE sensitive_data ENCRYPTION = 'Y';
备份文件的安全同样重要。使用mysqldump导出数据时,通过管道直接压缩和加密,避免明文数据落盘。使用openssl对备份文件进行AES-256加密,密钥通过密钥管理系统获取,不要硬编码在脚本中。备份文件传输使用scp或rsync over SSH,禁止使用FTP等明文协议。
mysqldump --single-transaction --routines --triggers db_name | openssl enc -aes-256-cbc -salt -out backup.sql.enc网络层访问控制
防火墙规则是数据库安全的第一道防线。MySQL默认端口3306不应该对公网开放,只允许应用服务器和运维跳板机的IP地址访问。在云环境中使用安全组,在物理服务器上使用iptables或firewalld,精确控制来源IP和端口。同时配置MySQL的bind-address参数,只监听内网IP,不监听0.0.0.0。
[mysqld] bind-address = 10.0.1.100 skip-networking = OFF
对于需要跨机房访问的场景,不要直接暴露数据库端口,而是通过SSH隧道或专用的数据库代理中间件来中转。数据库代理可以做连接池、读写分离,同时还能实现SQL审计和拦截,相当于在数据库前面加了一层应用防火墙。
日志文件安全与信息泄露防护MySQL的错误日志、慢查询日志和通用查询日志中可能包含敏感数据。一条INSERT语句如果被记录到通用日志中,用户的个人信息就明文暴露了。生产环境必须关闭通用查询日志,慢查询日志只记录查询结构和执行时间,不记录具体的参数值。同时设置日志文件的访问权限,只允许mysql用户和授权运维人员读取。
SET GLOBAL general_log = OFF; SET GLOBAL log_slow_verbosity = 'microtime,query_plan';
错误日志的详细程度也要控制。log_error_verbosity设置为2即可,不要使用3级详细度,因为3级会记录每个连接和断开的信息,日志量巨大且可能泄露连接来源。定期检查所有日志文件,确保没有意外记录密码或认证令牌。
版本更新与漏洞管理MySQL的每个大版本都有固定的生命周期,运行已停止支持的版本意味着已知漏洞不会被修复。建立定期的版本升级机制,至少保持在MySQL 8.0的最新小版本上。每次安全公告发布后,评估影响范围,对于标记为Critical或High的漏洞,要在48小时内完成修补。升级前在测试环境验证兼容性,特别是使用了特定存储引擎或插件的场景。
同时关闭不使用的存储引擎和插件,减少攻击面。比如不需要Federated引擎就禁用它,不需要MySQL Shell的X插件就卸载。每个额外的组件都可能引入新的漏洞。
[mysqld] disabled_storage_engines = "FEDERATED,BLACKHOLE,ARCHIVE"操作系统层面的协同加固
数据库安全不能脱离操作系统安全。MySQL的数据目录/var/lib/mysql必须设置严格的权限,属主为mysql用户,权限为750,不允许其他系统用户访问。MySQL的配置文件/etc/my.cnf同样需要保护,因为配置文件中可能包含加密密钥路径、插件路径等敏感信息。
使用SELinux或AppArmor为MySQL进程设置强制访问控制策略,限制MySQL进程只能访问必要的文件和网络端口。即使MySQL进程被攻破,攻击者也难以横向移动到系统的其他部分。配置MySQL服务的systemd单元文件,使用PrivateTmp、NoNewPrivileges等安全选项来进一步隔离进程。
[Service] PrivateTmp = true NoNewPrivileges = true ProtectSystem = strict ProtectHome = true定期安全巡检与自动化检查
安全加固不是一次性工作,需要持续进行。编写自动化检查脚本,定期扫描所有MySQL实例的安全配置。检查项包括:是否存在弱密码、是否有不必要的远程访问权限、SSL是否启用、审计日志是否正常写入、备份文件是否加密、版本是否为最新等。将检查结果输出为报告,对接安全团队的工单系统。
使用MySQL自带的安全检查功能作为辅助。执行mysql_secure_installation脚本可以交互式地完成部分基础加固,但在自动化运维场景下,应该将每一步拆解为可独立执行的SQL语句或命令,集成到配置管理工具中。每次新建MySQL实例时,自动执行安全基线脚本,确保没有遗漏任何加固项。
最后建立数据库安全事件响应流程。当发现异常登录、权限变更或数据泄露迹象时,按照预定流程立即隔离受影响的实例,保留现场证据,启动审计日志分析,追踪攻击路径。定期进行安全演练,模拟数据库被入侵的场景,检验团队响应能力和加固措施的有效性。
