LXF102:Доверительный PHP

Материал из Linuxformat.

Перейти к: навигация, поиск

Содержание

Доверимся PHP

В определенных кругах разработчиков безопасность PHP ставится под сомнение. Фрэнк Полманн развенчивает многие из этих мифов...

Все мы знаем, что PHP может быть источником весьма серьезных проблем с безопасностью. Регулярно обнаруживаются новые эксплойты, атакующие непосредственно интерпретатор; PHP-программисты часто имеют дело с системами управления контентом (CMS), нередко становясь при этом жертвами взломщиков.

Однако существуют весьма простые способы обеспечить безопасность, приняв во внимание то, на чем базируется работа PHP, а именно, web-сервер. За основу возьмем Apache, хотя часть последующих примеров применима также и к другим серверам.

Как запускать PHP-программы

Разбираться придется с несколькими различными случаями. Программы PHP могут, во-первых, выполняться как CGI-скрипты; во-вторых, запускаться в рамках модуля Apache; и в-третьих, программы PHP можно писать как CGI-скрипты, но выполнять как часть так называемого модуля FastCGI. Скрипты CGI вызывают особенно неприятные проблемы с безопасностью, ибо каждый CGI-скрипт выполняется как отдельный процесс. А каждый процесс, выполняемый web-сервером, наследует идентификатор пользователя (uid) web-сервера, что не есть хорошо, если кто-нибудь заполучит контроль либо над CGI-скриптом, либо над исполняемым файлом Apache: тогда выполнение Apache и всех других CGI-скриптов будут во власти атакующего.

Существуют, однако, способы держать PHP-скрипты в безопасности без обращения к средствам второй линии защиты, вроде применения chroot’а к Apache и оставшейся части пакета {L,M}AMP. Зададимся вопросом о способах присвоить каждому CGI-скрипту отдельный uid; в частности, поговорим о suEXEC. Но есть более фундаментальные приемы, с которыми нужно ознакомиться, прежде чем прибегать к средствам типа suEXEC и suPHP.

Первичное укрепление Apache

Практическую безопасность PHP можно подразделить на четыре части: безопасность файловой системы, безопасность Apache, уровень безопасности PHP-интерпретатора и безопасность PHP-клиента. Первыми тремя мы займемся целиком, а говоря о suEXEC, кратко остановимся на четвертой. Предположим, имеется стандартная схема размещения файлов: все файлы Apache расположены в /usr/local/apache2/.

ServerRoot                                /usr/local/apache2
DocumentRoot                              /usr/local/apache2/htdocs
Основной конфигурационный файл             /usr/local/apache2/conf/httpd.conf
Параметры SSL                              /usr/local/apache2/conf/ssl.conf
ErrorLog                                  /usr/local/apache2/logs/error_log
AccessLog                                 /usr/local/apache2/logs/access_log
cgi-bin                                   /usr/local/apache2/cgi-bin
Исполняемые файлы Apache                   /usr/local/apache2/bin

Можно, а иногда и нужно, иметь несколько каталогов cgi-bin; этим мы воспользуемся при разговоре о местоположении PHP-интерпретатора.

Права доступа и последствия

Следует принять в расчет два сценария:

a Различные пользователи не должны иметь возможность случайно или умышленно перезаписывать файлы или каталоги друг друга.

b Каждый пользователь может иметь дело с виртуальной копией Apache, избегая таким образом некоторых, но не всех, проблем с правами доступа.

Данная возможность отражается на производительности и безопасности, но в данном контексте мы ее обсуждать не будем.

Права доступа PHP CGI

Когда PHP скомпилирован для запуска с Apache, и используется файл конфигурации по умолчанию, рабочий режим PHP – это режим CGI-скрипта. Каждый CGI-скрипт, независимо от языка программирования, должен иметь необходимые права доступа, чтобы владелец скрипта мог запустить его. Это значит, что владелец скрипта должен уметь считывать и запускать сам скрипт, а Apache должно быть достаточно только прав на запуск. Если верить документации Apache, права доступа каталогов cgi-bin, где Apache ищет CGI-скрипты, должны быть таковы:

