Делаем безопасный хостинг PHP-сайтов

Безопасность php -сайтовЭта статья написана только по одной причине: мы сами сначала наступили на грабли. Наши сайты (skycase.ru и skycase.ru) сделаны на WordPress, т.е., внутри у них PHP и MySQL. Когда мы искали хостинг для них последнее о чем мы думали была безопасность. А зря. Буквально через два месяца сайты были взломаны, на главной странице висела фишинговая форма входа в Facebook. Удивительным образом это событие совпало с недоступностью сайта самого хостинг-провайдера, а недолгие поиски в Сети нашли несколько прежних обсуждений о взломанных серверах этого хостера. Конечно же, мы делаем бэкапы, сайты были восстановлены за несколько часов, но Gooogle успел добавить их в черный список. И последствия этого мы ощущаем до сих пор. Так вот, это статья для тех, кто (больше) не хочет наступать на грабли shared web hosting services  — «обычных» сервисов хостинга сайтов.

Чем плох обычный PHP хостинг?

Рассмотрим поставленный вопрос только из соображений безопасности вашего сайта.

  1. Все хостеры размещают на одном сервере по несколько сотен, а иногда и тысяч, сайтов своих клиентов. Если злоумышленник взломал такой сервер, то все сайты на этом сервере становятся жертвами.
  2. Если вы приобрели хостинг для нескольких сайтов (соблазнившись соображениями очевидной экономии, как это когда-то сделали мы), то все ваши файлы на хостинге принадлежат одному и тому же пользователю системы, выданному вам хостером. Т.е., если злоумышленник смог получить доступ к системе, взломав один из ваших сайтов, все остальные ваши сайты тоже станут его жертвами.

Как сделать безопасным хостинг сайта?

Безопасность — это всегда солнечный зайчик. Его не поймать. Поэтому для обеспечения безопасности собственных сайтов мы пришли к разумному пересечению кривых паранойи и ресурсных затрат. Ресурсы здесь, как и всегда, это деньги и время. Вот наш security рецепт.

  1. Используйте виртуальные выделенные сервера (VDS) вместо обычного shared хостинга.
    Почему? Потому что на собственном сервере вы в значительно большей степени хозяин положения:

    • Вы сами можете настроить PHP: редактировать php.ini, устанавливать только необходимые модули PHP, …
    • На своем виртуальном сервере можно безопасно разместить несколько сайтов.
    • Можно установить любимый фаервол и дать своей паранойе по-настоящему разгуляться!

    Команда SkyCase совершенно бесплатно для себя рекомендует присмотреться к сервису digitalocean.com, где сейчас за 5$ в месяц можно арендовать виртуальный сервер с 512 MB оперативной памяти, 20 GB на SSD диске, 1 TB трафика и кучей прочих плюшек вроде снэпшотов и бэкапов.

  2. Настройте SSH-аутентификацию по ключам и запретите доступ по логину-паролю. Инструкции для вашего сервера вы легко найдете в интернете.
  3. Создайте в системе по одному специальному пользователю на каждый размещенный на сервере сайт и запускайте PHP-скрипты каждого конкретного сайта от имени соответствующего пользователя. Таким образом, взломав один сайт, злоумышленник не сможет получить доступ ни к системе, ни к другим сайтам на этом сервер. Сказать легко, а сделать … мы расскажем вам как.

Запуск PHP-скриптов от имени пользователя, которому принадлежит сайт

Концепт такой: nginx работает как фронтенд, раздает статику, а динимаку, PHP-скрипты, обрабатывает PHP-демон php5-fpm.

