教你一步一步地加强MySQL的安全性


本文的实验都假设PHP模块已经安装在了Apache Web服务器上了,并且被放在了目录 /chroot/httpd下。除此以外,我们还作了如下的假设:

MySQL数据库仅被同主机上的PHP使用;

默认的管理工具像mysqladmin、mysql和 mysqldump,被用来管理数据库。另外,远程的数据备份将使用SSH协议执行。

为了获得更高的安全性能,对于MySQL的安装和配置,应该与以下的安全要求相符合:

1) MySQL数据库应该在被改变了默认根目录的环境下运行;

2) MySQL进程必须使用一个在系统进程中唯一的UID/GID;

3) 对MySQL的访问仅限于本地;

4) 保证MySQL根账户的密码不易被猜测;

5) 禁止对数据库的匿名访问;

6) 删除系统自带的所有的示例数据库和表;

安装MySQL

在我们开始加强MySQL的安全性之前,必须在服务器上安装它。我们在操作系统上来创建一个组和用户帐号,并使之唯一,使之与MySQL数据库相关联,方法如下:

pw groupadd mysql
pw useradd mysql -c "MySQL Server" -d /dev/null -g mysql -s /sbin/nologin

1.编译MySQL

我们先来在目录/usr/local/mysql下编译和安装MySQL软件:

./configure --prefix=/usr/local/mysql --with-mysqld-user=mysql
 --with-unix-socket-path=/tmp/mysql.sock --with-mysqld-ldflags=-all-static
make
su
make install
strip /usr/local/mysql/libexec/mysqld
scripts/mysql_install_db
chown -R root /usr/local/mysql
chown -R mysql /usr/local/mysql/var
chgrp -R mysql /usr/local/mysql

总的来说,在Apache服务器上安装MySQL的过程基本上与手动安装等同。可能唯一的不同点就是使用了一些参数而已,像指定了./configure行。这里最重要的不同点在于参数“--with-mysqld-ldflags=-all-static”的使用,它可以使MySQL服务器使用静态连接。就像在第三部分涉及到的其他参数一样,这将显著简化改变服务器的根路径的过程,它们通知make程序安装在目录/usr/local/mysql下,以mysql账户的权限运行MySQL的后台程序,并且在/tmp目录下创建mysql.sock套接字。

2.拷贝配置文件

在执行了上面的命令后,我们还必须拷贝默认的配置文件以与期望的数据库大小一致。例如:

cp support-files/my-medium.cnf /etc/my.cnf
chown root:sys /etc/my.cnf
chmod 644 /etc/my.cnf

3.启动服务器

到这里为止,MySQL已经完全安装了,并且整装待发了。我们可以通过执行下面的命令来启动MySQL的服务器:

/usr/local/mysql/bin/mysqld_safe &

4.测试连接

尝试与一个数据库建立连接:

/usr/local/mysql/bin/mysql -u root mysql

如果连接成功,系统将显示如下内容:

Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2 to server version: 4.0.13-log
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

然后,我们就可以看看本机上有哪些数据库,如下所示:

mysql> show databases;
+----------+
| Database |
+----------+
| mysql |
| test |
+----------+

2 rows in set (0.00 sec)

然后退出当前命令,使用如下命令:

mysql> quit;

一旦连接成功建立,我们就可以通过如下命令关闭数据库:

/usr/local/mysql/bin/mysqladmin -u root shutdown

如果不这样做,我们就应该分析一下存储在 /usr/local/mysql/var/`hostname`.err日志文件中的信息的安全性,以消除导致问题的因素。

改变服务器的默认根目录

接下来的第一步就是准备chrooted环境,以使MySQL服务能够运行。这里的目标操作系统是FreeBSD4.7。然而,本文中所讲授的方法同样适用于大多数的UNIX和类似UNIX的系统。

1.准备chroot环境

为了准备chrooted环境,我们必须创建如下的目录:

mkdir -p /chroot/mysql/dev
mkdir -p /chroot/mysql/etc
mkdir -p /chroot/mysql/tmp
mkdir -p /chroot/mysql/var/tmp
mkdir -p /chroot/mysql/usr/local/mysql/libexec
mkdir -p /chroot/mysql/usr/local/mysql/share/mysql/english

2.设置访问权利

上面对目录的访问权利应该如下设置:

chown -R root:sys /chroot/mysql
chmod -R 755 /chroot/mysql
chmod 1777 /chroot/mysql/tmp

