===================================
php记录用户操作日志
1.保存到数据库的话,数据量会非常大--肯定影响性能
2.保存到文件的话,是保存到一个文件中还是根据日期来保存到很多个文件中
如果保存到一个文件中也会有数据过大的问题
保存到很多文件的话如何实现根据不同用户检索操作日志以及分页显示问题
1
保存到日志文件中或者保存到document db(如mongodb)都是一个优秀的解决方案。
保存到日志的话相对占用容量较少,不过需要自己写脚本分析日志。
可以存mongo这种文档型数据库
2 保存到数据库的话,数据量会非常大 --- 量大不一定就影响性能, 你得具体分析分析读写数据的情况, 合理合理分表分库
3
保存在文件的话可以根据用户的ID来做目录,然后再用日期来归类,比如7天或30天就重新生成日志目录,这样的话就可以很大的减轻查询压力,再详细的话可以每种操作则单独生成一个日志
而且建议用文件来保存,不要用数据库,
栗子:
用户1
目录:uid1
--------2014-1
-----------查看日志.php
-----------修改日志.php
-----------删除日志.php
--------2014-2
-----------查看日志.php
-----------修改日志.php
-----------删除日志.php
============================================================
MySQL 物理文件组成
日志文件
1、错误日志:Error Log 错误日志记录了 MyQL Server 运行过程中所有较为严重的警告和错误信息,以及 MySQL Server 每次启动和关闭的详细信息。在默认情况下,系统记录错误日志的功能是关闭的, 错误信息被输出到标准错误输出(stderr),如果要开启系统记录错误日志的功能,需要在 启动时开启-log-error 选项。
2、二进制日志:Binary Log & Binary Log Index 二进制日志,也就是我们常说的 binlog,也是 MySQL Server 中最为重要的日志之一。 当我们通过“--log-bin[=file_name]”打开了记录的功能之后,MySQL 会将所有修改数据 库数据的 query 以二进制形式记录到日志文件中。当然,日志中并不仅限于 query 语句这么 简单,还包括每一条 query 所执行的时间,所消耗的资源,以及相关的事务信息,所以 binlog 是事务安全的。
mysql-bin.index 文件(binary log index)的功能是记录所有 Binary Log 的绝对路 径,保证 MySQL 各种线程能够顺利的根据它找到所有需要的 Binary Log 文件。
3、更新日志:update log 更新日志是 MySQL 在较老的版本上使用的,其功能和 binlog 基本类似,只不过不是以 二进制格式来记录而是以简单的文本格式记录内容。自从 MySQL 增加了 binlog 功能之后, 就很少使用更新日志了。从版本 5.0 开始,MySQL 已经不再支持更新日志了。
4、查询日志:query log 查询日志记录 MySQL 中所有的 query,通过“--log[=fina_name]”来打开该功能。由 于记录了所有的 query,包括所有的 select,体积比较大,开启后对性能也有较大的影响, 所以请大家慎用该功能。一般只用于跟踪某些特殊的 sql 性能问题才会短暂打开该功能。默 认的查询日志文件名为 hostname.log。
5、慢查询日志:slow query log 顾名思义,慢查询日志中记录的是执行时间较长的 query,也就是我们常说的 slow query,通过设--log-slow-queries[=file_name]来打开该功能并设置记录位置和文件名, 默认文件名为 hostname-slow.log,默认目录也是数据目录。
慢查询日志采用的是简单的文本格式,可以通过各种文本编辑器查看其中的内容。其中 记录了语句执行的时刻,执行所消耗的时间,执行用户,连接主机等相关信息。MySQL 还提 供了专门用来分析满查询日志的工具程序 mysqlslowdump,用来帮助数据库管理人员解决可 能存在的性能问题。
6、Innodb 的在线 redo 日志:innodb redo log Innodb 是一个事务安全的存储引擎,其事务安全性主要就是通过在线 redo 日志和记录 在表空间中的 undo 信息来保证的。redo 日志中记录了 Innodb 所做的所有物理变更和事务 信息,通过 redo日志和undo信息,Innodb保证了在任何情况下的事务安全性。Innodb的redo 日志同样默认存放在数据目录下,可以通过 innodb_log_group_home_dir来更改设置日志的 存放位置,通过 innodb_log_files_in_group设置日志的数量。
数据文件
在 MySQL 中每一个数据库都会在定义好(或者默认)的数据目录下存在一个以数据库名 字命名的文件夹,用来存放该数据库中各种表数据文件。不同的 MySQL 存储引擎有各自不同 的数据文件,存放位置也有区别。多数存储引擎的数据文件都存放在和 MyISAM 数据文件位 置相同的目录下,但是每个数据文件的扩展名却各不一样。如 MyISAM 用“.MYD”作为扩展 名,Innodb 用“.ibd”,Archive 用“.arc”,CSV 用“.csv”,等等。
1、“.frm”文件 与表相关的元数据(meta)信息都存放在“.frm”文件中,包括表结构的定义信息等。 不论是什么存储引擎,每一个表都会有一个以表名命名的“.frm”文件。所有的“.frm”文 件都存放在所属数据库的文件夹下面。
2、“.MYD”文件
“.MYD”文件是 MyISAM 存储引擎专用,存放 MyISAM 表的数据。每一个 MyISAM 表都会 有一个“.MYD”文件与之对应,同样存放于所属数据库的文件夹下,和“.frm”文件在一起 。
3、“.MYI”文件 “.MYI”文件也是专属于 MyISAM 存储引擎的,主要存放 MyISAM 表的索引相关信息。对 于 MyISAM 存储来说,可以被 cache 的内容主要就是来源于“.MYI”文件中。每一个 MyISAM 表对应一个“.MYI”文件,存放于位置和“.frm”以及“.MYD”一样。
4、“.ibd”文件和 ibdata 文件 这两种文件都是存放 Innodb 数据的文件,之所以有两种文件来存放 Innodb 的数据(包 括索引),是因为 Innodb 的数据存储方式能够通过配置来决定是使用共享表空间存放存储数 据,还是独享表空间存放存储数据。独享表空间存储方式使用“.ibd”文件来存放数据,且 每个表一个“.ibd”文件,文件存放在和 MyISAM 数据相同的位置。如果选用共享存储表空 间来存放数据,则会使用 ibdata 文件来存放,所有表共同使用一个(或者多个,可自行配 置)ibdata 文件。ibdata 文件可以通过 innodb_data_home_dir 和 innodb_data_file_path 两 个 参 数 共 同 配 置 组 成 , innodb_data_home_dir 配 置 数 据 存 放 的 总 目 录 , 而 innodb_data_file_path 配 置 每 一 个 文 件 的 名 称 。 当 然 , 也 可 以 不 配 置 innodb_data_home_dir 而直接在 innodb_data_file_path 参数配置的时候使用绝对路径来 完成配置。innodb_data_file_path中可以一次配置多个 ibdata 文件。文件可以是指定大 小,也可以是自动扩展的,但是 Innodb 限制了仅仅只有最后一个 ibdata 文件能够配置成自 动扩展类型。当我们需要添加新的 ibdata 文件的时候,只能添加在 innodb_data_file_path 配置的最后,而且必须重启 MySQL 才能完成 ibdata 的添加工作。不过如果我们使用独享表 空间存储方式的话,就不会有这样的问题,但是如果要使用裸设备的话,每个表一个裸设备 , 可能造成裸设备数量非常大,而且不太容易控制大小,实现比较困难,而共享表空间却不会 有这个问题,容易控制裸设备数量。我个人还是更倾向于使用独享表空间存储方式。
Replication 相关文件:
1、master.info 文件: master.info 文件存在于 Slave 端的数据目录下,里面存放了该 Slave 的 Master 端的 相关信息,包括 Master 的主机地址,连接用户,连接密码,连接端口,当前日志位置,已 经读取到的日志位置等信息。
2、relay log 和 relay log index mysql-relay-bin.xxxxxn 文件用于存放 Slave 端的 I/O 线程从 Master 端所读取到 的 Binary Log 信息,然后由 Slave 端的 SQL 线程从该 relay log 中读取并解析相应的 日志信息,转化成 Master 所执行的 SQL 语句,然后在 Slave 端应用。
mysql-relay-bin.index 文件的功能类似于 mysql-bin.index ,同样是记录日志的存
放位置的绝对路径,只不过他所记录的不是 Binary Log,而是 Relay Log。
3、relay-log.info 文件: 类似于 master.info,它存放通过 Slave 的 I/O 线程写入到本地的 relay log 的相关信 息。供 Slave 端的 SQL 线程以及某些管理操作随时能够获取当前复制的相关信息。
2.1.4 其他文件: 1、system config file MySQL 的系统配置文件一般都是“my.cnf”,Unix/Linux 下默认存放在"/etc"目录下, Windows 环境一般存放在“c:/windows”目录下面。“my.cnf”文件中包含多种参数选项组 (group),每一种参数组都通过中括号给定了固定的组名,如“[mysqld]”组中包括了 mysqld 服务启动时候的初始化参数, “[client]”组中包含着客户端工具程序可以读取的参数,此 外还有其他针对于各个客户端软件的特定参数组,如 mysql 程序使用的“[mysql]”,mysqlchk 使用的“[mysqlchk]”,等等。如果读者朋友自己编写了某个客户端程序,也可以自己设定 一个参数组名,将相关参数配置在里面,然后调用 mysql 客户端 api 程序中的参数读取 api 读取相关参数。
2、pid file pid file 是 mysqld 应用程序在 Unix/Linux 环境下的一个进程文件,和许多其他 Unix/Linux 服务端程序一样,存放着自己的进程 id。
3、socket file socket 文件也是在 Unix/Linux 环境下才有的,用户在 Unix/Linux 环境下客户端连接 可以不通过 TCP/IP 网络而直接使用 Unix Socket 来连接 MySQL。
===========================================================
MySQL 自带工具使用
mysqladmin
Usage: mysqladmin [OPTIONS] command command ...
mysqadmin, MySQL 管理相关的各种功能。如 MySQL Server 状态检查,各种统计信息的 flush,创建/删除数据库,关闭 MySQL Server 等等。mysqladmin 所能做的事情,虽然大部分都可以通过 mysql 连接登录上 MySQL Server 之后来完成,但是 大部分通过 mysqladmin 来完成操作会更简单更方便。 几个常用功能:
ping 命令可以很容易检测 MySQL Server 是否还能正常提供服务
sky@sky:~# mysqladmin -u sky -ppwd -h localhost ping
mysqld is alive
status 命令可以获取当前 MySQL Server 的几个基本的状态值:
sky@sky:~# mysqladmin -u sky -ppwd -h localhost status
Uptime: 20960 Threads: 1 Questions: 75 Slow queries: 0 Opens: 15 Flush tables: 1 Open tables: 9 Queries per second avg: 0.3
processlist 获取当前数据库的连接线程信息:
sky@sky:~# mysqladmin -u sky -ppwd -h localhost processlist
+----+------+-----------+----+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+----+---------+------+-------+------------------+
| 48 | sky | localhost | | Query | 0 | | show processlist | +----+------+-
----------+----+---------+------+-------+------------------+
完成一些比较基本的监控来说,
还可以通过 mysqladmin 来 start slave 和 stop slave,
kill 某个连接到 MySQL Server 的线程等等。
mysqldump
Usage: mysqldump [OPTIONS] database [tables]
OR
mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...] OR mysqldump [OPTIONS] --all-databases [OPTIONS]
mysqldump 将 MySQL Server 中的数据以 SQL 语句的形式从数据库中 dump 成文本文件。
虽然 mysqldump 是做为 MySQL 的 一种逻辑备份工具为大家所认识,
SQL 生成导出工具更合适一点,
通过 mysqldump 所生成的文件,全部是 SQL 语句,包括数据库和表的创建语句。
当然,通过 给 mysqldump 程序加“-T”选项参数之后,可以生成非 SQL 形式的指定给是的文本文件。
这 个功能实际上是调用了 MySQL 中的“select * into OUTFILE from ...”语句而实现。
也可 以通过“-d,--no-data”仅仅生成结构创建的语句。在声称 SQL 语句的时候,字符集设置这 一项也是比较关键的,建议每次执行 mysqldump 程序的时候都通过尽量做到“--defaultcharacter-set=name”显式指定字符集内容,以防止以错误的字符集生成不可用的内容。 mysqldump 所生成的 SQL 文件可以通过 mysql 工具执行。
4、mysqlimport Usage: mysqlimport [OPTIONS] database textfile ...
mysqlimport 程序是一个将以特定格式存放的文本数据(如通过“select * into OUTFILE from ...”所生成的数据文件)导入到指定的 MySQL Server 中的工具程序,比如 将一个标准的 csv 文件导入到某指定数据库的指定表中。mysqlimport 工具实际上也只是 “load data infile”命令的一个包装实现。
5、mysqlbinlog
Usage: mysqlbinlog [OPTIONS] log-files
mysqlbinlog 程序的主要功能就是分析 MySQL Server 所产生的二进制日志(也就是大 家所熟知的 binlog)。当我们希望通过之前备份的 binlog 做一些指定时间之类的恢复的时 候,mysqlbinlog 就可以帮助我们找到恢复操作需要做哪些事情。通过 mysqlbinlog,我们 可以解析出 binlog 中指定时间段或者指定日志起始和结束位置的内容解析成 SQL 语句,并 导出到指定的文件中,在解析过程中,还可以通过指定数据库名称来过滤输出内容。
6、mysqlcheck
Usage: mysqlcheck [OPTIONS] database [tables]
OR mysqlcheck [OPTIONS] --databases DB1 [DB2 DB3...] OR mysqlcheck [OPTIONS] --all-databases
mysqlcheck 工具程序可以检查(check),修复(repair),分析(analyze)和优化 (optimize)MySQL Server 中的表,但并不是所有的存储引擎都支持这里所有的四个功能, 像 Innodb 就不支持修复功能。实际上,mysqlcheck 程序的这四个功能都可以通过 mysql 连 接登录到 MySQL Server 之后来执行相应命令完成完全相同的任务。
7、myisamchk
Usage: myisamchk [OPTIONS] tables[.MYI]
功能有点类似“mysqlcheck -c/-r”,对检查和修复 MyISAM 存储引擎的表,但只能对 MyISAM 存储引擎的索引文件有效,而且不用登录连接上 MySQL Server 即可完成操作。
8、myisampack
Usage: myisampack [OPTIONS] filename ...
对 MyISAM 表进行压缩处理,以缩减占用存储空间,一般主要用在归档备份的场景下, 而且压缩后的 MyISAM 表会变成只读,不能进行任何修改操作。当我们希望归档备份某些历 史数据表,而又希望该表能够提供较为高效的查询服务的时候,就可以通过 myisampack 工 具程序来对该 MyISAM 表进行压缩,因为即使虽然更换成 archive 存储引擎也能够将表变成 只读的压缩表,但是 archive 表是没有索引支持的,而通过压缩后的 MyISAM 表仍然可以使 用其索引。
9、mysqlhotcopy
Usage: mysqlhotcopy db_name[./table_regex/] [new_db_name | directory]
mysqlhotcopy 和其他的客户端工具程序不太一样的是他不是 c(或者 c++)程序编写的 , 而是一个 perl 脚本程序,仅能在 Unix/Linux 环境下使用。他的主要功能就是对 MySQL 中 的 MyISAM 存储引擎的表进行在线备份操作,其备份操作实际上就是通过对数据库中的表进 行加锁,然后复制其结构,数据和索引文件来完成备份操作,当然,也可以通过指定“-noindices”告诉 mysqlhotcopy 不需要备份索引文件。
=========================================================
存储引擎
在 MySQL 中使用最为广泛的除了 MyISAM 之外,就非 Innodb 莫属了。
Innodb 之所以能如此受宠,主要是在于其功能方面的较多特点:
1、支持事务安装
Innodb在功能方面最重要的一点就是对事务安全的支持,这无疑是让Innodb成为MySQL 最为流行的存储引擎之一的一个非常重要原因。而且实现了 SQL92 标准所定义的所有四个级 别(READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ 和 SERIALIZABLE)。对事务安 全的支持,无疑让很多之前因为特殊业务要求而不得不放弃使用 MySQL 的用户转向支持 MySQL,以及之前对数据库选型持观望态度的用户,也大大增加了对 MySQL 好感。
2、数据多版本读取
Innodb 在事务支持的同时,为了保证数据的一致性已经并发时候的性能,通过对 undo 信息,实现了数据的多版本读取。
3、锁定机制的改进 Innodb 改变了 MyISAM 的锁机制,实现了行锁。虽然 Innodb 的行锁机制的实现是通过 索引来完成的,但毕竟在数据库中 99%的 SQL 语句都是要使用索引来做检索数据的。所以, 行锁定机制也无疑为 Innodb 在承受高并发压力的环境下增强了不小的竞争力。
4、实现外键 Innodb 实现了外键引用这一数据库的重要特性,使在数据库端控制部分数据的完整性 成为可能。虽然很多数据库系统调优专家都建议不要这样做,但是对于不少用户来说在数据 库端加如外键控制可能仍然是成本最低的选择。
除了以上几个功能上面的亮点之外,Innodb 还有很多其他一些功能特色常常带给使用 者不小的惊喜,同时也为 MySQL 带来了更多的客户。
在物理存储方卖弄,Innodb 存储引擎也和 MyISAM 不太一样,虽然也有.frm 文件来存放 表结构定义相关的元数据,但是表数据和索引数据是存放在一起的。至于是每个表单独存放 还是所有表存放在一起,完全由用户来决定(通过特定配置),同时还支持符号链接。
Innodb 的物理结构分为两大部分: 1、数据文件(表数据和索引数据) 存放数据表中的数据和所有的索引数据,包括主键和其他普通索引。在 Innodb 中,存 在了表空间(tablespace)这样一个概念,但是他和 Oracle 的表空间又有较大的不同。首 先,Innodb 的表空间分为两种形式。一种是共享表空间,也就是所有表和索引数据被存放 在同一个表空间(一个或多个数据文件)中,通过 innodb_data_file_path来指定,增加数 据文件需要停机重启。 另外一种是独享表空间,也就是每个表的数据和索引被存放在一个 单独的.ibd 文件中。
虽然我们可以自行设定使用共享表空间还是独享表空间来存放我们的表,但是共享表空 间都是必须存在的,因为 Innodb 的 undo 信息和其他一些元数据信息都是存放在共享表空间 里面的。共享表空间的数据文件是可以设置为固定大小和可自动扩展大小两种形式的,自动 扩展形式的文件可以设置文件的最大大小和每次扩展量。在创建自动扩展的数据文件的时 候,建议大家最好加上最大尺寸的属性,一个原因是文件系统本身是有一定大小限制的(但 是 Innodb 并不知道),还有一个原因就是自身维护的方便。另外,Innodb 不仅可以使用文 件系统,还可以使用原始块设备,也就是我们常说的裸设备。
当我们的文件表空间快要用完的时候,我们必须要为其增加数据文件,当然,只有共享 表 空 间 有 此 操 作 。 共 享 表 空 间 增 加 数 据 文 件 的 操 作 比 较 简 单 , 只 需 要 在 innodb_data_file_path参数后面按照标准格式设置好文件路径和相关属性即可,不过这里 有一点需要注意的,就是 Innodb 在创建新数据文件的时候是不会创建目录的,如果指定目 录不存在,则会报错并无法启动。另外一个较为令人头疼的就是 Innodb 在给共享表空间增 加数据文件之后,必须要重启数据库系统才能生效,如果是使用裸设备,还需要有两次重启 。 这也是我一直不太喜欢使用共享表空间而选用独享表空间的原因之一。
2、日志文件 Innodb 的日志文件和 Oracle 的 redo 日志比较类似,同样可以设置多个日志组(最少 2 个),同样采用轮循策略来顺序的写入,甚至在老版本中还有和 Oracle 一样的日志归档特性 。 如果你的数据库中有创建了 Innodb 的表,那么千万别全部删除 innodb 的日志文件,因为很 可能就会让你的数据库 crash,无法启动,或者是丢失数据。
由于 Innodb 是事务安全的存储引擎,所以系统 Crash 对他来说并不能造成非常严重的 损失,由于有 redo 日志的存在,有 checkpoint 机制的保护,Innodb 完全可以通过 redo 日 志将数据库 Crash 时刻已经完成但还没有来得及将数据写入磁盘的事务恢复,也能够将所有 部分完成并已经写入磁盘的未完成事务回滚并将数据还原。
Innodb 不仅在功能特性方面和 MyISAM 存储引擎有较大区别,在配置上面也是单独处理 的。在 MySQL 启动参数文件设置中,Innodb 的所有参数基本上都带有前缀“innodb_”,不 论是 innodb 数据和日志相关,还是其他一些性能,事务等等相关的参数都是一样。和所有 Innodb 相关的系统变量一样,所有的 Innodb 相关的系统状态值也同样全部以“Innodb_” 前缀。当然,我们也完全可以仅仅通过一个参数(skip-innodb)来屏蔽 MySQL 中的 Innodb 存储引擎,这样即使我们在安装编译的时候将 Innodb 存储引擎安装进去了,使用者也无法 创建 Innodb 的表
================================
Memory 存储引擎:
Memory 存储引擎,通过名字就很容易让人知道,他是一个将数据存储在内存中的存储 引擎。Memory 存储引擎不会将任何数据存放到磁盘上,仅仅存放了一个表结构相关信息 的.frm 文件在磁盘上面。所以一旦 MySQL Crash 或者主机 Crash 之后,Memory 的表就只剩 下一个结构了。Memory 表支持索引,并且同时支持 Hash 和 B-Tree 两种格式的索引。由于 是存放在内存中,所以 Memory 都是按照定长的空间来存储数据的,而且不支持 BLOB 和 TEXT 类型的字段。Memory 存储引擎实现页级锁定。
既然所有数据都存放在内存中,那么他对内存的消耗量是可想而知的。在 MySQL 的用户 手册上面有这样一个公式来计算 Memory 表实际需要消耗的内存大小: SUM_OVER_ALL_BTREE_KEYS(max_length_of_key + sizeof(char*) * 4) + SUM_OVER_ALL_HASH_KEYS(sizeof(char*) * 2) + ALIGN(length_of_row+1, sizeof(char*))
================================================
权限系统
MySQL 的权限系统在实现上比较简单,相关权限信息主要存储在几个被称为 grant tables 的系统表中,即:mysql.User,mysql.db,mysql.Host,mysql.table_priv 和 mysql.column_priv。由于权限信息数据量比较小,而且访问又非常频繁,所以 Mysql 在启 动的时候,就会将所有的权限信息都 Load 到内存中保存在几个特定的结构中。所以才有我 们每次手工修改了权限相关的表之后,都需要执行“FLUSH PRIVILEGES”命令重新加载 MySQL 的权限信息。当然,如果我们通过 GRANT,REVOKE 或者 DROP USER 命令来修改相关权限,则 不需要手工执行 FLUSH PRIVILEGES 命令,因为通过 GRANT,REVOKE 或者 DROP USER 命令所 做的权限修改在修改系统表的同时也会更新内存结构中的权限信息。在 MySQL5.0.2 或更高 版本的时候,MySQL还增加了CREATE USER命令,以此创建无任何特别权限(仅拥有初始USAGE 权限)的用户,通过 CREATE USER 命令创建新了新用户之后,新用户的信息也会自动更新到 内存结构中。所以,建议读者一般情况下尽量使用 GRANT,REVOKE,CREATE USER 以及 DROP USER 命令来进行用户和权限的变更操作,尽量减少直接修改 grant tables 来实现用户和权 限变更的操作。
权限授予与去除
要为某个用户授权,可以使用 GRANT 命令,要去除某个用户已有的权限则使用 REVOKE 命令。当然,出了这两者之外还有一种比较暴力的办法,那就是直接更新 grant tables 系 统表。当给某个用户授权的时候,不仅需要指定用户名,同时还要指定来访主机。如果在授 权的时候仅指定用户名,则 MySQL 会自动认为是对'username'@'%'授权。要去除某个用户的 的权限同样也需要指定来访主机。
可能有些时候我们还会需要查看某个用户目前拥有的权限,这可以通过两个方式实现, 首先是通过执行“SHOW GRANTS FOR 'username'@'hostname'” 命令来获取之前该用户身上 的所有授权。另一种方法是查询 grant tables 里面的权限信息。
权限级别
MySQL 中的权限分为五个级别,分别如下:
1、Global Level: Global Level 的权限控制又称为全局权限控制,所有权限信息都保存在 mysql.user 表 中。Global Level 的所有权限都是针对整个 mysqld 的,对所有的数据库下的所有表及所有 字段都有效。如果一个权限是以 Global Level 来授予的,则会覆盖其他所有级别的相同权 限设置。比如我们首先给 abc 用户授权可以 UPDATE 指定数据库如 test 的 t 表,然后又在 全局级别 REVOKE 掉了 abc 用户对所有数据库的所有表的 UPDATE 权限。则这时候的 abc 用户 将不再拥有用对 test.t 表的更新权限。Global Level 主要有如下这些权限(见表 4-1):
表 4-1 名称 版本支持 限制信息 ALTER ALL 表结构更改权限 ALTER ROUTINE 5.0.3+ procedure,function 和 trigger 等的 变更权限 CREATE ALL 数据库,表和索引的创建权限 CREATE ROUTINE 5.0.3+ procedure,function 和 trigger 等的 变更权限 CREATE TEMPORARY TABLES 4.0.2+ 临时表的创建权限 CREATE USER 5.0.3+ 创建用户的权限 CREATE VIEW 5.0.1+ 创建视图的权限 DELETE All 删除表数据的权限 DROP All 删除数据库对象的权限 EXECUTE 5.0.3+ procedure,function 和 trigger 等的 执行权限 FILE All 执行 LOAD DATA INFILE 和 SELECT ... INTO FILE 的权限 INDEX All 在已有表上创建索引的权限 INSERT All 数据插入权限 LOCK TABLES 4.0.2+ 执行 LOCK TABLES 命令显示给表加锁的 权限 PROCESS All 执行 SHOW PROCESSLIST 命令的权限 RELOAD All 执行FLUSH等让数据库重新Load某些对 象或者数据的命令的权限 REPLICATION CLIENT 4.0.2+ 执 行 SHOW MASTER STATUS 和 SHOW SLAVE STATUS 命令的权限 REPLICATION SLAVE 4.0.2+ 复制环境中 Slave 连接用户所需要的复 制权限 SELECT All 数据查询权限 SHOW DATABASES 4.0.2+ 执行 SHOW DATABASES 命令的权限 SHOW VIEW 5.0.1+ 执行 SHOW CREATE VIEW 命令查看 view 创建语句的权限 SHUTDOWN All MySQL Server 的 shut down 权限(如通 过mysqladmin执行 shutdown命令所使 用的连接用户) SUPER 4.0.2+ 执 行 kill 线 程 , CHANGE MASTER, PURGE MASTER LOGS, and SET GLOBAL 等命令的权限 UPDATE All 更新数据的权限 USAGE All 新创建用户后不授任何权限的时候所拥 有的最小权限
要授予 Global Level 的权限,则只需要在执行 GRANT 命令的时候,用“*.*”来指定适 用范围是 Global 的即可,当有多个权限需要授予的时候,也并不需要多次重复执行 GRANT 命令,只需要一次将所有需要的权限名称通过逗号( “,”)分隔开即可,如下:
root@localhost : mysql 05:14:35> GRANT SELECT,UPDATE,DELETE,INSERT ON *.* TO 'def'@'localhost'; Query OK, 0 rows affected (0.00 sec)
2、Database Level Database Level 是在 Global Level 之下,其他三个 Level 之上的权限级别,其作用域 即为所指定整个数据库中的所有对象。与 Global Level 的权限相比,Database Level 主要 少了以下几个权限:CREATE USER,FILE,PROCESS,RELOAD,REPLICATION CLIENT,REPLICATION SLAVE,SHOW DATABASES,SHUTDOWN,SUPER 和 USAGE 这几个权限,没有增加任何权限。之 前我们说过Global Level的权限会覆盖底下其他四层的相同权限,Database Level也一样 , 虽然他自己可能会被 Global Level 的权限设置所覆盖,但同时他也能覆盖比他更下层的 Table,Column 和 Routine 这三层的权限。
如果要授予 Database Level 的权限,则可以有两种实现方式: 1、在执行 GRANT 命令的时候,通过“database.*”来限定权限作用域为 database 整个 数据库,如下: root@localhost : mysql 06:06:26> GRANT ALTER ON test.* TO 'def'@'localhost'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 06:12:45> SHOW GRANTS FOR def@localhost; +------------------------------------------------------------------+ | Grants for def@localhost | +------------------------------------------------------------------+ | GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'def'@'localhost' | | GRANT ALTER ON `test`.* TO 'def'@'localhost' | +------------------------------------------------------------------+
2、先通过 USE 命令选定需要授权的数据库,然后通过“*”来限定作用域,这样授权的 作用域实际上就是当前选定的整个数据库。 root@localhost : mysql 06:14:05> USE test; Database changed root@localhost : test 06:13:10> GRANT DROP ON * TO 'def'@'localhost'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 06:15:26> SHOW GRANTS FOR def@localhost; +------------------------------------------------------------------+ | Grants for def@localhost | +------------------------------------------------------------------+ | GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'def'@'localhost' | | GRANT DROP, ALTER ON `test`.* TO 'def'@'localhost' |
+------------------------------------------------------------------+
在授予权限的时候,如果有相同的权限需要授予多个用户,我们也可以在授权语句中一 次写上多个用户信息,通过逗号(,)分隔开就可以了,如下: root@localhost : mysql 05:22:32> grant create on perf.* to 'abc'@'localhost','def'@'localhost'; Query OK, 0 rows affected (0.00 sec)
root@localhost : mysql 05:22:46> SHOW GRANTS FOR def@localhost; +------------------------------------------------------------------+ | Grants for def@localhost | +------------------------------------------------------------------+ | GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'def'@'localhost' | | GRANT DROP, ALTER ON `test`.* TO 'def'@'localhost' | | GRANT CREATE ON `perf`.* TO 'def'@'localhost' | +-----------------------------------------------------------------+ 3 rows in set (0.00 sec)
root@localhost : mysql 05:23:13> SHOW GRANTS FOR abc@localhost; +------------------------------------------------------------------+ | Grants for abc@localhost | +------------------------------------------------------------------+ | GRANT CREATE ON `perf`.* TO 'abc'@'localhost' | | GRANT SELECT ON `test`.* TO 'abc'@'localhost' | +------------------------------------------------------------------+ 3 rows in set (0.00 sec)
3、Table Level Database Level 之下就是 Table Level 的权限了,Table Level 的权限可以被 Global Level 和 Database Level 的权限所覆盖,同时也能覆盖 Column Level 和 Routine Level 的 权限。
Table Level 的权限作用范围是授权语句中所指定数据库的指定表。如可以通过如下语 句给 test 数据库的 t1 表授权: root@localhost : test 12:02:15> GRANT INDEX ON test.t1 TO 'abc'@'%.jianzhaoyang.com'; Query OK, 0 rows affected, 1 warning (0.00 sec)
root@localhost : test 12:02:53> SHOW GRANTS FOR 'abc'@'%.jianzhaoyang.com'; +----------------------------------------------------------+ | Grants for abc@*.jianzhaoyang.com | +----------------------------------------------------------+ | GRANT USAGE ON *.* TO 'abc'@'%.jianzhaoyang.com' |
| GRANT INDEX ON `test`.`t1` TO 'abc'@'%.jianzhaoyang.com' | +----------------------------------------------------------+
上面的授权语句在测试给 test 数据库的 t1 表授予 Table Level 的权限的同时,还测试 了将权限授予含有通配符“%”的所有“.jianzhaoyang.com”主机。其中的 USAGE 权限是每 个用户都有的最基本权限。
Table Level 的权限由于其作用域仅限于某个特定的表,所以权限种类也比较少,仅有 ALTER,CREATE,DELETE,DROP,INDEX,INSERT,SELECT UPDATE 这八种权限。
4、Column Level Column Level 的权限作用范围就更小了,仅仅是某个表的指定的某个(活某些)列。 由于权限的覆盖原则,Column Level 的权限同样可以被 Global,Database,Table 这三个 级别的权限中的相同级别所覆盖,而且由于 Column Level 所针对的权限和 Routine Level 的权限作用域没有重合部分,所以不会有覆盖与被覆盖的关系。针对 Column Level 级别的 权限仅有INSERT,SELECT和UPDATE这三种。Column Level的权限授权语句语法基本和Table Level 差不多,只是需要在权限名称后面将需要授权的列名列表通过括号括起来,如下: root@localhost : test 12:14:46> GRANT SELECT(id,value) ON test.t2 TO 'abc'@'%.jianzhaoyang.com'; Query OK, 0 rows affected(0.00 sec)
root@localhost : test 12:16:49> SHOW GRANTS FOR 'abc'@'%.jianzhaoyang.com'; +-----------------------------------------------------------------------+ | Grants for abc@*.jianzhaoyang.com | +-----------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'abc'@'%.jianzhaoyang.com' | | GRANT SELECT (value, id) ON `test`.`t2` TO 'abc'@'%.jianzhaoyang.com' | | GRANT INDEX ON `test`.`t1` TO 'abc'@'%.jianzhaoyang.com' | +-----------------------------------------------------------------------+
注意:当某个用户在向某个表插入(INSERT)数据的时候,如果该用户在该表中某列上 面没有 INSERT 权限,则该列的数据将以默认值填充。这一点和很多其他的数据库都有一些 区别,是 MySQL 自己在 SQL 上面所做的扩展。
5、Routine Level
Routine Level 的权限主要只有 EXECUTE 和 ALTER ROUTINE 两种,主要针对的对象是 procedure 和 function 这两种对象,在授予 Routine Level 权限的时候,需要指定数据库 和相关对象,如: root@localhost : test 04:03:26> GRANT EXECUTE ON test.p1 to 'abc'@'localhost'; Query OK, 0 rows affected (0.00 sec)
除了上面几类权限之外,还有一个非常特殊的权限 GRANT,拥有 GRANT 权限的用户可以
将自身所拥有的任何权限全部授予其他任何用户,所以 GRANT 权限是一个非常特殊也非常重 要的权限。GRANT 权限的授予方式也和其他任何权限都不太一样,通常都是通过在执行 GRANT 授权语句的时候在最后添加 WITH GRANT OPTION 子句达到授予 GRANT 权限的目的。
此外,我们还可以通过 GRANT ALL 语句授予某个 Level 的所有可用权限给某个用户,
如: root@localhost : test 04:15:48> grant all on test.t5 to 'abc'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 04:27:39> grant all on perf.* to 'abc'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 04:27:52> show grants for 'abc'; +--------------------------------------------------+ | Grants for abc@% | +--------------------------------------------------+ | GRANT USAGE ON *.* TO 'abc'@'%' | | GRANT ALL PRIVILEGES ON `perf`.* TO 'abc'@'%' | | GRANT ALL PRIVILEGES ON `test`.`t5` TO 'abc'@'%' | +--------------------------------------------------+
在以上五个 Level 的权限中,Table、Column 和 Routine 三者在授权中所依赖(或者引 用)的对象必须是已经存在的,而不像 Database Level 的权限授予,可以在当前不存在该 数据库的时候就完成授权。
4.2.4 4.2.4 4.2.4 4.2.4 MySQL MySQL MySQL MySQL 访问控制实现原理
MySQL 访问控制实际上由两个功能模块共同组成,从第一篇的第二章架构组成中可以看 到,一个是负责“看守 MySQL 大门”的用户管理模块,另一个就是负责监控来访者每一个动 作的访问控制模块。用户管理模块决定造访客人能否进门,而访问控制模块则决定每个客人 进门能拿什么不能拿什么。下面是一张 MySQL 中实现访问控制的简单流程图(见图 4-2):
图 4-2
1、 用户管理 我们先看看用户管理模块是如何工作的。在 MySQL 中,用户访问控制部分的实现比较简 单,所有授权用户都存放在一个系统表中:mysql.user,当然这个表不仅仅存放了授权用户 的基本信息,还存放有部分细化的权限信息。用户管理模块需要使用的信息很少,主要就是 Host,User,Password 这三项,都在 mysql.user 表中,如下: sky@localhost : (none) 12:35:04> USE mysql; Database changed sky@localhost : mysql 12:35:08> DESC user; +---------------+--------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------+--------------------+------+-----+---------+-------+ | Host | char(60) | NO | PRI | | | | User | char(16) | NO | PRI | | | | Password | char(41) | NO | | | | ... ... +---------------+--------------------+------+-----+---------+-------+
一个用户要想访问 MySQL,至少需要提供上面列出的这三项数据,MySQL 才能判断是否 该让他“进门”。这三项实际上由量部分组成:访问者来源的主机名(或者主机 IP 地址信息 ) 和访问者的来访“暗号”(登录用户名和登录密码),这两部分中的任何一个没有能够匹配上 都无法让看守大门的用户管理模块乖乖开门。其中 Host 信息存放的是 MySQL 允许所对应的 User 的信任主机,可以是某个具体的主机名(如:mytest)或域名(如:www.domain.com), 也可以是以“%”来充当通配符的某个域名集合(如:%.domain.com);也可以是一个具体的 IP 地址(如:1.2.3.4),同样也可以是存在通配符的域名集合(如:1.2.3.%);还可以用“%” 来代表任何主机,就是不对访问者的主机做任何限制。如以下设置:
root@localhost : mysql 01:18:12> SELECT host,user,password FROM user ORDER BY user; +--------------------+------+-------------------------------------------+ | host | user | password | +--------------------+------+-------------------------------------------+ | % | abc | | | *.jianzhaoyang.com | abc | | | localhost | abc | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | | 1.2.3.4 | abc | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | | 1.2.3.* | def | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | | % | def | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | | localhost | def | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | ... ... +--------------------+------+-------------------------------------------+
但是这里有一个比较特殊的访问限制,如果要通过 localhost 访问的话,必须要有一条 专门针对 localhost 的授权信息,即使不对任何主机做限制也不行。如下例所示,存在 def@% 的用户设置,但是如果不使用-h 参数来访问,则登录会被拒绝,因为 mysql 在默认情况下
会连接 localhost:
sky@sky:~$ mysql -u def -p Enter password: ERROR 1045 (28000): Access denied for user 'def'@'localhost' (using password: YES)
但是当通过-h 参数,明确指定了访问的主机地址之后就没问题了,如下: sky@sky:~$ mysql -u def -p -h 127.0.0.1 Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 17 Server version: 5.0.51a-log Source distribution Type 'help;' or '\h' for help. Type '\c' to clear the buffer. [email protected] : (none) 01:26:04>
如果我们有一条localhost的访问授权则可以不使用-h 参数来指定登录host 而连接默 认的 localhost: sky@sky:~$ mysql -u abc -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 18 Server version: 5.0.51a-log Source distribution Type 'help;' or '\h' for help. Type '\c' to clear the buffer. abc@localhost : (none) 01:27:19> exit Bye
如果 MySQL 正在运行之中的时候,我们对系统做了权限调整,那调整之后的权限什么时 候会生效呢?
我们先了解何时 MySQL 存放于内存结构中的权限信息被更新:FLUSH PRIVILEGES 会强 行让 MySQL 更新 Load 到内存中的权限信息;GRANT、REVOKE 或者 CREATE USER 和 DROP USER 操作会直接更新内存中俄权限信息;重启 MySQL 会让 MySQL 完全从 grant tables 中读取权 限信息。
那内存结构中的权限信息更新之后对已经连接上的用户何时生效呢? 对于 Global Level 的权限信息的修改,仅仅只有更改之后新建连接才会用到,对于已 经连接上的 session 并不会受到影响。而对于 Database Level 的权限信息的修改,只有当 客户端请求执行了“USE database_name”命令之后,才会在重新校验中使用到新的权限信 息。所以有些时候如果在做了比较紧急的Global和Database这两个Level的权限变更之后 , 可能需要通过“KILL”命令将已经连接在 MySQL 中的 session 杀掉强迫他们重新连接以使 用更新后的权限。对于 Table Level 和 Column Level 的权限,则会在下一次需要使用到该 权限的 Query 被请求的时候生效,也就是说,对于应用来讲,这两个 Level 的权限,更新之 后立刻就生效了,而不会需要执行“KILL”命令。
2、 访问控制 当客户端连接通过用户管理模块的验证,可连接上 MySQL Server 之后,就会发送各种 Query 和 Command 给 MySQL Server,以实现客户端应用的各种功能。当 MySQL 接收到客户 端的请求之后,访问控制模块是需要校验该用户是否满足提交的请求所需要的权限。权限校 验过程是从最大范围的权限往最小范围的权限开始依次校验所涉及到的每个对象的每个权 限。 在验证所有所需权限的时候,MySQL 首先会查找存储在内存结构中的权限数据,首先查 找 Global Level 权限,如果所需权限在 Global Level 都有定义(GRANT 或者 REVOKE), 则完成权限校验(通过或者拒绝),如果没有找到所有权限的定义,则会继续往后查找 Database Level 权限,进行 Global Level 未定义的所需权限的校验,如果仍然没有能够 找到所有所需权限的定义,MySQL 会继续往更小范围的权限定义域查找,也就是 Table Level,最后则是 Column Level 或者 Routine Level。 下面我们就以客户端通过 abc@localhost 连接后请求如下 Query 我为例: SELECT id,name FROM test.t4 where status = 'deleted';
图 4-3 在前面我们了解到 MySQL 的 grant tables 有 mysql.user,mysql.db,mysql.host, mysql.table_priv 和 mysql.column_priv这五个,我想出了 mysql.host 之外的四个都是非
常容易理解的,每一个表针对 MySQL 中的一种逻辑对象,存放某一特定 Level 的权限,唯独 mysql.host 稍有区别。我们现在就来看看 mysql.host 权限表到底在 MySQL 的访问控制中充 当了一个什么样的角色呢?
mysql.host在MySQL访问控制模块中所实现的功能比较特殊,和其他几个grant tables 不太一样。首先是 mysql.host 中的权限数据不是(也不能)通过 GRANT 或者 REVOKE 来授予 或者去除,必须通过手工通过 INSERT、UPDATE 和 DELETE 命令来修改其中的数据。其次是 其中的权限数据无法单独生效,必须通过和 mysql.db 权限表的数据一起才能生效。而且仅 当 mysql.db 中存在不完整(某些场景下的特殊设置)的时候,才会促使访问控制模块再结 合 mysql.host 中查找是否有相应的补充权限数据实现以达到权限校验的目的,就比如上图 中所示。在 mysql.db 中无法找到满足权限校验的所有条件的数据(db.User = 'abc' AND db.host = 'localhost' AND db.Database_name = 'test'),则说明在 mysql.db 中无法完 成权限校验,所以也不会直接就校验 db.Select_priv 的值是否为'Y'。但是 mysql.db 中有 db.User = 'abc' AND db.Database_name = 'test' AND db.host = '' 这样一条权限信息 存在,大家可能注意到了这条权限信息中的 db.host 中是空值,注意是空值而不是'%'这个 通配符哦。当 MySQL 注意到有这样一条权限信息存在的时候,就该是 mysql.host 中所存放 的权限信息出场的时候了。这时候,MySQL 会检测 mysql.host 中是否存在满足如下条件的 权限信息:host.Host = 'localhost' AND host.Db = 'test'。如果存在,则开始进行 Select_priv 权限的校验。由于权限信息存在于 mysql.db 和 mysql.host 两者之中,而且是 两者信息合并才能满足要求,所以 Select_priv 的校验也需要两表都为'Y'才能满足要求, 通过校验。
我们已经清楚,MySQL 的权限是授予“username@hostname”的,也就是说,至少需要 用户名和主机名二者才能确定一个访问者的权限。又由于 hostname 可以是一个含有通配符 的域名,也可以是一个含有通配符的 IP 地址段。那么如果同一个用户有两条权限信息,一 条是针对特定域名的,另外一个是含有通配符的域名,而且前者属于后者包含。这时候 MySQL 如何来确定权限信息呢?实际上 MySQL 永远优先考虑更精确范围的权限。在 MySQL 内部会按 照 username 和 hostname 作一个排序,对于相同 username 的权限,其 host 信息越接近访问 者的来源 host,则排序位置越靠前,则越早被校验使用到。而且,MySQL 在权限校验过程中 , 只要找到匹配的权限之后,就不会再继续往后查找是否还有匹配的权限信息,而直接完成校 验过程。
大家应该也看到了在 mysql.user 这个权限表中有 max_questions,max_updates, max_connections,max_user_connections 这四列,前面三列是从 MySQL4.0.2 版本才开始 有的,其功能是对访问用户进行每小时所使用资源的限制,而最后的 max_user_connections 则是从 MySQL5.0.3 版本才开始有的,他和 max_connections 的区别是限制耽搁用户的连接 总次数,而不是每小时的连接次数。而要使这四项限制生效,需要在创建用户或者给用户授 权的时候加上以下四种子句: max_questions : WITH MAX_QUERIES_PER_HOUR n; max_updates : WITH MAX_UPDATES_PER_HOUR n; max_connections : WITH MAX_CONNECTIONS_PER_HOUR n; max_user_connections: MAX_USER_CONNECTIONS。
四个子句可以同时使用,如:
“ WITH MAX_QUERIES_PER_HOUR 5000 MAX_CONNECTIONS_PER_HOUR 10 MAX_USER_CONNECTIONS 10000”。
4 4 4 4. . . .3 3 3 3MySQL MySQL MySQL MySQL 访问授权策略 访问授权策略 访问授权策略 访问授权策略
在我们了解了影响数据库系统安全的相关因素以及 MySQL 权限系统的工作原理之后,就 需要为我们的系统设计一个安全合理的授权策略。我想,每个人心里都清楚,要想授权最简 单最简单方便,维护工作量最少,那自然是将所有权限都授予所有的用户来的最简单方便了 。 但是,我们大家肯定也都知道,一个用户所用有的权限越大,那么他给我们的系统所带来的 潜在威胁也就越大。所以,从安全方面来考虑的话,权限自然是授予的越小越好。一个有足 够安全意识的管理员在授权的时候,都会只授予必要的权限,而不会授予任何多余的权限。 既然我们这一章是专门讨论安全的,那么我们现在也就从安全的角度来考虑如何设计一个更 为安全合理的授权策略。
首先,需要了解来访主机。 由于 MySQL 数据库登录验证用户的时候是出了用户名和密码之外,还要验证来源主机。 所以我们还需要了解每个用户可能从哪些主机发起连接。当然,我们也可以通过授权的时候 直接通过“%”通配符来给所有主机都有访问的权限,但是这样作就违背了我们安全策略的 原则,带来了潜在风险,所以并不可取。尤其是在没有局域网的防火墙保护的情况下,更是 不能轻易允许可以从任何主机登录的用户存在。能通过具体主机名或者 IP 地址指定的尽量 通过使用具体的主机名和 IP 地址来限定来访主机,不能用具体的主机名或者 IP 地址限定的 也需要用尽可能小的通配范围来限定。
其次,了解用户需求。 既然是要做到仅授予必要的权限,那么我们必须了解每个用户所担当的角色,也就是说 , 我们需要充分了解每个用户需要连接到数据库上完成什么工作。了解该用户是一个只读应用 的用户,还是一个读写都有的帐户;是一个备份作业的用户还是一个日常管理的帐户;是只 需要访问特定的某个(或者某几个)数据库(Schema),还是需要访问所有的数据库。只有 了解了需要做什么,才能准确的了解需要授予什么样的权限。因为如果权限过低,会造成工 作无法正常完成,而权限过高,则存在潜在的安全风险。
再次,要为工作分类。 为了做到各司其职,我们需要将需要做的工作分门别类,不同类别的工作使用不同的用 户,做好用户分离。虽然这样可能会带来管理成本方面的部分工作量增加,但是基于安全方 面的考虑,这部分管理工作量的增加是非常值得的。而且我们所需要做的用户分离也只是一 个适度的分离。比如将执行备份工作、复制工作、常规应用访问、只读应用访问和日常管理 工作分别分理出单独的特定帐户来授予各自所需权限。这样,既可以让安全风险尽量降低, 也可以让同类同级别的相似权限合并在一起,不互相交织在一起。对于 PROCESS,FILE 和 SUPER 这样的特殊权限,仅仅只有管理类帐号才需要,不应该授予其他非管理帐号。
最后,确保只有绝对必要者拥有 GRANT OPTION 权限。 之前在权限系统介绍的时候我们已经了解到 GRANT OPTION 权限的特殊性,和拥有该权
限之后的潜在风险,所以在这里也就不再累述了。总之,为了安全考虑,拥有 GRANT OPTION 权限的用户越少越好,尽可能只让拥有超级权限的用户才拥有 GRANT OPTION 权限。
4 4 4 4. . . .4 4 4 4 安全设置注意事项 安全设置注意事项 安全设置注意事项 安全设置注意事项
在前面我们了解了影响数据库系统安全的几个因素,也了解了 MySQL 权限系统的相关原 理和实现,这一节我们将针对这些因素进行一些基本的安全设置讨论,了解一些必要的注意 事项。 首先,自然是最外围第一层防线的网络方面的安全。 我们首先要确定我们所维护的 MySQL 环境是否真的需要提供网络服务?是否可以使我 们的 MySQL 仅仅提供本地访问,而禁止网络服务?如果可以,那么我们可以在启动 MySQL 的时候通过使用“--skip-networking”参数选项,让 MySQL 不通过 TCP/IP 监听网络请求, 而仅仅通过命名管道或共享内存(在Windows中)或Unix套接字文件(在Unix中)来和客户端 连接交互。 当然,在本章最开始的时候,我们就已经讨论过,由于 MySQL 数据库在大部分应用场景 中都是在网络环境下,通过网络连接提供服务。所以我们只有少部分应用能通过禁用网络监 听来断绝网络访问以保持安全,剩下的大部分还是需要通过其他方案来解决网络方面存在的 潜在安全威胁。 使用私有局域网络。我们可以通过使用私有局域网络,通过网络设备,统一私有局域网 的出口,并通过网络防火墙设备控制出口的安全。
使用 SSL 加密通道。如果我们的数据对保密要求非常严格,可以启用 MySQL 提供的 SSL 访问接口,将传输数据进行加密。使网络传输的数据即使被截获,也无法轻易使用。
访问授权限定来访主机信息。在之前的权限系统介绍中我们已经了解到 MySQL 的权限信 息是针对用户和来访主机二者结合定位的。所以我们可以在授权的时候,通过指定主机的主 机名、域名或者 IP 地址信息来限定来访主机的范围。
其次,在第二层防线主机上面也有以下一些需要注意的地方。
OS 安全方面。关闭 MySQL Server 主机上面任何不需要的服务,这不仅能从安全方面减 少潜在隐患,还能减轻主机的部分负担,尽可能提高性能。使用网络扫描工具(如 nmap 等) 扫描主机端口,检查除了 MySQL 需要监听的端口 3306(或者自定义更改后的某个端口)之 外,还有哪些端口是打开正在监听的,并去掉不必要的端口。严格控制 OS 帐号的管理,以 防止帐号信息外泄,尤其是 root 和 mysql 帐号。对 root 和 mysql 等对 mysql 的相关文件有 特殊操作权限的 OS 帐号登录后做出比较显眼的提示,并在 Terminal 的提示信息中输出当前 用户信息,以防止操作的时候经过多次用户切换后出现人为误操作。
用非 root 用户运行 MySQL。这在 MySQL 官方文档中也有非常明显的提示,提醒用户不 要使用 root 用户来运行 MySQL。因为如果使用 root 用户运行 MySQL,那么 mysqld 的进程就 会拥有 root 用户所拥有的权限,任何具有 FILE 权限的 MySQL 用户就可以在 MySQL 中向系统 中的任何位置写入文件。当然,由于 MySQL 不接受操作系统层面的认证,所以任何操作系统
层级的帐号都不能直接登录 MySQL,这一点和 Oracle 的权限认证有些区别,所以在这一方 面我们可以减少一些安全方面的顾虑。
文件和进程安全。合理设置文件的权限属性,MySQL 相关的数据和日志文件和所在的文 件夹属主和所属组都设置为 mysql,且禁用其他所有用户(除了拥有超级权限的用户,如 root)的读写权限。以防止数据或者日志文件被窃取或破坏。因为如果一个用户对 MySQL 的数据文件有读取权限的话,可以很容易将数据复制。binlog 文件也很容易还原整个数据 库。而如果有写权限的话就更糟了,因为有了写权限,数据或者日志文件就有被破坏或者删 除的风险存在。保护好 socket 文件的安全,尽量不要使用默认的位置(如/tmp/mysql.sock), 以防止被有意或无意的删除。
确保 MySQL Server 所在的主机上所必要运行的其他应用或者服务足够安全,避免因为 其他应用或者服务存在安全漏洞而被入侵者攻破防线。
在 OS 层面还有很多关于安全方面的其他设置和需要注意的地方,但考虑到篇幅问题, 这里就不做进一步分析了,有兴趣的读者可以参考各种不同 OS 在安全方面的专业书籍。
再次,就是最后第三道防线 MySQL 自身方面的安全设置注意事项。
到了最后这道防线上,我们有更多需要注意的地方。
用户设置。我们必须确保任何可以访问数据库的用户都有一个比较复杂的内容作为密 码,而不是非常简单或者比较有规律的字符,以防止被使用字典破解程序攻破。在 MySQL 初始安装完成之后,系统中可能存在一个不需要任何密码的 root 用户,有些版本安装完成 之后还会存在一个可以通过 localhost 登录的没有用户名和密码的帐号。这些帐号会给系统 带来极大的安全隐患,所以我们必须在正式启用之前尽早删除,或者设置一个比较安全的密 码。对于密码数据的存放,也不要存放在简单的文本文件之中,而应该使用专业密码管理软 件来管理(如 KeePass)。同时,就像之前在网络安全注意事项部分讲到的那样,尽可能为 每一个帐户限定一定范围的可访问主机。尤其是拥有超级权限的 MySQL root 帐号,尽量确 保只能通过 localhost 访问。
安全参数。在 MySQL 官方参考手册中也有说明,不论是从安全方面考虑还是从性能以及 功能稳定性方面考虑,不需要使用的功能模块尽量都不要启用。例如,如果不需要使用用户 自定义函数,就不要在启动的时候使用“--allow-suspicious-udfs”参数选项,以防止被 别有居心的潜在威胁者利用此功能而对 MySQL 的安全造成威胁;不需要从本地文件中 Load 数据到数据库中,就使用“--local-infile=0”禁用掉可以从客户端机器上 Load 文件到数 据库中;使用新的密码规则和校验规则(不要使用“--old-passwords”启动数据库),这项 功能是为了兼容旧版本的密码校验方式的,如无额数必要,不要使用该功能,旧版本的密码 加密方式要比新的方式在安全方面弱很多。
除了以上这三道防线,我们还应该让连接 MySQL 数据库的应用程序足够安全,以防止入 侵者通过应用程序中的漏洞而入侵到应用服务器,最终通过应用程序中的数据库相关关配置 而获取数据库的登录口令。