drwxrwxrwx

что также известно как права доступа 0777. Как мы увидим позже, все сценарии внутри каталогов cgi-bin должны восприниматься Apache как CGI-скрипты, и не иначе; отсюда следует, что надо привести в согласие права доступа всех скриптов, размещенных в каталоге cgi-bin (о файлах с данными и с контентом речь пока не идет). Права 0777 допустимы, но это рискованно, так как если вы предоставите их, любая брешь в защите приведет к тому, что люди смогут запросто перезаписывать и даже заменять CGI-скрипты, если они вызываются и модифицируются напрямую.

Строго говоря, единственный пользователь (или группа!), кто во время разработки и тестирования должен иметь право считывать, записывать и получать доступ к каталогу cgi-bin – это web-разработчик, или группа таковых. Тогда права на каталог cgi-bin снижаются до

drwxrwxr-x

они же – права доступа 775. Имеет смысл также предположить, что web-разработчику нужно считывать, изменять и выполнять файлы внутри каталога cgi-bin (и выводить их список!). По завершении разработки, когда CGI-скрипты попали на сервер, группе web-разработчиков в целом, по идее, незачем в них писать.

Выходит, права доступа для cgi-bin755, как и сделано по умолчанию на многих установках Apache и пакета LAMP (Linux, Apache, MySQL, и PHP). Они также подразумевают, что пользователь Apache, если таковой существует, сможет выводить список файлов в данном каталоге, исполнять и считывать их.

Права доступа CGI-скрипта

Так как всем остальным пользователям нужно уметь считывать CGI-скрипты, но только Apache приходится их выполнять, администратор должен установить всем CGI-скриптам права 644.

$ chmod 644 {любое_имя_файла}.cgi

Настройка Apache для PHP

Чтобы заставить web-сервер действовать по заданному сценарию, мы должны удостовериться, что прописаны определенные директивы Apache, и присутствуют определенные модули. Прежде всего следует убедиться, что пользователь Apache – единственный, которому система разрешает запускать Apache, и что этот пользователь – единственный член группы Apache. Назовем этого пользователя httpd, и группу тоже назовем httpd.

$ groupadd httpd
$ useradd httpd -g httpd -d /dev/null -s /sbin/nologin

Нам нужно создать для Apache нового пользователя, чтобы пресечь любые попытки взломщиков «угадать» владельца скрипта во время работы. Мы не дадим самому Apache многих привилегий: у него даже не будет отдельного входа в систему. Владельцу скриптов и файлов данных, конечно, полагается быть web-разработчиком или кем-то из группы web-дизайна.

Данная мера безопасности окажется бесполезной, только если кто-нибудь сумеет «угадать» идентификатор web-сервера, но для этого требуется несколько больше, чем способность выполнять CGI-скрипты.

Еще нам понадобится возможность создавать другие каталоги cgi, чтобы объединять наши скрипты вместе по типам. Желательно группировать вместе PHP-скрипты, так как можно, а иногда даже нужно, иметь копию интерпретатора PHP, запущенную внутри CGI-каталогов. Обычно считается, что это – дыра в безопасности, но с ней можно управиться способом, которым мы потом займемся отдельно.

UID и GID созданного в системе Linux/Unix пользователя httpd, отображаемые в списке Linux-пользователей, должны совпадать с UID и GID пользователя и группы Apache.

Директивы http.conf, присутствующие в главном разделе конфигурации Apache, таковы:

User httpd
Group httpd

