Debian.pro/

Про Debian


Большой мануал: часть 22. Ставим phpmyadmin и делаем его чуть безопаснее.

Эта статья — часть Большого Мануала по настройке lamp-сервера на debian.

Предыдущая часть цикла — Учимся делать редиректы в nginx.

Следующая часть цикла — Делаем бэкапы.

Вообще я не любитель использовать PMA (здесь и далее — PhpMyAdmin). Он не умеет ничего такого, чего нельзя было бы сделать в консольном mysql, а местами даже умеет меньше. Но на чужих серверах ставить его всё же приходится.

Вообще, прежде чем наживать себе геморрой на задницу, посмотрите на «легкую» альтернативу PMA — https://www.adminer.org/. Он, в буквальном смысле, состоит из одного файла (раньше были стили в отдельных файлах, но сейчас всё вроде в один положили). Хоть и проблемы обеспечения безопасности у вас будут почти те же и всю статью прочитать всё же придется, но с adminer-ом всё же немного попроще. Да и кодовая база меньше, уязвимостей меньше.

Перейдем к проблемам безопасности, с которыми нам придется бороться.
Во-первых PMA (и adminer, само собой) обязаны работать только по https. Иначе любые данные, передаваемые в браузере (а это и пароли к базе, и её содержимое) у вас смогут перехватить в открытой сети. Ну или провайдер заинтересуется. Или по СОРМ-у нагрепают нужный пароль. Впрочем, с https мы уже справились в прошлых частях, так что тут всё просто. Здесь же важно понимать, что HTTP-протокол туповат и редиректа на https там, куда приходят POST-запросы, недостаточно (данные-то по http будут отправлены).
Во-вторых, очень важно понимать, что в PMA находили достаточно уязвимостей, которые можно было эксплуатировать без авторизации в mysql (честно говоря я не помню сколько, но и одной достаточно). Да и сам торчащий голой жопой PMA в ситуации, когда пароль от базы могут достать где-то ещё, вынуждает задуматься о том, что мы катимся не туда. В общем, PMA (и adminer тоже, куда ж без него), обязательно необходимо убирать за дополнительную авторизацию. Я ниже рассмотрю авторизацию по IP и http basic auth.
В третьих, PMA — излюбленная цель для CSRF-атак. В чём их суть. Если кто-то проникнет на ваш сервер (сами по ssh пустите или php-шелл зальют), то можно будет обращаться с самого сервера в http://127.0.0.1:8080 (или где у вас там апач). И если настраивать ограничение доступа к PMA на уровне nginx-а — то мы оставляем лазейку для скрипт-киддисов. И если с большинством CMS/CMF эта проблема не очень актуальна, то для PMA (с учетом абзаца выше) такая проблема — весьма критична. Поэтому ограничения мы будем реализовывать на уровне apache (через htaccess-файл) и конфиге самого PMA, дабы человек, добравшийся до возможной CSRF-атаки, не получил доступа к PMA.

Теперь об установке. Для дебианщиков есть 2 более или менее «правильных» способа установки PMA. Первый — пойти на сайт phpmyadmin.org, скачать архив и положить в каталог определенного домена на сервере. Такой способ работает только на вашем первом сервере, с которого вы сдуваете пылинки. Как только у вас появляется второй — вы забиваете на обновление установленного таким образом PMA и он обрастает мхом.
Мы будем ставить из пакета. Хотя бы периодически вы запускаете apt-get upgrade, тогда и PMA будет обновляться.

Ну, поехали. Ставим пакет:

root@server:~# apt-get -qq update; apt-get install phpmyadmin

Когда у вас будут спрашивать, какой у вас веб-сервер, выбирайте стрелочками apache2, нажимайте пробел, и только потом нажимайте tab и enter (а то задавали мне уже вопрос на эту тему, чего PMA не поставился из пакета). Потом у вас спросят что-то в духе «Configure database for phpmyadmin with dbconfig-common?», отвечаем Yes.
«Password of the database’s administrative user» — наш пароль от рута в mysql (мы его ещё в .my.cnf сохраняли, дабы не протерялся).
«MySQL application password for phpmyadmin» — пароль для базы самого PMA. Вообще он нам больше не понадобится, так что скопируйте туда любые 100 символов из случайного места в интернете. Дважды (потом подтверждение спросят же).

На этом в общем-то установка закончена и наш уютненький сервер радостно торчит огромной дыркой в безопасности на весь интернет. Ну а радостные роботы-сканеры эту дырку уже нашли и пытаются в неё чем-нибудь потыкаться. Здесь бы и статью закончить, но зачем я тогда лишних 600 слов писал? Давайте исправлять.

Для начала выбираем URL, по которому в будущем будет доступен наш PMA. Да, можно, конечно и оставить /phpmyadmin на всех доменах (как это организовано по умолчанию), но это не наш путь.
Допустим, у нас fqdn у сервера host.example.com. Допустим, у нас свободен наружу порт 7443. Допустим, мы сможем запомнить /pma-is-bad в качестве uri. Чудесно, будем селить по урлу https://host.example.com:7443/pma-is-bad

