LXF102:Доверительный PHP
(→Права доступа и последствия) |
(Викификация, оформление) |
||
Строка 156: | Строка 156: | ||
Чтобы никто не вздумал разрешить выполнение ''CGI'' с использованием файлов ''PHP'' не из каталога '''/usr/local/apache2/cgi-bin''' (или любого другого, явно прописанного вами в директивах '''Directory'''), добавим | Чтобы никто не вздумал разрешить выполнение ''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> |
Текущая версия на 07:39, 29 мая 2009
|
|
|
[править] Доверимся 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-bin – 755, как и сделано по умолчанию на многих установках 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>