Позаботимся, чтобы Apache мог выполнять CGI-скрипты. Это гарантируется модулем mod_cgi (он доступен как часть базового дистрибутива Apache), но существуют некоторые директивы, которые следует добавить к httpd.conf еще до работы модуля. Нам нужно сообщить httpd.conf, что CGI-скрипты могут запускаться, и откуда они могут запускаться. PHP должен запускаться из какого-либо каталога cgi-bin, независимо от того, один или несколько пользователей работают с CGI-скриптами. Мы должны начать с директивы, запрещающей всему, что располагается за пределами дерева web-сервера, запускаться владельцем или любым пользователем Apache. Потом мы сможем ослабить наши правила, но для начала лучше проявить максимум консерватизма.

Следующие директивы тривиальны, но совершенно необходимы для безопасных операций Apache:

<Directory />
Order deny,allow
deny from all
</Directory>

Это – стандартная директива, она гарантирует, что Apache не сможет обслуживать файлы по всей файловой системе, даже дерево web- сервера. Конечно, теперь придется потрудиться, чтобы Apache СМОГ обслуживать нужные файлы, так что добавим другую директиву, сообщающую, что Apache может обслуживать файлы данных и контента из DocumentRoot:

DocumentRoot /usr/local/apache2/htdocs
<Directory /usr/local/apache2/htdocs>
Order allow,deny
Allow from all
</Directory>

Для правильной работы PHP-скрипты обязаны восприниматься как CGI-скрипты.

Чтобы никто не вздумал разрешить выполнение CGI с использованием файлов PHP не из каталога /usr/local/apache2/cgi-bin (или любого другого, явно прописанного вами в директивах Directory), добавим

Options -Includes -ExecCGI

в главный конфигурационный раздел httpd.conf.

Нужно также назначить каталог, содержащий CGI-скрипты; это делается директивой SetHandler. Тот факт, что мы разрешаем исполнение CGI через директиву Options, на вид противоречит предыдущей директиве, но вы ведь помните, что нельзя выполнять только CGI-скрипты, не содержащиеся в указанном ниже каталоге.

<Directory /usr/local/apache2/cgi-bin>
Options +ExecCGI
SetHandler cgi-script
</Directory>

Сперва, однако, не мешает предупредить Apache, с какими файлами следует обращаться как со сценариями PHP. Итак, в главном файле конфигурации говорим Apache, что мы используем скрипты CGI – это мы уже сделали, осталось сообщить, что мы используем файлы PHP:

AddHandler application/x-httpd-php .php

Вы можете добавить другие обработчики-handler’ы и, если захотите, новые расширения файлов, например, php3 или php5, но это на ваше усмотрение. Здесь приведен фрагмент httpd.conf: понадобится еще несколько директив, чтобы наш httpd.conf заработал, но советую добавить предыдущие директивы, в соответствии с нашими рекомендациями. Я добавил директиву ServerRoot, чтобы httpd.conf стал понятнее, и вот наши изменения:

User httpd
Group httpd
ServerRoot /usr/local/apache2
DocumentRoot /usr/local/apache2/htdocs
Options -Includes -ExecCGI
AddHandler application/x-httpd-php .php
<Directory />
Order deny,allow
deny from all
</Directory>
<Directory /usr/local/apache2/htdocs>
Order allow,deny
Allow from all
</Directory>
<Directory /usr/local/apache2/cgi-bin>
Options +ExecCGI
SetHandler cgi-script
</Directory>

Теперь нам понятна основная структура прав доступа и те изменения, что мы сделали в httpd.conf для стандартного укрепления Apache. Нужно также укрепить нашу установку PHP – неважно, выполняются ли приложения PHP как CGI-скрипты или внутри модуля Apache.

Еще о настройке Apache

Сначала поразмыслим о том, чего именно мы хотим – допустим, гибкости: например, возможности запуска PHP и как скрипта CGI, и как модуля. Если мы добавим еще одну директиву конфигурации, например

Action application/x-httpd-php /cgi-bin/php

к уже существующей директиве

AddHandler application/x-httpd-php .php

