PHP в деталях

       

По следам виртуальных директорий, или как можно настроить VirtualHost



11.12.2001

От автора. Если завтра мне понадобится настроить Apache для работы с очередным виртуальным сервером, я буду использовать именно эту "рыбу".

Здесь просуммирован опыт нескольких специалистов с которыми в свое время мне приходилсь общаться, за что огромное спасибо!

Конечно жизнь не стоит на месте? будут появляться новые проблемы, их решения, будут меняться взгляды, и обстоятельства, но надеюсь мои развернутые комментарии помогут Вам понять суть и обоснование всех пунктов, а значит Вы сможете и дальше уже самостоятельно двигаться в нужном направлении...

"На берегу" хотелось бы отметить один момент ? автор давно уже придерживается идеи о том, что обращения ко всем документам (исключая статические), в пределах одного сайта, должны сводиться в одну точку (н.п. /index.html). Исключения, конечно, бывают, но они, как обычно, лишь подтверждают правило.

Сделать это можно несколькими путями:

 - созданием нужной файловой структуры, где все файлы делают только лишь

include(файл_движок);

 - созданием нужной файловой структуры, с помощью линков* на файл_движок (кстати, это самый привычный и высокопроизводительный вариант, хотя и имеющий организационные ограничения в плане поддержания достаточно динамической структуры, например, дерево каталога большой торговой фирмы);

 - поддержание виртуальной файловой структуры с помощью Web-сервера (здесь тоже возможны варианты, и один из них, для Apache, самый высокопроизводительный, эффективный и гибкий, описан ниже);

* ? "жесткие" линки, работают не только в Unix/Linux-системах, но и в Windows NT, Windows 2000, Windows XP (используйте Far с Alt+F6 или специальные утилиты).

<VirtualHost *>

# обратите внимание - для сайта отведена домашняя директория



# при этом всё, что видно через браузер пользователю находится в поддиректории /html/

DocumentRoot "/www/имя_сайта/html" ServerAdmin webmaster@имя_сайта.ru # хороший тон, когда сайт откликается и на полное и на укороченное имя


ServerName www.имя_сайта.ru ServerAlias имя_сайта.ru

# вот еще использование домашней директории - поддиректория для лог-файлов

# удобно, что они всегда под рукой разработчика (некоторые провайдеры

# ВООБЩЕ не предоставляют доступ к логам! :( - это очень неудобно),

# и в тоже время, они недоступны пользователю, т.к. находятся вне DocumentRoot

ErrorLog "/www/имя_сайта/logs/error_log" CustomLog "/www/имя_сайта/logs/access_log" common # Кстати, "common" конечно не единственный формат логов, более того -

# есть возможность самому задать его структуру

# (см. http://httpd.apache.org/docs/mod/mod_log_config.html).

# пользоваль комфортнее себя чувствует видя привычное расширение .html

# в пользу расширения .html и другие аргументы:

# - при сохранение в браузере страницы, пользователь получит файл с

# таким расширением, а потом легко его откроет по Enter`у;

# - если Вы пойдете на поводу у провайдера и воспользуетесь расширением php3, к примеру,

# то не факт, что у другого будет такое же (.phtml - как вариант).

# ну и что - мне то какая проблема, скажете Вы,

# изменить расширения у своих файлов... в итоге после запуска проекта на новом месте

# Вы с удивлением обнаружите падение посещаемости и,

# стремительно увеличивающийся /www/имя_сайта/logs/error_log

# а дело в том, что поисковые системы уже давно проиндексировали Ваш сайт,

# и ещё долго будут водить пользователей на сайт-призрак - они же не знают, что всего то и нужно,

# это изменить расширение скриптовых файлов!

# - в конце концов, это просто больше соответствует истине -

# ведь в итоге получается действительно HTML-файл.

<IfModule mod_mime.c>

AddType application/x-httpd-php .html </IfModule>

# Кстати, если нагрузка на сайт предполагается большая,

# а часть страниц можно сохранить в "компилированном" виде, т.е. в чистом HTML-е,

# то можно сэкономить немного процессорного времени "отсадив" такие страницы

# в отдельную директорию, и отключить там PHP, например так:



#<Directory /www/имя_сайта/html/article/>

# <IfModule mod_mime.c>

# RemoveType .html

# </IfModule>

#</Directory>

# или использовать для "чистых" HTML-документов другое расширение, н.п. .htm,

# но тут могут вылезти проблемы, описанные выше - вдруг эти документы

# нужно будет сделать динамическими, позже, по каким либо причинам,

# и что - менять расширение?

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

# в пределах данного сайта, и сюда удобно вынести присвоение переменных и define,

# а так же include связанные с загрузкой библиотек т.д. и т.п.,

# которые привязывают к физическому расположению, чего-либо.

# этот документ также находится вне DocumentRoot,

# что повышает безопасность и логично отражает иерархию

# возможно Вы скажете, что это ненужный файл - ведь все обращения итак

# обрабатываются одним файлом (н.п. index.html), и именно в нем легко можно сделать

