X-Frame-Options SAMEORIGIN + X-Content-Type-Options nosniff + Referrer-Policy на все web-ответы (go-live аудит 17.06). CSP вынесен отдельно (SPA Vue+Vuetify). TDD-тест на публичном /. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.6 KiB
Спека — middleware безопасных HTTP-заголовков (закрытие ZAP Medium anti-clickjacking)
Цель
Добавить Laravel-middleware, проставляющий безопасные HTTP-заголовки на все
web-ответы портала, чтобы закрыть Medium-находку go-live аудита 17.06.2026
«Missing Anti-clickjacking Header» (плюс базовые X-Content-Type-Options,
Referrer-Policy). Реализация по TDD; CSP в этот объём НЕ входит (отдельная
выверенная задача под Vue+Vuetify SPA).
H1 — какие заголовки ставит middleware
Контракт. Новый класс App\Http\Middleware\SecurityHeaders в handle() после
$next($request) проставляет на ответе:
X-Frame-Options: SAMEORIGIN(анти-clickjacking);X-Content-Type-Options: nosniff;Referrer-Policy: strict-origin-when-cross-origin.
Edge-case. Заголовки ставятся без условий на все ответы web-группы; уже
существующие значения перезаписываются предсказуемо (headers->set).
Конвенция. Стиль файла — как у прочих middleware: declare(strict_types=1),
namespace App\Http\Middleware, сигнатура handle(Request, Closure): Response
(Symfony Response).
Критерий. Класс существует, в handle() выставлены ровно три заголовка.
H2 — регистрация
Контракт. Middleware регистрируется глобально на web-группу в
bootstrap/app.php внутри ->withMiddleware(...) через
$middleware->web(append: [SecurityHeaders::class]) (импорт класса сверху, как
SetTenantContext/EnsureSaasAdmin). Все маршруты портала — в web-группе, значит
покрытие полное.
Критерий. В bootstrap/app.php есть append SecurityHeaders в web-группу.
H3 — тест (TDD)
Контракт. Pest-feature-тест tests/Feature/SecurityHeadersTest.php бьёт по
публичному маршруту / (welcome SPA, без auth/БД) и проверяет наличие и значения
трёх заголовков. Сначала RED (middleware ещё нет), после реализации — GREEN.
Edge-case. Маршрут / отдаёт 200 (Route::view welcome), БД не требуется —
тест лёгкий и не зависит от тестовой базы.
Критерий. Тест есть; до реализации падает, после — проходит.
H4 — CSP вне объёма
Контракт. Content-Security-Policy в этом middleware НЕ ставится: корректная
политика под Vue 3 + Vuetify 3 SPA (script-src/style-src/inline) требует выверки и
прогона по экранам, иначе ломает интерфейс. Фиксируется как отдельная задача.
Критерий. В коде middleware CSP отсутствует; в комментарии класса — пометка, что CSP вынесен отдельно.
[
{"id":"vc1","kind":"EXTRACTED","ref":"app/bootstrap/app.php","anchor":"withMiddleware(function"},
{"id":"vc2","kind":"EXTRACTED","ref":"app/app/Http/Middleware/SetTenantContext.php","anchor":"Устанавливает app.current_tenant_id"}
]