Для начала заведем конфиг в nginx (напомню, что сертификат мы уже получили, а nginx немного настроили). Заводим файл /etc/nginx/sites-available/phpmyadmin7443.conf (опять же, смысл написанного в этом файле можно посмотреть по ссылкам в предыдущих скобках).

# Молча дропаем все запросы по https в 7443-й порт, если они пришли в "неправильный" домен:
server {
    listen 7443 ssl default;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/cert.pem;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2;
    ssl_session_cache shared:SSL:64m;
    ssl_session_timeout 28h;
    location / { return 444; }
}


# Здесь и далее - server{} для домена, на котором будет работать PMA.
server {
    listen 7443 ssl;
    server_name host.example.com;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/cert.pem;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2;
    ssl_session_cache shared:SSL:64m;
    ssl_session_timeout 28h;

# location для самого PMA, проксируем запросы к нему в apache:
    location /pma-is-bad {
        proxy_pass http://127.0.0.1:81;
        proxy_redirect http://127.0.0.1:81/ /;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # запросы к файлам .htaccess/.htpasswd на всякий случай фильтруем и здесь тоже.
    location ~ /\.ht {
        return 404;
    }
# остальные запросы тоже молча дропаем:
    location / { return 444; }
}

Не забываем сделать симлинку в sites-enabled и рестартнуть nginx:

root@server:~# ln -s /etc/nginx/sites-available/phpmyadmin7443.conf /etc/nginx/sites-enabled/phpmyadmin7443.conf; nginx -t && /etc/init.d/nginx restart

Следующий конфиг, который нам интересен, это /etc/apache2/conf-enabled/phpmyadmin.conf
Ищем в нём строку:

Alias /phpmyadmin /usr/share/phpmyadmin

И меняем на (опять же, /pma-is-bad меняйте на то, что сами придумали):

Alias /pma-is-bad /usr/share/phpmyadmin

Так же где-то в блоке «<Directory /usr/share/phpmyadmin>» (можно следующей строкой, например) добавляем такую строчку:

AllowOverride All

Эта строчка разрешает использовать файлы htaccess в /usr/share/phpmyadmin (дада, all не очень безопасно, только вот писать в /usr/share/phpmyadmin может только рут).
Рестартим апач:

root@server:~# apachectl restart

Теперь пора положить .htaccess файл в /usr/share/phpmyadmin/.htaccess

# Подключаем http-auth авторизацию
 AuthType Basic
 AuthName "PMA"
 AuthUserFile /usr/share/phpmyadmin/.htpasswd
 Require valid-user

Заводим пользователя для basic-авторизации (команда htpasswd запросит пароль для пользователя):

root@server:~# htpasswd -nB username >> /usr/share/phpmyadmin/.htpasswd

Собственно, команда htpasswd -n генерирует строчку из username и хэша его пароля. -B — указывает, что для генерации хэша нужно использовать bcrypt-функции (судя по ману, сейчас это самый безопасный вариант в плане перебора пароля по хэшу). При помощи >> мы записываем сгенерированную строку в нужный нам файл (указанный в .htaccess).
Чтобы удалить пользователя — просто удаляем его строку из .htpasswd, чтобы сменить пароль — удаляем строку, а потом генерируем новую. Кстати, сменить username, не меняя пароля, можно тупо поменяв его в файлике.

Сама строчка выглядит для username с паролем 123 примерно вот так:

username:$2y$05$m/jEXSvgh84huqR2bsFHN.Vvkw4pLcYYB/ZHdPDr/mQ/NZ4dnFEAe

Если есть возможность (статический адрес или хотя бы подсеть у админов, которым нужен доступ в PMA) — очень хорошо будет ограничить доступ к PMA по IP-адресам. Например, так (всё в том же /usr/share/phpmyadmin/.htaccess):

 order deny,allow
 deny from all
 # можно указывать подсети
 allow from 192.168.0.0/24
 # или отдельные адреса
 allow from 10.0.0.23

В любом случае, неплохим решением будет запретить доступ с локалхоста в PMA:

 deny from 127.0.0.1

Ну и ещё, если вы не заморачивались с запретом location /pma-is-bad на каждом сайте, есть смысл сделать редирект для тех, кто попадёт в PMA по какой-то «левой ссылке» (через другой домен, например):

RewriteEngine On
RewriteCond %{HTTP_HOST} !^host\.example\.com$ [NC,OR]
RewriteCond %{REQUEST_URI} !^/pma-is-bad [NC]
RewriteRule (.*) https://host.example.com:7443/pma-is-bad [R=301,L]

Само собой, примеры htaccess-а выше можно комбинировать друг с другом в одном файле. Ну и если кто забыл — то рестартить апацхе после изменений в .htaccess-файлах не нужно.

14.12.2017 byinkvizitor68sl|big-manual

Комментарии (2):

  1. Tony :

    +1 к adminer (если уж оно вообще надо)

    С возвращением!

    P.S. ждем бакапы. В теории, это вообще должна была быть 2ая статья по счету :)

  2. Бэкапы следующая будет точно. На следующей неделе в тудушник закинул.

    > С возвращением!
    спасибо

Написать комментарий