3.创建目录结构

接下来,下面的这些文件应该拷贝到新的目录中:

cp /usr/local/mysql/libexec/mysqld /chroot/mysql/usr/local/mysql/libexec/
cp /usr/local/mysql/share/mysql/english/errmsg.sys/ chroot/mysql/usr/local/mysql
/share/mysql/english/
cp /etc/hosts /chroot/mysql/etc/
cp /etc/host.conf /chroot/mysql/etc/
cp /etc/resolv.conf /chroot/mysql/etc/
cp /etc/group /chroot/mysql/etc/
cp /etc/master.passwd /chroot/mysql/etc/passwords
cp /etc/my.cnf /chroot/mysql/etc/

4.强化密码和组

我们必须从文件/chroot/mysql/etc/passwords和/chroot/mysql/etc/group里删除除了账户和组以外的行,接下来,还应该按照如下方法build密码数据库:

cd /chroot/mysql/etc
pwd_mkdb -d /chroot/mysql/etc passwords
rm -rf /chroot/mysql/etc/master.passwd

5.特殊考虑

在Apache Web服务器上,我们还必须创建一个特殊的设备文件/dev/null:

ls -al /dev/null

系统将显示如下的信息:

crw-rw-rw- 1 root sys 2, 2 Jun 21 18:31 /dev/null

然后,我们再使用如下的命令:

mknod /chroot/mysql/dev/null c 2 2
chown root:sys /chroot/mysql/dev/null
chmod 666 /chroot/mysql/dev/null

我们还必须拷贝那些包含着在MySQL安装过程中创建的包含授权表的mysql数据库:

cp -R /usr/local/mysql/var/ /chroot/mysql/usr/local/mysql/var
chown -R mysql:mysql /chroot/mysql/usr/local/mysql/var

6.本地化

如果主机上使用的默认语言不是英语,我们还应该拷贝合适的字符集,可以在目录/usr/local/mysql/share/mysql/charset下找到。

7.测试配置

现在,MySQL应该可以在chrooted环境中运行了。我们可以通过下面这个命令来验证一下它是否能正确的运行:





chrootuid /chroot/mysql mysql /usr/local/mysql/libexec/mysqld &

如果在执行了上面的命令后出现了错误,我们应该使用truss命令或者具有等价功能的命令如:ktrace/kdump、strace等等。这些命令可以帮助我们来找出和消除引起问题的原因。

