Безопасность MySQL: Шаг-за-шагом

mysql security

Безопасность MySQL: Шаг-за-шагом

© перевод Cobalt 11.07.2006 статья не оптимизирована

 Вот готов перевод еще одной статьи Артура Маджа из цикла Шаг-за-Шагом. Эта
статья посвящена серверу баз данных MySQL. Перевод осуществлен в рамках
проекта GFS. Перевел Cobalt. Оригинал на английском можно найти тут:
http://www.securityfocus.com/infocus/1726

1. Вступление

 MySQL один из самых популярных серверов баз данных используемых в интернете
совместно с PHP. Кроме своих несравненных преимуществ таких как легкость в
использовании и относительно высокий класс исполнения, MySQL обладает
простым но эффективным механизмом безопасности. Но вслучае дефолтовой установеи,
когда рутовый пароль пустой, и велика вероятность потенциальных уязвимостей
переполнения буфера, из базы данных получается легкая цель для атаки.

 Эта статья описывает основные шаги которые необходимо предпринять для
защиты базы данных от большинства типов удаленных и локальных атак. Эта статья
является третьей статьей цикал Безопасность Шаг-за-Шагом. Первые две посвящаны
безопасности Apache и PHP (прим. переводчика: их можно найти у нас на сайте).

1.1 Функциональность

 Эта статья подразумевает что вебсервер Apache совместно с PHP установлен и
настроен в соответствии с предыдущими статьями и помещен в /chroot/httpd.

 К вышеуказанному можно добавить следущее:

        * Базы данных MySQL будут использоваться только PHP скриптами которые
          находятся на данном хосту.
        * Дефолтовые административные утилиты, такие как  mysqladmin, mysql,
          mysqldump etc. будут использоваться для управления базой данных.
        * Резервное копирование на удаленный хост будет проводиться через
          SHH протокол.

1.2 Требования безопасности

 Для достижения максимально возможного уровня безопасности, установка и настройка
MySQL должна соответствовать следующим требованиям:

        * MySQL должна запускаться в чрутовом окружении
        * MySQL процессы должны запускатьсяч из под UIDGID не используемых
          другими процессами.
        * Только локальный доступ к MySQL должен быть разрешен.
        * Учетная запись MySQL root должна быть защищена паролем сложным для
          брута.
        * Административный аккаунт должен быть переименован.
        * Необходимо запретить анонимный доступ к БД.
        * Все тестовые таблицы и базы данных должны быть удалены.

2. Установка MySQL

 Перед тем как собственно начать процесс защиты MySQL, мы должны установить софт
на сервер. В соответствии с тем что писалось выше, мы начнем с создания
уникального аккаунта под которым будет работать сервер:

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

2.1 Компиляция MySQL

 Мы будем компилировать и устанавливать MySQL в директорию /usr/local/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

 Вобщем, процесс установки MySQL практически идентичен описаному в мануале.
Единственное отличие в использовании нескольких дополнительных параметров.
Главное - использование ключа -with-mysqld-ldflags=-all-static, который
заставлет компилятор собрать MySQL статически. Это значительно упростит
процесс чрутинга.На равне с остальными параметрами, мы заставляем
make собрать сервер в директории /usr/local/mysql и запускать его
под учетной записью mysql. При запуске сервер будет создавать сокет mysql.sock
в директории /tmp.

2.2 Копирование конфигурационных файлов

 После выполнения превиденных команд, мы должны скопировать дефолтовые
конфиги в соответствии с предполагаемым размером БД (small, medium, large, huge).
Например:

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

2.3 Запуск сервера

 Теперь MySQL полностью установлен и готов к запуску. Мы можем запустить
его выполнив такую команду:

/usr/local/mysql/bin/mysqld_safe &

2.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

3. Чрутинг сервера

 Первым шагом на пути к безопасности сервера, является создание чрутового окружения
в котором он будет работать. Технология чрутинга описана в одной из предыдущих
статей цикла ("Securing Apache: Step-by-Step"). Если вы с ней не знакомы, то
настоятельно рекомендую прочитать предыдущию статью.

