Четыре ручки статического слоя и защита от обхода путей
После XS_FALLBACK управление принимает статический слой. Он безопасен по умолчанию и быстр по умолчанию — четырёх ручек уровня хоста хватает большинству статических сайтов, а защиту от обхода путей сторожат восемь тестовых векторов.
Обзор
10.1 Введение
Главная масса ресурсов веб-приложения обычно статична: фронтенд-сборки, картинки, шрифты. Писать код для статики — расточительно, внешний статический сервер ломает «развёртывание одним файлом». Ответ XServer — встроенный статический слой: необработанные скриптом GET/HEAD попадают сюда и ищут файлы в path хоста — не написав ни строчки кода, вы уже владеете безопасным статическим сервером.
10.2 Поведение статического слоя
- Методы: GET отдаёт содержимое, HEAD — только заголовки; остальные методы вне семантики статического слоя
- Пути: percent-decode, затем нормализация; запрос каталога перебирает массив index (по умолчанию index.html)
- Передача: SendFile кусками по 512 КБ; под TLS — 64-килобайтный насос открытого текста (с объединением AGAIN/повторных входов) — на противодавление больших файлов в func_test есть реальный кейс 4 МиБ в несколько раундов
- MIME: 18 встроенных сопоставлений типов
- Страницы ошибок: без кастомной страницы — встроенный минималистичный вариант
10.3 Четыре ручки (Custom уровня хоста)
"host_default": {
"name": "app",
"path": "wwwroot",
"static": {
"headers": { /* ≤8 полей; 8 зарезервированных заголовков запрещены */
"X-Frame-Options": "DENY",
"Cache-Control": "public, max-age=3600"
},
"index": ["index.html", "home.html"], /* перебор по порядку */
"error_pages": { "404": "404.html", "500": "5xx.html" },
"deny_dotfiles": true /* по умолчанию true: файлы с точкой отвергаются всегда */
}
}
| Ручка | Ограничение | Эффект |
|---|---|---|
static.headers | ≤8 полей; Transfer-Encoding / Connection и ещё 6 зарезервированных заголовков запрещены (fail-fast) | Прикрепляются к каждому статическому ответу. Предрендерятся при сборке в массив полей — на пути запроса только memcpy, ноль строковой обработки |
static.index | массив строк | Кандидаты индекса каталога, перебор по порядку |
static.error_pages | ключи 400–599; значения относительно path | Для соответствующего кода — своя страница ошибки (работает при существовании файла) |
static.deny_dotfiles | bool · true | Файлы с точкой отвергаются всегда — даже на обычном пути |
10.4 Восемь векторов обхода
Красная линия статического слоя: URL никогда не выходит за path. func_test сторожит эту линию восемью векторами — все должны давать 403/404, а не содержимое файла:
Ключи реализации: percent-decode выполняется до нормализации пути, поэтому закодированного обхода не существует; deny_dotfiles — дополнительный слой (попытки обхода с файлом с точкой попадают дважды); символические ссылки из корня отвергаются якорем корня.
10.5 Контракт
- Граница: статический слой обслуживает только GET/HEAD, только внутри path хоста, файлы с точкой по умолчанию отвергаются.
- Производительность: headers предрендерятся при сборке — ноль строковой обработки на пути запроса, ключевое решение, держащее высокую конкурентность.
- Откат: если у кастомной страницы ошибки нет файла — встроенная страница, но не 500.
Частые ошибки
- Симптом
- Старт падает: static headers содержит зарезервированный заголовок.
- Причина
- Content-Length / Transfer-Encoding / Connection и ещё пять управляются слоем протокола; перекрытие из конфигурации сломало бы корректность сообщений — потому fail-fast, а не рантайм-переопределение.
- Симптом
- Старт падает: ключ error_pages вне диапазона.
- Причина
- Ключ — это код состояния (числовая строка 400–599), значение — путь страницы. Перепутанные местами или не-коды — fail-fast.
Упражнения
Дайте демо 404.html, зарегистрируйте в error_pages и проверьте на несуществующем пути.
Положите secret.txt вне wwwroot и попробуйте прочитать каждым из восьми векторов 10.4.
Настройте для статического сайта набор защитных заголовков (CSP, X-Content-Type-Options, Referrer-Policy…) и проверьте нулевую стоимость предрендеренного пути (разница нагрузочных тестов с конфигурацией без headers должна быть ничтожной).
Итоги
| Тезис | Вывод |
|---|---|
| Триггер | GET/HEAD с XS_FALLBACK попадают в статический слой |
| Четыре ручки | headers (предрендер) / index / error_pages / deny_dotfiles |
| Защита | decode до нормализации; восемь сторожевых векторов; якорь для symlink |
| Передача | SendFile кусками 512 КБ; 64-КБ насос TLS; 18 типов MIME |
- справочник xrt · file — низкоуровневые SendFile и асинхронный IO
- справочник xrt · path — основа нормализации путей и якоря корня