то сможем работать с любой установкой PHP, независимо от того, где располагается PHP-интерпретатор и запускается ли приложение PHP как скрипт CGI или в рамках модуля. Директива Action включается посредством модулей mod_actions, доступных по умолчанию в базовом дистрибутиве Apache. Это гарантирует, что все PHP-скрипты могут запускаться как скрипты CGI, так как PHP определяется как конкретный тип MIME, который должен запускаться как скрипт CGI. В итоге Apache сможет обрабатывать все PHP-скрипты.

Настройка и компиляция PHP

Нам следует запустить настроечный скрипт со специальной опцией перенаправления, чтобы иметь возможность поместить PHP-интерпретатор в любой указанный нами каталог. Обратите внимание, что PHP-интерпретатор может размещается в потенциально опасном месте, а именно, в любом общедоступном каталоге cgi-bin.

$ sudo ./configure \
> --enable-force-cgi-redirect \
> --prefix=/usr/local/apache2/php
$ make
$ sudo make install

Взгляните на опцию enable-force-cgi-redirect: вы только что разрешили копирование интерпретатора PHP в любой каталог cgi-bin. Опция настройки сделает невозможной любую попытку получить доступ к интерпретатору PHP через URL. В любом случае, исполняемые файлы PHP CGI не обрабатывают аргументов командной строки.

Другой путь: подготовить php.ini для скриптов PHP CGI

Мы также должны добавить директивы на уровень выше сервера Apache, в файл php.ini (файл настройки PHP). Удобной возможностью является ввод так называемой директивы doc_root.

doc_root хорошо работает, если при компиляции не используется опция конфигурации enable-force-cgi-redirect. PHP-интерпретатор в этом случае будет искать файлы PHP, начиная с каталога, упомянутого в директиве doc_root:

doc_root /var/www

прикажет PHP-интерпретатору искать скрипты PHP CGI и файлы данных, начиная с этого каталога. Бывает, что некоторые, а то и все, CGI-скрипты по соображениям безопасности расположены не в обычных каталогах cgi-bin. Тогда можно создать отдельное дерево каталогов и внести этот путь в doc_root. Помните, что это не будет работать, если PHP выполняется как модуль Apache.

Нам также следует сообщить PHP-интерпретатору, к каким файлам данных он получает доступ, добавив

open_basedir /var/www

или любой другой подобный каталог. Это следует поменять, если вы используете разные пути каталогов или, естественно, многопользовательский режим.

Запуск PHP как модуля

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

В то же время, разделение привилегий, хоть и не столь легко достижимое, все же проще с CGI-скриптами, основанными на suEXEC. Другими словами, использовать PHP как модуль в ситуации массового хостинга следует крайне осторожно. Но мы попробуем получить представление о том, как выглядит этот сценарий и что можно сделать, чтобы его обезопасить.

Убедитесь, что интерфейс между apache и его модулями доступен: он известен как apxs2. Первым делом нужно сменить каталог на то место, где располагается исходный код PHP, и выполнить настроечный скрипт вроде следующего:

$ sudo /.configure --with-apxs2=/usr/local/apache2/bin/apxs \
> --prefix=/user/local/apache2/php

Если модули еще не активированы, можете сделать это вручную: добавьте следующую строку в файл httpd.conf, позаботившись, чтобы расширения файлов, которые вы собираетесь использовать в вашей установке PHP, распознавались Apache:

LoadModule php5_module libexec/libphp5.so

Возможно, вам придется добавить директиву DirectoryIndex, чтобы отображался индексный файл.

DirectoryIndex index.html index.php

suEXEC

Наконец, классическим решением проблемы запуска CGI-скриптов под UID’ами, отличными от UID пользователя Apache, является suEXEC. И наоборот, если Apache установлен вместе с suEXEC, он не станет выполнять скрипты CGI под root’ом и/или администратором.