# привязку к физическому положению! можно - но я все-таки считаю, что правильнее,

# когда в единой точке входа сосредоточена логика сайта, а физика - отдельно.

# кроме того, возможны несколько точек входа - в случае с

# виртуальными сайтами или физическими разделами (см. ниже настройку mod_rewrite)

php_admin_value auto_prepend_file "/www/имя_сайта/.startup.html" # расширение .html предлагаю использовать для единообразия картины

# Кстати, это не "стопудовый" PHP - <script language="php"></script>

# или кому больше нравится <? ?>, всё таки нужно писать...

# Для подобных же вещей (т.е. для комфортной привязки проекта,

# к конфигурации сервера) может служить вот эта директива

#php_admin_value include_path "/www/имя_сайта/include"

# она перечисляет директории, которые будут отрабатываться

# по командам серии include(). Конечно на "домашнем" и рабочем

# сервере это будут разные каталоги (если вы конечно не на одной

# платформе и там и там работаете :)

# для ошибок, происходящих в пределах PHP логичнее всего



# тоже завести отдельный файл, в пределах нашей logs директории

php_admin_flag log_errors On php_admin_value error_log "/www/имя_сайта/logs/php_errors"

# после запуска сайта в плановую эксплуатацию имеет смысл

# выключить показ сообщений о ошибках, т.к. в некоторых случаях

# в сообщениях об ошибках есть информация о конфигурации и путях сервера,

# а это прямой удар по безопасности!

php_admin_flag display_errors Off

# данный флаг управляет автоматической регистрацией в PHP всех переменных

# переданных методами GET или POST

# т.е. фактически это глобальные переменные на ряду с переменными

# определёнными в теле скрипта - вот тут и кроется "дыра" в безопасности сайта,

# ведь если злоумышленник узнает какие переменные Вы используете как внутренние,

# то он сможет управлять Вашим сайтом, через передачу нужных значений в запросах!

# второй аргумент в пользу такого решения - стиль программирования,

# на который стоит обращать внимание при сложном по структуре

# (много связанных модулей, например) проекте - обращаясь к переменной в тексте программы

# программист (имеется в виду тот, который не писал другие модули) может только догадываться

# откуда берется эта переменная: от пользователя, или из программного кода

php_admin_flag register_globals Off # конечно в небольших проектах, которые пишутся на одном дыхании,

# автоматическая регистрация может только облегчить жизнь программисту,

# и многие считают это большим плюсом, тем не менее я данный флаг ставлю

# в Off всегда и получаю значения через свои функции

# хотя бы потому, что код малого проекта может быть использован в большом,

# а там _не_использование_ автоматической регистрации (глобальных переменных то бишь)

# считаю более чем оправданным, особенно при использовании ООП

<IfModule mod_rewrite.c>

RewriteEngine On

#RewriteLog "/www/имя_сайта/logs/rewrite_log"

#RewriteLogLevel 9

# произведем обработку запроса

# уберём подряд идущие слэши

RewriteRule (.*)/(/.*) $1$2 [N]



# если какой либо элемент, название директории или имя файла, начинается с точки [.]

# то это считается служебным ресурсом и доступ к нему с клиентской стороны НЕвозможен

# кстати "так думают" ещё ls НЕ показывая по умолчанию такие файлы и samba, которая

# добавляет к таким файлам атрибут hidden

RewriteRule ^(.*/)\.(.*) %{REQUEST_FILENAME} [G]

# физические файлы и директории (в случае наличия DefaultIndex)

# имеют абсолютный приоритет

# если то, что просят, это физический файл,

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -f # то отдаем как есть и останавливаемся

RewriteRule ^ %{REQUEST_FILENAME} [L]

# это не файл - пытаемся думать, что это директория

# если не было слеша в конце, то делаем редирект

# прилепляя слеш в конец запроса - т.о. пользователь увидит "красивый" адрес

# фактически это может произойти только один раз - если запрос не файл,

# и в конце нет слеша (то, что после ? и сам ? НЕ учитывается!)

# кстати - [R,L] можно убрать,

#RewriteRule [^\/]$ %{REQUEST_FILENAME}/

# и тогда итоговый URL для пользователя будет больше походить на путь к

# виртуальному файлу, чем к виртуальной директории :) и это можно красиво использовать!

RewriteRule [^\/]$ %{REQUEST_FILENAME}/ [R,L]

# если то, что просят, это физическая директория,

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d # и в ней есть DefaultIndex файл

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}index.html -f # то передаём на исполнение этот файл

RewriteRule ^ %{REQUEST_FILENAME}index.html [L]

# если то, что просят, это физическая директория,

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -d # НО в ней НЕТ DefaultIndex файл

RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME}index.html !-f # то передаём на исполнение сообщение об отказе в доступе

RewriteRule ^ %{REQUEST_FILENAME} [F]

# и, если запрос содержит более одной директории,

# то, зацикливаем анализ, откинув последнюю директорию

#RewriteRule (/.*/)(.*/)$ $1 [N]

# ОБРАТИТЕ ВНИМАНИЕ! Данная конструкция может добавить ненужную нагрузку на сервер,