А теперь пора засучить рукава. Вы уже разобрались с SSH, и находитесь в уютной консоли свеже-купленного виртуального сервера. Сразу оговорюсь, что все инструкции ниже написаны (и проверены) для Ubuntu версии 12.04. Если вы предпочитаете другие дистрибутивы, то процесс конфигурирования будет примерно таким же, с точностью до управления менеджером пакетов и расположением конфиг-файлов в системе.

  1. Устанавливаем nginx (в минимальной комплектации) и PHP
    sudo apt-get install nginx-light
    sudo service nginx restart
    sudo apt-get install php5
    sudo service php5-fpm restart
    
  2. Создаем системного пользователя с именем USERNAME. Он будет хозяином конкретного сайта, и будет удобно если его имя недвусмысленно говорит о названии сайта.
    sudo useradd --system --create-home --user-group --shell /bin/false USERNAME
    
  3. В домашнем каталоге пользователя USERNAME создаем каталог www, который будет являться рутовым для данного сайта.
    sudo mkdir /home/USERNAME/www
    
  4. Создаем тестовый PHP-скрипт чтобы позже проверить что вся схема работает исправно.
    sudo nano /home/USERNAME/www/test.php
    
    <?php phpinfo(); ?>
    
  5. К динамическому контенту (PHP-скриптам) сайта доступ, притом только на чтение, должен быть только у пользователя USERNAME.
    sudo chown -R USERNAME:USERNAME /home/USERNAME/www/
    sudo chmod -R a-rw /home/USERNAME/www/
    sudo chmod -R u+r /home/USERNAME/www/
    
  6. PHP-демон будет обрабатывать запросы, пришедшие на соответствующий сокет от nginx, от имени пользователя USERNAME. Для этого надо скопировать дефолтный конфиг fpm, назвать его соответственно, а в самом конфиге поменять имя и группу пользователя и имя слушаемого им сокета.
    sudo cp /etc/php5/fpm/pool.d/www.conf /etc/php5/fpm/pool.d/USERNAME.conf
    sudo nano /etc/php5/fpm/pool.d/USERNAME.conf
    
    [USERNAME]
    ; Unix user/group of processes
    ; Note: The user is mandatory. If the group is not set, the default user's group
    ;       will be used.
    user = USERNAME
    group = USERNAME
    
    ; The address on which to accept FastCGI requests.
    ; Valid syntaxes are:
    ;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
    ;                            a specific port;
    ;   'port'                 - to listen on a TCP socket to all addresses on a
    ;                            specific port;
    ;   '/path/to/unix/socket' - to listen on a unix socket.
    ; Note: This value is mandatory.
    listen = /var/run/php5-fpm.USERNAME.sock
    ...
    
    sudo service php5-fpm restart
    
  7. Создаем конфигурацию сайта в nginx, где перенаправляем все запросы на динамический контент на PHP-демона через сконфигуренный в пункте 6 сокет. Статитка раздается nginx-ом прямо с файловой системы.
    sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/USERNAME
    sudo nano /etc/nginx/sites-available/USERNAME
    
    server {
    	listen 80;
    
    	root /home/USERNAME/www;
    	index index.php index.html index.htm;
    
    	server_name skycase-example.com;
    
    	location / {
    		# Сначала пытаемся обработать запрос как файл, затем
    		# как каталог, а если не получилось - показываем 404 ошибку.
    		try_files $uri $uri/ =404;
    	}
    
    	# Запрещаем доступ к скрытым файлам, таким как .htaccess, .htpasswd, .DS_Store (Mac).
    	location ~ /\. {
        	deny all;
            access_log off;
            log_not_found off;
    	}
    
    	location ~* \.(jpg|jpeg|png|gif|css|js|ico)$ {
        	expires max;
            log_not_found off;
    	}
    
    	# Перенаправляем запрос на PHP-скрипты к FastCGI серверу, слушающему на файл-сокете.
        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php5-fpm.USERNAME.sock;
            fastcgi_index index.php;
            include fastcgi_params;
        }
    }
    
    sudo ln -s /etc/nginx/sites-available/USERNAME /etc/nginx/sites-enabled/USERNAME
    sudo service nginx reload
    
  8. Чтобы nginx работал с php5-fpm демоном нужно в файле конфигурации PHP-демона изменить значение cgi.fix_pathinfo на 0.
    sudo nano /etc/php5/fpm/php.ini
    ...
    sudo service php5-fpm reload
    
  9. Пора проверять! Откройте в браузере нашу тестовую страницу skycase-example.com/test.php. Должна открыться стандартная страница со всеми подробностями настройки PHP. Обратите внимание на раздел Environment, он должен говорить нам, что:
    USER 	USERNAME
    HOME 	/home/USERNAME
    

Все, победа!

Нет, не совсем. Сейчас nginx работает от имени пользователя www-data, а файлы сайта эксклюзивно принадлежат пользователю USERNAME и группе USERNAME. Nginx раздает статику, но не имеет к ней доступа. Плохо. Как я и говорил в начале статьи, нужно удерживать паранойю в узде. Разумным компромиссом будет разрешить доступ для группы www-data на чтение для всей статики сайта, сохранив эксклюзивное право на чтение PHP-скриптов для пользователя USERNAME. Что мы и проделаем.

sudo chown -R USERNAME:www-data /home/USERNAME/www/
sudo chmod -R g+r /home/USERNAME/www/
sudo find /home/USERNAME/www/ -type f -name "*.php" -exec chown USERNAME:USERNAME {} \;

Резюме

На старый вопрос «Как по-простому сделать систему X безопасной» есть старый ответ. Никак. Безопасность — это всегда компромисс. Стоит ли «заморачиваться», да еще и так изрядно как описано в этой статье — решать вам. Если от сайта зависит имидж вашей компании, работа с клиентами, и, наконец, доход, тогда… В общем, мы стали заморачиваться.
И да минует нас всех злой злоумышленник!

Добавить комментарий