При правильной настройке suEXEC почти так же эффективен, как chroot, хотя chroot считается только второй линией защиты: suEXEC будет выполнять только те скрипты, правом запуска которых обладает только их владелец. Если, например, права на запись распространяются на группу или на весь мир, скрипт не выполнится; точнее, он не выполнится, если хоть какое-нибудь право доступа распространяется на весь мир. В основном будут запускаться скрипты с правами доступа 750.

Сам suEXEC может быть запущен только Apache. Группа, к которой принадлежит Apache – в нашем случае это httpd – имеет только одного члена, самого себя. Всему миру нельзя будет запускать ничего, если suEXEC включен. Apache следует переключаться между собой и другим пользователем, чтобы запускать CGI-скрипты от его имени. Чтобы достичь этого, нужно либо запустить Apache под root’ом (очень плохая идея), либо в оболочке SUID – тогда Apache сможет запускать процесс под другим ID для каждого динамического CGI-запроса. Таким образом, для каждого динамического запроса Apache выполняет два процесса: двоичный файл SUID и собственно сам скрипт.

Настройка suEXEC

Одной из причин, почему suEXEC, будучи довольно продуманным приложением, не сильно популярен, является довольно сложный процесс настройки: его необходимо сконфигурировать и скомпилировать вручную, так как suEXEC не есть часть дистрибутива Apache. Однако он полностью поддерживается Apache Foundation и чрезвычайно хорошо интегрируется с web-сервером. Некоторые дистрибутивы Linux или пакеты LAMP могут содержать уже настроенный suEXEC, однако это лучше проверить лично.

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

$ ./configure --enable-suexec \
> --suexec-bin /usr/local/apache2/bin/suexec

Предыдущую опцию следует проверить: она должна быть работоспособной и жестко закодированной для вашей системы. Если вы случайно ее пропустите, Apache даже не сможет найти suEXEC. Переменные PATH работать не будут. (Документация по Apache рекомендует /usr/sbin/suexec). Проверьте ваше систему на предмет размещения Apache по умолчанию и ваших требований.

> --with-suexec-caller=httpd

Теперь вы понимаете, почему мы так настаивали, чтобы Apache запускался под пользователем httpd. При использовании suEXEC не обязательно позволять Apache отступать к пользователю root или пытаться запускать Apache и PHP в системе, требующей каждому запросу выполняться от имени отдельного пользователя. Это возможно, но в таком случае вам следует перекомпилировать Apache с другой потоковой моделью.

 > -- with-suexec-userdir=public_html

Если вы запускаете PHP-скрипты, на которые имеет права только определенный пользователь, отличный от httpd, вам следует указать здесь родительский каталог для всех пользовательских директорий; то есть независимо от того, кто пишет скрипты – Петя, Вася или Коля, PHP-скрипты должны быть доступны из каталога public_html, например, каталога html/cgi-bin. Несмотря на мое обещание не говорить о виртуальных хостах, пожалуйста, убедитесь, что все они совместно используют корневой каталог, упомянутый здесь!

 > --with-suexec-docroot=/home

задает каталоги, начиная с которых suEXEC будет проводить поиск файлов и скриптов каждого пользователя.

Если виртуальные хосты и файлы .htaccess не используются, это должно соответствовать конфигурации, представленной файлом httpd.conf. Мы можем оставить установки в .config по умолчанию, но стоит проверить документацию Apache, чтобы понять, какие опции не совсем отвечают условиям вашей системы: suEXEC имеет много правил, но мало исключений.

Доступ suEXEC к PHP

Нам также нужно убедиться, что suEXEC позволяет обычному пользователю иметь доступ к PHP. Как уже говорилось, каждый пользователь должен иметь копию PHP-интерпретатора в своей каталоге cgi-bin.

Теперь следует подключить модуль Apache mod_rewrite. Сделав это, мы можем добавить следующие правила в httpd.conf:

 RewriteCond %{REQUEST_URI} .\php