# если Вы используете достаточно длинные (с большим количеством вложенных поддиректорий)

# URL`ы, ведь на каждый запрос она будет производить разложение на составляющие

# и анализ каждой из них, по вышеописанным правилам, поэтому,

# если вам нужны виртуальные сайты с виртуальными директориями в пределах сайта

# (что это такое - см. ниже :), то только тогда "раскоментарьте" это правило!

# а тут оседают все остальные обращения к сайту

RewriteRule ^ /index.html [L]

# Принцип работы:

# 1. Если то что просят файл - всё остаётся как есть.

# 2. Если то что просят директория, и там есть DefaultIndex файл, то управление на него.

# 3. Если дошли до этого шага? значит это и есть "виртуальная" директория -

# теперь нужно теперь найти обработчик для неё. Можно задать его жестко,

# н.п. как DefaultIndex-файл, находящийся в корне сайта,

# но можно добавить нашему ReWrite`у немного интеллекта -

# пусть он попытается подняться вверх по предполагаемому дереву каталогов,

# и там поискать, по правилу п/п 1 и 2, т.е. зациклим анализ;

# 4. Если подниматься больше некуда (или мы и находились в корне), то данную

# "виртуальную" директорию будет обрабатывать DefaultIndex-файл корня сайта.

#

# В итоге мы получим сайт с "виртуальными" директориями, но если часть структуры

# сайта построена физически, то это будет как бы сайт в сайте, т.е. если URL выглядит как

# /test/бла/бла/, и директория /test/ физическая и в ней есть DefaultIndex-файл,

# то именно он получит управление, а не корневой DefaultIndex-файл.

#

# Конечно для Ваших задач может понадобиться

# другое решение - ReWrite наверняка с ним справится! :)

</IfModule>

# Если уж тема безопасности затронута, то хотелось бы упомянуть ещё несколько моментов:

php_admin_flag safe_mode On # - safe_mode в PHP это тот режим, который практически полностью позволяет исключить

# возможность несанкционированного доступа пользователя к ресурсам доступ к которым

# (хотя бы даже и только на чтение) имеет PHP, запущенный как модуль Apache:



# 1. Запрещается изменять переменные окружения для запускаемых программ, через putenv();

# 2. Запрещается запускать программы через exec(),

# находящиеся вне указанной директории, см. safe_mode_exec_dir в php.ini;

# 3. Файловые операции корректируются:

php_admin_value open_basedir "/www/имя_сайта/" php_admin_value doc_root "/www/имя_сайта/" # ни одна из файловых функций не сможет открыть файл, если он не входит

# в указаное поддерево (их, кстати, можно указать несколько).

# Подробнее на http://www.php.net/manual/en/features.safe-mode.php

# - disable_functions данная директива позволяет отключить некоторые функции

# (конечно можно это сделать при компиляции, но "геморроя" тогда больше), которые могут

# слишком много и быстро рассказать о конфигурации сервера, например phpinfo()

# или слишком много позволить пользователю, н.п. dl()

# Кстати, данная конструкция (как и некоторые другие, safe_mode_exec_dir)

# должна устанавливаться только из php.ini, т.е. глобальна в пределах одного Apache

# - На тему загрузки модулей есть специальная директива

# php_admin_flag enable_dl Off

# хотя в safe_mode функция dl() и так не доступна

# Кстати, программисту создающему сайт на домашнем сервере имеет смысл установить

# всё настройки (в том числе и по вопросам безопасности) так, как будто этот сервер,

# доступен всем Сетевым Ветрам - просто для того, чтобы потом на реальном сервере

# не вылезла какая-либо несовместимость конфигурации

# Подробности по настройке PHP

# см. http://www.php.net/manual/en/configuration.php#ini.safe-mode

# И последнее, что хотелось бы упомянуть на этут тему: если на Вашем сервере

# размещается несколько десятков или сотен серверов, и многое в их конфигурации

# однотипно, то стоит подумать над использованием одного из механизмов, подробнее

# описанных тут http://httpd.apache.org/docs/vhosts/mass.html

# а конкретно http://httpd.apache.org/docs/mod/mod_vhost_alias.html

#VirtualDocumentRoot "/www/%0/html"

# данная директива используется как шаблон в одном <VirtualHost *></VirtualHost>

# для отработки нескольких серверов вместо DocumentRoot

# в нескольких <VirtualHost *></VirtualHost>

# где %0 полное имя сервера, т.о. для заведения одного из типичных серверов

# достаточно будет только в нужном месте создать директорию... и все!

# Есть только одна проблема - в директиве php_admin_value %0 останется неизменным :(

# Ну, что ждём от Apache Team создание virtual_php_admin_value или напишем сами?

</VirtualHost>

Яцевич Роман, неважно кто по жизни

отдельное спасибо Бохонковичу Юрию, за консультации по безопасности систем на базе Unix/Linux-серверов а также Лебедеву Дмитрию, за то, что он открыл мне глаза на отнюдь не виртуальную проблему виртуальных директорий!

Содержание раздела