3.1 Операционная система

 Так же как и в предыдущих статьях мы будем использовать FreeBSD 4.7. Но
описаный метод может быть слегкостью применен и на других Unix-like системах.

3.2 Подготовка чрутового окружения

Создадим дерево директорий:

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

3.3 Установка прав доступа

Права доступа должны быть следущими:

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

3.4 Создание дерева дерикторий

Скопируем следущие файлы в новое окружение:

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/

3.5 Перенос паролей и групп

Из фалов /chroot/mysql/etc/passwords и /chroot/mysql/etc/group необходимо
удалить все строки кроме содержащих mysql аккаунт и группу. Теперь построем
базу данных паролей следущим образом (это применимо только для FreeBSD):

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

3.6 Специальные устройства

Как и в случае Apacha, нам надо создать специальное устройство /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

 Теперь скопируем базы данных созданые при установке:

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

3.7 Локализация

 Если используются какие-то языки кроме английского, надо скопировать соответ-
ствующую кодировку из директории /usr/local/mysql/share/mysql/charsets.

3.8 Тестирование конфигурации

 Теперь MySQL готов к запуску в чрутовом окружении. Мы можем протестировать его
запустив следующей командой:

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

 В случае ошибок, используйте truss или ktrace/kdump, strace, etc. Это поможет
вам локализовать проблему.

Примечание: Заметьте, что для запуска демона mysql была использована программа
chrootuid а не chroot как в случае с Apache и PHP. Основная разница в том, что
chrootuid изменяет владельца запускаемого процесса. В нашем примере, mysqld
запускается в чрутовом окружении, но владельцем процесса будет не root а mysql.
chrootuid по умолчанию не установлена во многие операционные системы, и может
понадобиться скачать и установить ее. Она может быть скачена от сюда:
ftp://ftp.porcupine.org/pub/security/chrootuid1.3.tar.gz

4. Настройка сервера

 Следующим шагом будет настройка сервера баз данных в соответствии с заявлеными
требованиями безопасности.

 В случае дефолтовой установки, главный конфиг находится в /etc/my.cnf. В нашем
случае, т.к. сервер запущен в чрутовом окружении, мы будем использовать два
конфигурационных файла: /chroot/mysql/etc/my.cnf и /etc/my.cnf. Первый используется
сервером MySQL, а второй утилитами (т.к. mysqladmin, mysql, mysqldump etc.). В
обоих случаях необходимы некоторые изменения.

4.1 Отключение удаленного доступа

 Первое изменение касается 3306/tcp порта, на котором MySQL установлен по
умолчанию. Т.к. нам необходимо разрешить только локальные соединения, мы можем
задисэйблить этот порт. Это значительно ограничит кол-во типов аттак на сервер.
Локальные же соединения будут проводиться с использованием mysql.sock сокета.
И так, внесем следущие орбавления в секцию [mysqld] файла /chroot/mysql/etc/my.cnf:

skip-networking

Если же нам все-таки понадобиться удаленное соединение с сервером (например для
резервного копирования данных) мы можем воспользоваться SSH:

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

4.2 Повышение локальной безопасности

 Следующим шагом станет отключение команды LOAD DATA LOCAL INFILE, которая
может позволить злоумышленнику читать локальные файлы. Это будет иметь особое
значение когда будут найдены новые уязвимости типа SQL-inj в используемых
PHP-скриптах.

Для этого добавте такую строку в секцию [mysqld] файла /chroot/mysql/etc/my.cnf:

set-variable=local-infile=0

Теперь, чтобы сделать удобным применение административных утилит, добавим
в секцию [client] файла /etc/my.cnf такую строку:

socket = /chroot/mysql/tmp/mysql.sock

Благодаря этому, теперь не понадобиться каждый раз при использовании
утилит типа mysql, mysqladmin, mysqldump etc. потставлять к ним в параметры
--socket=/chroot/mysql/tmp/mysql.sock.

