### MySQL 安全性指南

作为一个 MySQL 的系统管理员，你有责任维护你的 MySQL 数据库系统的数据安全性和完整性。本文主要主要介绍如何建立一个安全的 MySQL 系统，从系统内部和外部网络两个角度，为你提供一个指南。
本文主要考虑下列安全性有关的问题：
为什么安全性很重要，你应该防范那些攻击？
服务器面临的风险（内部安全性），如何处理？
连接服务器的客户端风险（外部安全性），如何处理？
MySQL 管理员有责任保证数据库内容的安全性，使得这些数据记录只能被那些正确授权的用户访问，这涉及到数据库系统的内部安全性和外部安全性。内部安全性关心的是文件系统级的问题，即，防止 MySQL 数据目录（DATADIR）被在服务器主机有账号的人（合法或窃取的）进行攻击。如果数据目录内容的权限过分授予，使得每个人均能简单地替代对应于那些数据库表的文件，那么确保控制客户通过网络访问的授权表设置正确，对此毫无意义。
外部安全性关心的是从外部通过网络连接服务器的客户的问题，即，保护 MySQL 服务器免受来自通过网络对服务器的连接的攻击。你必须设置 MySQL 授权表（grant table），使得他们不允许访问服务器管理的数据库内容，除非提供有效的用户名和口令。
下面就详细介绍如何设置文件系统和授权表 mysql，实现 MySQL 的两级安全性。
一、内部安全性-保证数据目录访问的安全
MySQL 服务器通过在 MySQL 数据库中的授权表提供了一个灵活的权限系统。你可以设置这些表的内容，允许或拒绝客户对数据库的访问，这提供了你防止未授权的网络访问对你数据库攻击的安全手段，然而如果主机上其他用户能直接访问数据目录内容，建立对通过网络访问数据库的良好安全性对你毫无帮助，除非你知道你是登录 MySQL 服务器运行主机的唯一用户，否则你需要关心在这台机器上的其他用户获得对数据目录的访问的可能性。
以下是你应该保护的内容：
数据库文件。很明显，你要维护服务器管理的数据库的私用性。数据库拥有者通常并且应该考虑数据库内容的安全性，即使他们不想，也应该考虑时数据库内容公开化，而不是通过糟糕的数据目录的安全性来暴露这些内容。
日志文件。一般和更新日志必须保证安全，因为他们包含查询文本。对日志文件有访问权限的任何人可以监视数据库进行过的操作。 更要重点考虑的日志文件安全性是诸如 GRANT 和 SET PASSWORD 等的查询也被记载了，一般和更新日志包含有敏感查询的文本，包括口令（MySQL 使用口令加密，但它在已经完成设置后才运用于以后的连接建立。设置一个口令的过程设计象 GRANT 或 SET PASSWORD 等查询，并且这些查询以普通文本形式记载在日志文件中）。如果一个攻击者犹如日文件的读权限，只需在日志文件上运行 grep 寻找诸如 GRANT 和 PASSWORD 等词来发现敏感信息。显然，你不想让服务器主机上的其他用户有数据库目录文件的写权限，因为他们可以重写你的状态文件或数据库表文件，但是读权限也很危险。如果一个数据库表文件能被读取，偷取文件并得到 MySQL 本身，以普通文本显示表的内容也很麻烦，为什么？因为你要做下列事情：
在服务器主机上安装你自己“特制”的 MySQL 服务器，但是有一个不同于官方服务器版本的端口、套接字和数据目录。运行 mysql_install_db 初始化你的数据目录，这赋予你作为 MySQL root 用户访问你的服务器的权限，所以你有对服务器访问机制的完全控制，它也建立一个 test 数据库。将对应于你想偷取得表文件拷贝到你服务器的数据库目录下的 test 目录。
启动你的服务器。你可以随意访问数据库表，SHOW TABLES FROM test 显示你有一个偷来的表的拷贝，SELECT *显示它们任何一个的全部内容。如果你确实很恶毒，将权限公开给你服务器的任何匿名用户，这样任何人能从任何地方连接服务器访问你的 test 数据库。你现在将偷来的数据库表公布于众了。在考虑一下，从相反的角度，你想让别人对你这样吗？当然不！你可以通过在数据库录下执行 ls -l 命令确定你的数据库是否包含不安全的文件和目录。查找有“组”和“其他用户”权限设置的文件和目录。下面是一个不安全数据目录的一部分列出：
% ls -l
total 10148
drwxrwxr-x 11 mysqladm wheel 1024 May 8 12:20 .
drwxr-xr-x 22 root wheel 512 May 8 13:31 ..
drwx------ 2 mysqladm mysqlgrp 512 Apr 16 15:57 menagerie
drwxrwxr-x 2 mysqladm wheel 512 Jan 25 20:40 mysql
drwxrwxr-x 7 mysqladm wheel 512 Aug 31 1998 sql-bench
drwxrwxr-x 2 mysqladm wheel 1536 May 6 06:11 test
drwx------ 2 mysqladm mysqlgrp 1024 May 8 18:43 tmp
....
正如你看到的，有些数据库有正确的权限，而其他不是。本例的情形是经过一段时间后的结果。较少限制的权限由在权限设置方面比更新版本更不严格的较早版本服务器设置的（注意更具限制的目录 menageria 和 tmp 都有较近日期）。MySQL 当前版本确保这些文件只能由运行服务器的用户读取。
让我们来修正这些权限，使得只用服务器用户可访问它们。你的主要保护工具来自于由 UNIX 文件系统本身提供的设置文件和目录属主和模式的工具。下面是我们要做的：
进入该目录
% cd DATADIR
设置所有在数据目录下的文件属主为由用于运行服务器的账号拥有（你必须以 root 执行这步）。在本文使用 mysqladm 和 mysqlgrp 作为该账号的用户名和组名。你可以使用下列命令之一改变属主：
# chown mysqladm.mysqlgrp .
# find . -follow -type d -print | xargs chown mysqladm.mysqlgrp
设置你的数据目录和数据库目录的模式使得他们只能由 mysqladm 读取，这阻止其他用户访问你数据库目录的内容。你可以用下列命令之一以 root 或 mysqladm 身份运行。
% chmod -R go-rwx .
% find . -follow -type d -print | xargs chmod go-rwx
数据目录内容的属主和模式为 mysqladm 设置。现在你应该保证你总是以 mysqladm 用户运行服务器，因为现在这是唯一由访问数据库目录权限的用户（除 root）。
在完成这些设置后，你最终应该得到下面的数据目录权限：
% ls -l
total 10148
drwxrwx--- 11 mysqladm mysqlgrp 1024 May 8 12:20 .
drwxr-xr-x 22 root wheel 512 May 8 13:31 ..
drwx------ 2 mysqladm mysqlgrp 512 Apr 16 15:57 menagerie
drwx------ 2 mysqladm mysqlgrp 512 Jan 25 20:40 mysql
drwx------ 7 mysqladm mysqlgrp 512 Aug 31 1998 sql-bench
drwx------ 2 mysqladm mysqlgrp 1536 May 6 06:11 test
drwx------ 2 mysqladm mysqlgrp 1024 May 8 18:43 tmp
....
二、外部安全性-保证网络访问的安全
MySQL 的安全系统是很灵活的，它允许你以多种不同方式设置用户权限。一般地，你可使用标准的 SQL 语句 GRANT 和 REVOKE 语句做，他们为你修改控制客户访问的授权表，然而，你可能由一个不支持这些语句的老版本的 MySQL（在 3.22.11 之前这些语句不起作用），或者你发觉用户权限看起来不是以你想要的方式工作。对于这种情况，了解 MySQL 授权表的结构和服务器如何利用它们决定访问权限是有帮助的，这样的了解允许你通过直接修改授权表增加、删除或修改用户权限，它也允许你在检查这些表时诊断权限问题。
关于如何管理用户账号，见《MySQL 的用户管理》。而对 GRANT 和 REVOKE 语句详细描述，见《MySQL 参考手册》。
2.1 MySQL 授权表的结构和内容
通过网络连接服务器的客户对 MySQL 数据库的访问由授权表内容来控制。这些表位于 mysql 数据库中，并在第一次安装 MySQL 的过程中初始化（运行 mysql_install_db 脚本）。授权表共有 5 个表：user、db、host、tables_priv 和 columns_priv。
表 1 user、db 和 host 授权表结构
访问范围列
user db host
Host Host Host
User Db Db
Password User
数据库/表权限列
Alter_priv Alter_priv Alter_priv
Create_priv Create_priv Create_priv
Delete_priv Delete_priv Delete_priv
Drop_priv Drop_priv Drop_priv
Index_priv Index_priv Index_priv
Insert_priv Insert_priv Insert_priv
References_priv References_priv References_priv
Select_priv Select_priv Select_priv
Update_priv Update_priv Update_priv
File_priv Grant_priv Grant_priv
Grant_priv
Process_priv
Reload_priv
Shutdown_priv
表 2 tables_priv 和 columns_priv 属权表结构
访问范围列
tables_priv columns_priv
Host Host
Db Db
User User
Table_name Table_name
Column_name
权限列
Table_priv Column_priv
授权表的内容有如下用途：
user 表
user 表列出可以连接服务器的用户及其口令，并且它指定他们有哪种全局（超级用户）权限。在 user 表启用的任何权限均是全局权限，并适用于所有数据库。例如，如果你启用了 DELETE 权限，在这里列出的用户可以从任何表中删除记录，所以在你这样做之前要认真考虑。
db 表
db 表列出数据库，而用户有权限访问它们。在这里指定的权限适用于一个数据库中的所有表。
host 表
host 表与 db 表结合使用在一个较好层次上控制特定主机对数据库的访问权限，这可能比单独使用 db 好些。这个表不受 GRANT 和 REVOKE 语句的影响，所以，你可能发觉你根本不是用它。
tables_priv 表
tables_priv 表指定表级权限，在这里指定的一个权限适用于一个表的所有列。
columns_priv 表
columns_priv 表指定列级权限。这里指定的权限适用于一个表的特定列。
在“不用 GRANT 设置用户”一节里，我们再讨论 GRANT 语句如何对修改这些表起作用，和你怎样能通过直接修改授权表达到同样的效果。
tables_priv 和 columns_priv 表在 MySQL 3.22.11 版引进（与 GRANT 语句同时）。如果你有较早版本的 MySQL，你的 mysql 数据库将只有 user、db 和 host 表。如果你从老版本升级到 3.22.11 或更新，而没有 tables_priv 和 columns_priv 表，运行 mysql_fix_privileges_tables 脚本创建它们。
MySQL 没有 rows_priv 表，因为它不提供记录级权限，例如，你不能限制用户于表中包含特定列值的行。如果你确实需要这种能力，你必须用应用编程来提供。如果你想执行建议的记录级锁定，你可用 GET_LOCK()函数做到。