(脚本小子:这里为了能够运行mysqld进程,使用的是chrootuid程序而不是chroot程序,因为假设是在Apache或者PHP的环境下,它们两个的主要不同在于chrootuid可以改变执行进程的所有者,在我们的例子里面,mysqld在一个chrooted环境下被执行的,但是进程的所有者不是root而是mysql用户,Chrootid在许多操作系统里并不是默认安装的,需要我们从网上下载下来,然后手动安装上去,可以到网址[url]ftp://ftp.porcupine.org/pub/security/chrootuid1.3.tar.gz[/url]下载。)

配置服务器

接下来的步骤,我们就要来配置数据库服务器,使之能够适合我们的安全性要求。

在假定MySQL进行默认安装的时候,主要的配置文件是/etc/my.cnf。然而,在我们的例子里,因为是在chrooted环境下运行服务,所以我们就要使用两个配置文件/chroot/mysql/etc/my.cnf和/etc/my.cnf。其中,前一个可以被MySQL服务器使用,而后一个要被MySQL工具(如mysqladmin,mysql,mysqldump等)使用。

1.禁止远程访问

我们知道,MySQL默认情况下使用TCP的3306端口,所以,我们首先要改变一下。因为,按照我们开始的设想,数据库仅仅是让本机的PHP应用程序访问,所以,我们可以将这个监听端口的功能禁止掉。这样就可以降低MySQL数据库遭受直接经由TCP/IP访问的外部主机攻击的可能性。而同时,本地的交互还可以通过mysql.sock套接字进行。为了能够禁止在3306端口上监听,我们要将参数skip-networking添加到/chroot/mysql/etc/my.cnf目录下[mysqld]部分。如果在有的时候还需要从外部访问数据库,可以按照如下方法使用SSH协议:

backuphost$ ssh mysqlserver /usr/local/mysql/bin/mysqldump -A > backup

2.提高本地安全性

接下来,为了防止对本地文件进行未授权的访问,我们还需要禁止掉对LOAD DATA LOCAL INFILE命令的使用。这对于有效防止当在PHP应用程序中发现了新的SQL注入漏洞是非常有效的。这里,我们需要对在目录/chroot/mysql/etc/my.cnf下的[mysqld]部分加一个参数set-variable=local-infile=0。

另外,为了更方便的使用数据库管理工具,我们还必须改变/etc/my.cnf目录[client]部分的参数设置为socket = /chroot/mysql/tmp/mysql.sock。这样,当我们以后再需要用到管理工具的时候,就不用每次都来使用带有socket=/chroot/mysql/tmp/mysql.sock参数的命令了。

3.改变管理员的密码

在加强MySQL安全性的这些步骤里,改变管理员的密码是很重要的一步,但是,这首先需要MySQL运行起来,可以通过如下命令实现:

chrootuid /chroot/mysql mysql /usr/local/mysql/libexec/mysqld &

用下面的命令改变管理员的密码:

/usr/local/mysql/bin/mysql -u root 
mysql> SET PASSWORD FOR root@localhost=PASSWORD('new_password');

(脚本小子:不要直接在命令行里改变密码,如使用“mysqladmin password”命令,因为这就有可能被同时使用服务器的其他用户看到,从而泄漏密码。例如:其他用户可以使用“ps aux”命令或者查看历史文件,就可以看到我们设置的密码。)

4.删除默认的数据库和用户

接下来,我们必须删除示例数据库和除了root账户以外的所有的账户,可以使用如下命令来实现:

mysql> drop database test;
mysql> use mysql;
mysql> delete from db;
mysql> delete from user where not (host="localhost" and user="root");
mysql> flush privileges;

这就可以有效阻止对数据库的匿名连接和清除在/chroot/mysql/etc/my.cnf下的与skip-networking无关的参数,从而阻止远程连接。

5.改变管理员的登陆名称

我们同样也推荐将默认管理员的账户名称修改,这样,就可以使得外部用户很难猜测账户名称。这样一个改变,就可以有效的阻止对管理员密码的暴力攻击和字典攻击。因为,攻击者首先要猜出用户名称,然后才能猜测密码。可以通过如下方式改变:

mysql> update user set user="mydbadmin" where user="root";
mysql> flush privileges;

6.删除历史纪录文件

最后,我们应该将MySQL中历史文件的内容删除,因为所有执行过的SQL命令都被以明文的形式存储在这里,很容易泄密。这可以通过如下方式实现:

cat /dev/null > '/.mysql_history

如何在PHP和MySQL之间进行交互

因为本地的PHP与MySQL之间的交互通过/tmp/mysql.sock套接字进行,而将PHP放在chrooted环境中意味着它们之间不能交互。为了解决这个问题,在我们每次使用MySQL的时候,都必须先创建一个到PHP的chrooted环境的稳固连接。这可以通过如下命令实现:

ln /chroot/mysql/tmp/mysql.sock /chroot/httpd/tmp/

(脚本小子:/chroot/mysql/tmp/mysql.sock套接字和/chroot/httpd/tmp目录必须处在相同的文件系统下,否则他们之间的稳固连接将不能正常工作。)

现在,我们要创建能够被指定的PHP应用使用的所有的数据库和账户,需要注意的是,这些帐户仅仅拥有通过PHP应用对数据库进行访问的权利。特别需要指出的就是,这些帐户不应该具有对mysql数据库访问的任何权力,当然更不用说对系统的访问和管理特权(FILE, GRANT, ALTER, SHOW DATABASE, RELOAD, SHUTDOWN, PROCESS, SUPER 等)了。

最后,我们也应该创建一个shell脚本,用来在启动系统的时候来运行MySQL,可以到[url]http://www.securityfocus.com/unix/images/mysql.sh[/url]上下载,而在我们的FreeBSD系统里,上面的脚本应该放在/usr/local/etc/rc.d目录下,取名为mysql.sh。

好了,大功告成,通过上面的设置,如果有人想通过mysql入侵你的服务器,估计不死都得掉层皮,嘿嘿!

采用上面陈述的方法,我们可以显著提高MySQL的安全性。通过在一个chrooted环境下运行数据库,禁止掉对TCP3306端口的监听,采用强壮的用户密码,我们就可以使数据库免受大多数的攻击。虽然没有方法能够使我们得到100%的安全性,但是使用描述的方法,至少可以降低来自访问我们的Web服务的用户的不明目的攻击的可能性。

(T114)