4.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" или просмотром фалов истории
команд (~/.history, ~/.bash_history etc).

4.4 Удаление дефолтовых пользователейбаз данных

 Теперь мы должны удалить стандартную бызу данных (test) и все аккаунты кроме
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;

 Это предохранит базу данных от анонимных соединений.

4.5 Изменение имени администратора

 Также, очень рекомендуется сменить дефолтовое имя администратора root на более
сложное для подбора. Такое изменение может значительно затруднить атаку типа
brute-force и подбор по словарю.  В этом случае, нарушитель обычно не знает
ни акаунта не порароля, но предпологает, что используется аккаунт по умолчанию
root.

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

4.6 Удаление истории

 В завершении, мы можем удалить историю команд MySQL, ведь среди них содержатся
команды по установке нового пароля. По этому делаем следущее:

cat /dev/null > ~/.mysql_history

5. Соединение PHP с MySQL

 В предыдущей статье ("Securing PHP: Step-by-Step"), я упомянал о проблемме
комуникации между PHP и MySQL когда они выполняются в окружении croot. Потому как
для соединения PHP с MySQL используется сокет /tmp/mysql.sock, помещение PHP
в чрутовое окружение может привести к тому, что он не сможет соедениться с сокетом.
Для решения этой проблемы, при запуске MySQL необходимо создать жесткую ссылку
на чрутовое окружение PHP:

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

Примечательно, что /chroot/mysql/tmp/mysql.sock и /chroot/httpd/tmp должны
физически находиться в одной файловой системе. В противном случае, програмы
не смогут соедениться - жесткие ссылки не работают между файловыми системами.

6. Заключение

 Теперь можно создавать все аккаунты и базы данных, которые будут использованы
в ваших PHP скриптах. Однако, надо подчеркнуть, что создаваемый аккаунт должен
иметь доступ только к тем БД которые непосредственно используются в скрипте.
На практике, он может не иметь никаких прав на базу mysql и системных привелегий
(FILE, GRANT, ALTER, SHOW DATABASE, RELOAD, SHUTDOWN, PROCESS, SUPER etc.).

 И наконец, нам надо написать сценарий по которому будет запускаться MySQL во
время запуска ОС:

#!/bin/sh

CHROOT_MYSQL=/chroot/mysql
CHROOT_PHP=/chroot/httpd
SOCKET=/tmp/mysql.sock
MYSQLD=/usr/local/mysql/libexec/mysqld
PIDFILE=/usr/local/mysql/var/`hostname`.pid
CHROOTUID=/usr/local/sbin/chrootuid

echo -n " mysql"

case "$1" in
start)
        rm -rf ${CHROOT_PHP}/${SOCKET}
        nohup ${CHROOTUID} ${CHROOT_MYSQL} mysql ${MYSQLD} >/dev/null 2>&1 &
        sleep 5 && ln ${CHROOT_MYSQL}/${SOCKET} ${CHROOT_PHP}/${SOCKET}
        ;;
stop)
        kill `cat ${CHROOT_MYSQL}/${PIDFILE}`
        rm -rf ${CHROOT_MYSQL}/${SOCKET}
        ;;
*)
        echo ""
        echo "Usage: `basename $0` {start|stop}" >&2
        exit 64
        ;;
esac

exit 0

 В случае FreeBSD этот сценарий надо поместить в /usr/local/etc/rc.d под
именем mysql.sh.

6.1 Выводы

 Применение метода описаного в статье, позволяет значительно повысить уровень
безопасности сервера MySQL. Запуском сервера в чрутовом окружении, отключением
3306-го порта и применением сложных паролей, мы можем сделать базу данных
непробиваемой от многих типов аттак которые возможны при дефолтовой установке.

  Хотя ни какой метод не гарантирует вам 100%-й безопасности, применяя описаное
в этой статье, вы можете по крайней мере ограничить возможность нападения от
посетителей посещающих ваш сайт с дурными намерениями.

© Artur Maj