Они гарантируют, что любое преобразование URI затрагивает только PHP-файлы. Вам нужно убедиться, что все запросы, которые должен обрабатывать PHP-интерпретатор, действительно доходят до него; в то же время mod_userdir должен увидеть запрос и послать его в нужное место: интерпретатор php, расположенный в каталоге cgi-bin.

 RewriteRule ^/~(w+)/(.*)$ /~$1/cgi-bin/php/$1/$2 [NS, L, PT,
E=Redirect_STATUS:302

Флаги Rewrite гарантируют, что

  • (NS): внутренние подзапросы игнорируются;
  • (L): это последний запрос – после него не допускается запросов на перезапись;
  • (PT): сейчас mod_rewrite остановит обработку, а mod_userdir примется за дело, и, наконец, переменной REDIRECT_STATUS будет присвоено значение 302, чтобы PHP мог обработать скрипт.

Этот последний штрих в заклинании завершит нашу попытку создать безопасный PHP-сервер. Существуют другие модули для содержания PHP-скриптов в безопасности, а их доступа к файлам – в четких границах. suPHP, которому требуется suEXEC, запускает только PHP-скрипты, безопасно и под различными UID’ами. LXF

Настройка безопасности

Поскольку мы будем иметь дело с настройками безопасности вашего web-сервера, само собой разумеется, что не рекомендуется трогать копию Apache, поставляемой с вашей системой. Проверьте, запущена ли копия Apache:

$ ps -aux | grep httpd

Обнаружив, что Apache невозмутимо накручивает обороты, на время остановите web-сервер. Если у вас Debian или Ubuntu или любая другая система на базе Debian, сработает следующее:

$ sudo /etc/init.d/Apache2 stop

Чтобы обеспечить эксперименты исключительно с копией Apache и PHP собственной сборки, установите Apache 2.2.6 из исходных текстов:

$ lynx http://httpd.Apache.org/download. Cgi
$ gzip -d httpd-2.2.6.tar.gz
$ tar xvf httpd-2.2.6.tar
$ cd httpd-2.2.6 $ ./configure
$ make
$ make install

Ваша копия Apache будет установлена в /usr/local/apache2; если вы желаете установить его в другое место, видоизмените команду configure таким образом:

./configure -prefix=/path/to/Apache

Больше защиты

К проведенному нами исследованию безопасности нужно добавить новый уровень защиты: создать отдельный каталог cgi-bin, не являющийся частью установки Apache. Затем мы можем поместить туда PHP-интерпретатор и добавить следующие строки

<Directory /var/www/cgi-php>
Order allow,deny
Deny from all
</Directory>

в файл httpd.conf. Причина, почему это работает, в том, что мы добавили внутреннюю переадресацию посредством директивы Action:

Action application/x-httpd-php /cgi-bin/php

Добавляя максимально строгий контроль внешнего доступа и озаботившись правами доступа к файлам, мы не прекратим работу PHP-интерпретатора, так как контроль доступа по сети работает только для внешней переадресации, а не для внутренней: он закроет любой прямой доступ к PHP- интерпретатору или каталогу cgi-bin из нелокального источника, но при вызовах правильным пользователем PHP-скрипты будут работать идеально.

Запуск suEXEC и PHP

Надо добавить к Apache модуль mod_userdir посредством apxs, и добавить директивы UserDir в файл httpd.conf, например:

UserDir public_html
UserDir disabled root

suEXEC находит файлы по запросу пользователя, в данному случае – каноническому ~user, и приставляет к имени пользователя docroot. suEXEC может добавлять к имени пользователя общий подкаталог, что приводит к

/docroot/~username/publicfolder

Чтобы было совсем здорово, ему также нужно найти cgi-bin, что мы ранее уже разрешали, но только для пользователя Apache, а именно, httpd.

Так как же средний держатель учетной записи на хостинге Apache сможет получить выделенный cgi-bin?

Очень просто:

<Directory /home/*/public_html/cgi-bin/>
Options +ExecCGI
SetHandler cgi_script
</Directory>
Личные инструменты
  • Купить электронную версию
  • Подписаться на бумажную версию