.htaccess
Многие PHP-приложения поставляются с файлом .htaccess: WordPress, Laravel, готовые перенаправления на HTTPS и www, правила доступа, папки под паролем. WardPane сам читает и применяет эти файлы, поэтому привычные файлы работают как есть, без Apache на сервере.
Как включить
Откройте сайт, перейдите на вкладку Дополнительно и включите .htaccess в разделе «Совместимость с Apache». По умолчанию выключено.
Обработка .htaccess входит в обработчик PHP. Статический сайт, у которого она была включена до версии 2.0, её сохраняет, новые статические сайты файлы .htaccess не обрабатывают, а приложение Node.js само обрабатывает свои маршруты.
Когда обработка включена, файлы .htaccess в корне документов и во всех подпапках применяются так же, как в Apache: подпапка со своим .htaccess не наследует RewriteRule родительской папки, если в ней не указано RewriteOptions Inherit.
Поддерживаемые директивы
| Область | Директивы |
|---|---|
| Перезапись URL | RewriteEngine, RewriteBase, RewriteCond, RewriteRule, RewriteOptions Inherit. Флаги L, END, R=code (от 300 до 599; 4xx и 5xx отвечают сразу, без перенаправления), F, G, NC, QSA, QSD, NE, C, S, B, T=type (только обычный тип содержимого, не PHP и не CGI). Проверки -f, -d, -s, -l, =, <, >, -eq и подобные, -ipmatch. Переменные %{HTTP_HOST}, %{HTTPS}, %{REQUEST_URI}, %{REQUEST_FILENAME}, %{QUERY_STRING}, %{HTTP:Header} и другие. \x в подстановке означает буквальный x. |
| Перенаправления | Redirect, RedirectPermanent, RedirectTemp, RedirectMatch, Redirect gone. Коды от 300 до 599; вне 3xx код сам служит ответом, без адреса перехода (например, RedirectMatch 403 ... из чёрного списка 5G). |
| Доступ | Require all/ip/not ip/local/method/valid-user/user, <RequireAll>, <RequireAny>, <RequireNone>, Order, Allow from, Deny from, Satisfy |
| Пароли | AuthType Basic, AuthName, AuthUserFile (bcrypt из htpasswd -B, MD5 по умолчанию, {SHA}) |
| Ответы | ErrorDocument, Header set/append/add/merge/unset/setifempty [always], ExpiresActive, ExpiresDefault, ExpiresByType, AddType, AddDefaultCharset, DirectoryIndex |
| Блоки | <Files>, <FilesMatch>, <Limit>, <LimitExcept>, <IfModule>, <IfVersion> |
Детали, которые ведут себя как в Apache
<IfModule mod_php...>и<IfModule mod_deflate...>считаются отсутствующими модулями: их содержимое пропускается, как на сервере без них.%{REQUEST_FILENAME}означает путь до первого существующего файла, остальное становитсяPATH_INFO. Для/index.php/2024/helloэтоindex.php, поэтому!-fложно и WordPress не переписывает адрес.Expires...не трогает ответы с ошибками и ответы, в которых уже есть заголовокExpires(заданный скриптом). Егоmax-ageзаменяет стандартныйCache-Controlпанели для статических файлов (public, max-age=0) и добавляется к заголовку, заданному скриптом или черезHeader(у Laravelno-cache, privateостаётся).\в аргументе означает пробел внутри слова, как вRewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php\ HTTP/.Options +Indexesвыдаёт предупреждение: списков содержимого папок нет, и папка без индексной страницы отвечает 404.
Что отклоняется
Отклоняются директивы, которые изменили бы, что именно выполняется, или ослабили бы песочницу PHP:
php_value,php_flagAddHandler,SetHandler,ForceType,ActionAddTypeдля PHP или CGI
Файлы .htaccess в папках, куда PHP может писать, игнорируются: иначе взломанный скрипт мог бы переписать правила, которые защищают сайт.
Поведение в плане безопасности
Файлы паролей
Файл паролей, указанный в AuthUserFile, должен находиться внутри папки сайта. Относительный путь отсчитывается от папки сайта. Внутри корня документов принимается только файл, имя которого начинается с точки (.htpasswd), так как любой другой можно было бы скачать; папка, которая ссылается на такой файл, не открывается (500).
Проверка паролей защищена от подбора:
- после 10 неверных паролей за 10 минут адрес (для IPv6 его
/64) не проверяется 15 минут; - после 300 неверных паролей со всех адресов сайт на 5 минут переходит в режим атаки: пароли проверяются только для адресов без недавних неудач, поэтому ботнет получает одну попытку на адрес, а человек с правильным паролем всё равно входит;
- успешные проверки кэшируются, потому что браузер отправляет пароль с каждым запросом, а bcrypt намеренно медленный; одинаковые проверки, идущие одновременно, выполняются один раз;
- bcrypt работает в отдельных потоках и не замедляет другие сайты: не больше 4 проверок одновременно на сайт и 32 в очереди, с ожиданием до 10 секунд, после чего ответ 503 (а не повторный запрос пароля);
- стоимость bcrypt может быть не больше 12 (
htpasswd -B -C 12); более дорогие хэши отклоняются; - неудачная проверка никогда не отвечает быстрее 300 мс, поэтому по времени ответа нельзя отличить несуществующего пользователя от неверного пароля.
Пути и блоки файлов
- Адреса с сегментами
.или..(в том числе в виде%2e) получают 400. Иначе/./admin/мог бы проскочить мимо.htaccessпапкиadmin. <Files>и<FilesMatch>сравниваются с файлом, который действительно будет отдан: для/admin.php/xэтоadmin.php, каким бы ни был последний сегмент адреса.Headerне может менять заголовки, управляющие соединением:Content-Length,Transfer-Encoding,Connection,Upgrade,Alt-Svcи подобные.
Сломанные файлы закрывают доступ
.htaccess, который не удаётся прочитать или разобрать или который больше 256 КБ, закрывает свою папку: каждый запрос к этой папке получает 500, пока файл не исправят, а в журнал пишется ошибка. Apache поступает так же. Если бы папка оставалась открытой, правила доступа из этого файла молча перестали бы работать.
Ошибка в отдельной строке папку не закрывает. Такие ошибки обрабатываются так, чтобы промах никогда не открывал доступ:
- Ошибка в списке адресов в
Allow,DenyилиRequire ipне прощается, но влияет только на свою секцию и не приводит к 500. Нераспознанная запись вDeny fromозначает «запретить всем» в этой секции; вAllow fromилиRequire ipона означает «никому». Имя хоста вместо адреса никогда не совпадает (с предупреждением); мусор считается ошибкой в отчёте.localhostпонимается (127.0.0.1и::1), а/0означает все адреса. - Секция, условие которой нельзя проверить (
<Files>или<FilesMatch>с нечитаемым шаблоном,<If>), сохраняет только свои правила доступа, и те могут только ограничивать: побеждает более строгое из решений с ними и без них. - Нечитаемый
RewriteCondотключает и следующий за нимRewriteRule, который иначе сработал бы там, где не должен. Счёт[S=n]и цепочки[C]остаются верными. Исключение: правило, которое только запрещает ([F],[G],[R=4xx]). Оно остаётся, без своих условий, и запрещает всё, что совпадает с его шаблоном. ЕслиRewriteBaseне удаётся прочитать, правила с относительной целью не применяются.
Ограничения для регулярных выражений
Шаблоны из .htaccess не могут остановить сервер, а собственные ограничения панели никогда не кладут весь сайт: при срабатывании ограничения не проходит только этот один запрос.
Как выполняются шаблоны:
- Шаблон никогда не отклоняется из-за того, сколько он может стоить.
- Шаблоны, которые может обработать движок с линейным временем, выполняются прямо в запросе, если их оценочная стоимость укладывается в бюджет. Очень большие (скажем, список из 2000 имён ботов) уходят в отдельный рабочий поток.
- Повторяющийся класс символов вроде
([a-z0-9]{2000,})из файрволов 7G и 6G или\d{20,}проверяется в запросе линейным просмотром. - Всё остальное, что понимает JavaScript (lookahead, большие счётчики, обратные ссылки, вложенные повторы), выполняется в рабочем потоке.
- Отклоняется только синтаксис, который есть лишь в PCRE, и такая строка не применяется:
\K,\G,\Q...\E, притяжательныйa++, атомарные группы(?>...), рекурсия(?R), комментарии(?#...), отрицательные POSIX-классы[[:^alpha:]]и несколько других экранирований. Распространённые формы PCRE переводятся:\A,\z,\Z,\eи POSIX-классы вроде[[:alpha:]]работают.
Ограничения:
| Ограничение | Значение | Что происходит |
|---|---|---|
| Один шаблон в рабочем потоке | 25 мс | 500 только для этого запроса; шаблон продолжает использоваться |
| Все шаблоны одного запроса | 50 мс | 503 с Retry-After |
| Шаблонов на файл | 500 | |
Размер одного .htaccess |
256 КБ | Папка отвечает 500, пока файл не исправят |
| Ожидание свободного рабочего потока | 20 мс | 503 с Retry-After: 1 |
Строки, с которыми сравниваются шаблоны, тоже ограничены: адрес до 8190 символов, заголовки до 2 КБ, Cookie до 8 КБ, имена файлов до 255. Более длинные строки получают 414 (431 для заголовка), но только когда к ним действительно применяется шаблон. Строка, слишком длинная для большого шаблона (User-Agent размером 2 КБ против списка из 2000 ботов), сразу получает 431 или 414.
Медленная работа шаблонов засчитывается тому посетителю, который её вызвал. Посетителю (адресу, для IPv6 его /64, с отдельным счётом на каждую /48) засчитываются только тайм-ауты и медленные шаблоны с возвратами; обычные проверки бесплатны, поэтому люди за общим NAT не страдают. Тот, кто вызвал 5 тайм-аутов за 10 минут или исчерпал свой бюджет, получает 429 на этом сайте на 10 минут и передаётся защите сайта (при включённой защите от DDoS это означает бан). Белый список сайта и общие узлы Cloudflare никогда не наказываются.
Большой список ботов стоит каждому запросу несколько миллисекунд (2000 имён занимают около 4 мс для запроса от Chrome). Включите для такого сайта защиту от DDoS, чтобы не пускать потоки запросов.
Проверить файлы .htaccess
Строки, которых панель не понимает, она не пытается угадать: она их пропускает и сообщает о них. Нажмите Проверить файлы .htaccess на вкладке Дополнительно, чтобы увидеть все .htaccess сайта и неприменённые строки с причиной для каждой. Когда файл меняется, предупреждение о неприменённых строках также пишется в журнал панели.
Не поддерживается
<If>и<Else>(с предупреждением; из их содержимого сохраняются только правила доступа, и они работают против доступа при любом условии)- списки содержимого папок (
Options +Indexes) - переменные окружения:
SetEnv,SetEnvIf,E=,env= - сжатие (
mod_deflate) ErrorDocument, указывающий на PHP-скрипт- проверки по имени хоста (
Require hostи другие неизвестные формыRequireсчитаются против доступа; об именах вAllowиDenyсм. выше) - флаги
RewriteRuleP(прокси),NиH