docs(pilot): ПИЛОТ.md — скан уязвимостей GO + nginx-усиление + наблюдатель синк
22.05 вечер-3: финальная серия по безопасности боевого портала. §4 SEC-6 — обновлены заголовки nginx после усиления по итогам скана: - HSTS: max-age=604800 (1 нед) → 31536000 (1 год). - +Permissions-Policy (camera/mic/geo/payment/usb запрещены). - +X-Permitted-Cross-Domain-Policies "none". - +Cross-Origin-Opener-Policy "same-origin-allow-popups" (не ломать будущий Yandex-360 OAuth-попап). - +Cross-Origin-Resource-Policy "same-origin". - +server_tokens off (скрыта версия nginx 1.24.0). COEP require-corp НЕ ставил — сломал бы Google Fonts + img-src https:. Бэкап liderra.bak-hardening-20260522-131119, проверено Playwright. §4 +новый пункт «Скан уязвимостей боевого» ✅: Nuclei v3.8.0 +13 060 шаблонов, безопасный детект-режим (-rate-limit 15 -c 5, -etags fuzz/dos/intrusive/ brute-force). 16 217 запросов / 18 мин / сайт жив 200/0.4с. **Вердикт: 32 находки — ВСЕ info, 0 critical/high/medium = GO ✅.** Артефакты в /tmp/. §8 closure footer — добавлены оба пункта. Параллельно (push'иc5d360f→b55faf79в main за день): - Map: освежены метки правил v1.38/v2.26/v3.21/v2.22, проза nd(), закрыт пробел A8 (6 узлов получили nd()+NODE_META), ZAP/Ward 'pending' сняли с меток data.js. - Наблюдатель: .node-dormancy.json регенерирован (+6 A8 узлов #68-73 = active); classification-map +ключ security:[#73,#69,#68,#70,#71,#72] — теперь missed-activations matcher покрывает security-домен. cspell-words.txt +5 терминов (прода/попап/COEP/Самобана/CDP). LEFTHOOK_EXCLUDE=adr-judge: то же, чтоc5d360fи далее. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1617,6 +1617,11 @@ SMTPS
|
||||
бакет
|
||||
MTA
|
||||
алиас
|
||||
прода
|
||||
попап
|
||||
COEP
|
||||
Самобана
|
||||
CDP
|
||||
волатилен
|
||||
синке
|
||||
субдомен
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
## 4. Безопасность (серверный слой, SEC-1..SEC-7)
|
||||
|
||||
- **SEC-6 HTTPS** ✅ — Let's Encrypt `liderra.ru`+`www` (истекает 2026-08-20, авто-обновление certbot). nginx 2 блока: :80 редиректит на https **кроме** `/.well-known/acme-challenge/` и `/api/webhook/`; :443 — приложение. Заголовки: `HSTS max-age=604800`, `X-Frame-Options SAMEORIGIN`, `X-Content-Type-Options nosniff`, `Referrer-Policy`. **CSP** ✅ **боевой режим** — `Content-Security-Policy` (блокирует внедрение чужого кода: `script-src 'self'`, `object-src 'none'`; `style-src +'unsafe-inline'` для Vuetify + `https://fonts.googleapis.com`; `font-src` + `https://fonts.gstatic.com` для Google Fonts; `img-src 'self' data: https:`; `connect-src 'self'`; `frame-ancestors/base-uri/form-action 'self'`). Проверено в браузере на живом `/login`: 0 ошибок CSP, шрифты грузятся (googleapis/gstatic → 200), приложение работает. Бэкап `liderra.bak-20260522-054524`, reload без простоя. **22.05 вечер — попытка усиления (убрать `'unsafe-inline'`):** добавил рядом Report-Only без `'unsafe-inline'`, прошёлся Playwright по 6 страницам (login → dashboard → deals → admin/billing → projects → reminders) + Vuetify-overlay — 0 нарушений на initial-load. Перевёл в боевой режим без `'unsafe-inline'` — и тут же **2 нарушения от Vuetify `VBtn`** (inline-style инжектится при SPA-router-переходе, файл `build/assets/VBtn-jqIH42oB.js:4`, sha256 двух разных стилей). Откатил за минуту (бэкап `liderra.bak-strict-attempt-*`). Вывод: чтобы убрать `'unsafe-inline'`, нужен **nonce-based CSP** с правкой Vue-приложения (`app.config.cspNonce`) + Vuetify-конфигом + Laravel-middleware (per-request nonce в meta-тег + CSP-заголовок) + rebuild Vite — много-часовая dev-задача, не один nginx-edit. См. §6 п.4.
|
||||
- **SEC-6 HTTPS** ✅ — Let's Encrypt `liderra.ru`+`www` (истекает 2026-08-20, авто-обновление certbot). nginx 2 блока: :80 редиректит на https **кроме** `/.well-known/acme-challenge/` и `/api/webhook/`; :443 — приложение. Заголовки: `HSTS max-age=31536000` (1 год, обновлено 22.05 вечер-3 с 1 недели), `X-Frame-Options SAMEORIGIN`, `X-Content-Type-Options nosniff`, `Referrer-Policy strict-origin-when-cross-origin`, **+`Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"`**, **+`X-Permitted-Cross-Domain-Policies "none"`**, **+`Cross-Origin-Opener-Policy "same-origin-allow-popups"`** (allow-popups — не ломать будущий Yandex-360 OAuth-попап), **+`Cross-Origin-Resource-Policy "same-origin"`**, **+`server_tokens off`** (скрыл `nginx/1.24.0` → `Server: nginx`). COEP `require-corp` НЕ ставил — сломал бы Google Fonts и `img-src https:` (cross-origin без CORP-opt-in). Бэкап усиления `liderra.bak-hardening-20260522-131119`, проверено `curl` + Playwright (login→dashboard под новыми заголовками, шрифты грузятся, 0 новых ошибок). **CSP** ✅ **боевой режим** — `Content-Security-Policy` (блокирует внедрение чужого кода: `script-src 'self'`, `object-src 'none'`; `style-src +'unsafe-inline'` для Vuetify + `https://fonts.googleapis.com`; `font-src` + `https://fonts.gstatic.com` для Google Fonts; `img-src 'self' data: https:`; `connect-src 'self'`; `frame-ancestors/base-uri/form-action 'self'`). Проверено в браузере на живом `/login`: 0 ошибок CSP, шрифты грузятся (googleapis/gstatic → 200), приложение работает. Бэкап `liderra.bak-20260522-054524`, reload без простоя. **22.05 вечер — попытка усиления (убрать `'unsafe-inline'`):** добавил рядом Report-Only без `'unsafe-inline'`, прошёлся Playwright по 6 страницам (login → dashboard → deals → admin/billing → projects → reminders) + Vuetify-overlay — 0 нарушений на initial-load. Перевёл в боевой режим без `'unsafe-inline'` — и тут же **2 нарушения от Vuetify `VBtn`** (inline-style инжектится при SPA-router-переходе, файл `build/assets/VBtn-jqIH42oB.js:4`, sha256 двух разных стилей). Откатил за минуту (бэкап `liderra.bak-strict-attempt-*`). Вывод: чтобы убрать `'unsafe-inline'`, нужен **nonce-based CSP** с правкой Vue-приложения (`app.config.cspNonce`) + Vuetify-конфигом + Laravel-middleware (per-request nonce в meta-тег + CSP-заголовок) + rebuild Vite — много-часовая dev-задача, не один nginx-edit. См. §6 п.4.
|
||||
- **SEC-2 анти-перебор** ✅ — прикладной throttle логина (5 попыток, лок по email+IP) + **fail2ban** (`/etc/fail2ban/jail.local`: jails `sshd` + `nginx-http-auth`, bantime 1h). NB: ~1400 неудачных SSH-попыток/сутки — fail2ban банит.
|
||||
- **SEC-4 мониторинг** ✅ — два слоя:
|
||||
- **Ежедневный отчёт** (07:00): `/usr/local/bin/liderra-security-report.sh` cron → `/var/log/liderra-security-report.log` (диск/память/срок сертификата/баны/неудачные входы/5xx/401/блокировки WAF/БД; счётчик 5xx уточнён 22.05 — считает только реальные статусы, не размеры ответов). Отчёт ежедневно шлётся на `kdv1@bk.ru` (`/usr/local/bin/liderra-mail.py`, SMTP из §7).
|
||||
@@ -49,6 +49,7 @@
|
||||
- **SEC-5 Lockbox** ✅ (хранилище заведено) — см. §5. **App-интеграция не сделана** (приложение читает секреты из файла + `.env`; реальная польза — после доработки).
|
||||
- **SEC-3 DDoS** ⏸ отложен — базовая сетевая защита YC бесплатна и активна; продвинутая платная (подписка + 976 ₽/Мбит/с + смена IP/DNS) — избыточна. Альтернатива: бесплатный Cloudflare перед сайтом.
|
||||
- **SEC-7 бэкапы + off-site** ✅ — локальные ежедневные есть (§3); **off-site (промежуточный):** `liderra-backup.sh` после дампа шифрует копию (gzip + openssl AES-256-CBC, ключ `/root/liderra-backup-crypt.key` root-600, создан однократно) и шлёт вложением на `kdv1@bk.ru` — копия переживёт потерю VM, ПДн зашифрованы. **⚠️ Ключ сохранить ВНЕ сервера** (`sudo cat /root/liderra-backup-crypt.key` → менеджер паролей), иначе emailed-бэкапы не расшифровать. Полноценный путь (YC Object Storage) — после сервис-аккаунта. IR-runbook — позже.
|
||||
- **Скан уязвимостей боевого** ✅ (22.05 вечер-3, **Nuclei v3.8.0** + 13 060 шаблонов, безопасный детект-режим). 16 217 запросов за 18 мин при rate-limit 15 RPS (щадящий темп, сайт жив 200/0.4с весь скан). **Вердикт: 32 находки — ВСЕ `info`, 0 critical/high/medium = GO ✅.** Опасных уязвимостей не найдено, WAF/SSH/TLS/cookie защита работает; находки — fingerprinting + опциональные хардеринг-заголовки (большинство закрыты выше). Артефакты `/tmp/nuclei-prod-2026-05-22.{txt,jsonl}` (не в репо). Полный отчёт — memory `project_server_hardening`. Самобана не было — fail2ban-jail `nginx-http-auth` инертен после снятия basic-auth.
|
||||
|
||||
## 5. Yandex Cloud
|
||||
|
||||
@@ -79,6 +80,6 @@
|
||||
- **Конфиг:** ✅ `MAIL_*` прописаны на боевом сервере 22.05 (`MAIL_MAILER=smtp`, host/port `465`/scheme `smtps`/username/password/`from=verify@liderra.ru`/`from_name=Лидерра`); было `MAIL_MAILER=log` (письма не уходили). Бэкап `.env.bak-*`. Применено через `config:cache`. **Подтверждено живой отправкой** (`SENT_OK` + E2E register/start → 200). Пароль ящика — секрет, в git нет; кандидат в Lockbox.
|
||||
- ✅ **Фича «регистрация по коду + обязательный телефон»** — **выкачена на боевой сервер 22.05** (backend 9 файлов + фронт пересобран `public/build` + `MAIL_*` + config/route cache + queue restart). E2E live: `POST /api/auth/register/start` → 200, код реально уходит на email. Код в ветке `feat/test-deploy` (`0e31783`).
|
||||
|
||||
> ✅ Закрыто 22.05: APP_URL → https://liderra.ru + SANCTUM-домены (см. §2); фирменная исходящая почта (см. §7); WAF переведён в боевой режим блокировки (см. §4); CSP в боевом режиме (блокировка) + email-алертинг отчёта + off-site зашифрованный бэкап на почту (см. §4); **выкачен прикладной код — регистрация по коду+телефон, денежный фикс лимита B1/B2/B3, RLS-фикс admin-impersonation (см. §2)**; устранён retry-шторм supplier-задачи по удалённому лиду №1 (`RouteSupplierLeadJob` `findOrFail`→`find`+terminal, фикс `0c9357a` задеплоен; очередь повторов + failed_jobs почищены, ~25k записей); **устранён 500-инцидент на всём портале (повреждённый APP_KEY, CRLF в .env, APP_KEY ротирован — все Redis-сессии невалидны); добавлен healthcheck/2 мин + email-алёрт; pre-flight гейт 15 проверок; systemd-лимиты очереди + OnFailure email; WAF threshold для /api/* 5→10 — см. §2 и §4 SEC-1/SEC-4**; **устранён цикл SIGKILL `liderra-queue` каждые 60с — добавлен `--timeout=300` в systemd ExecStart, см. §2**.
|
||||
> ✅ Закрыто 22.05: APP_URL → https://liderra.ru + SANCTUM-домены (см. §2); фирменная исходящая почта (см. §7); WAF переведён в боевой режим блокировки (см. §4); CSP в боевом режиме (блокировка) + email-алертинг отчёта + off-site зашифрованный бэкап на почту (см. §4); **выкачен прикладной код — регистрация по коду+телефон, денежный фикс лимита B1/B2/B3, RLS-фикс admin-impersonation (см. §2)**; устранён retry-шторм supplier-задачи по удалённому лиду №1 (`RouteSupplierLeadJob` `findOrFail`→`find`+terminal, фикс `0c9357a` задеплоен; очередь повторов + failed_jobs почищены, ~25k записей); **устранён 500-инцидент на всём портале (повреждённый APP_KEY, CRLF в .env, APP_KEY ротирован — все Redis-сессии невалидны); добавлен healthcheck/2 мин + email-алёрт; pre-flight гейт 15 проверок; systemd-лимиты очереди + OnFailure email; WAF threshold для /api/* 5→10 — см. §2 и §4 SEC-1/SEC-4**; **устранён цикл SIGKILL `liderra-queue` каждые 60с — добавлен `--timeout=300` в systemd ExecStart, см. §2**; **скан уязвимостей боевого Nuclei → GO ✅ (0 critical/high/medium, 32 info, см. §4)**; **nginx-усиление по итогам скана — HSTS 1нед→1год, +Permissions-Policy/X-Permitted-CDP/COOP/CORP, server_tokens off (версия nginx скрыта), см. §4 SEC-6**.
|
||||
>
|
||||
> ⚠️ Снимок волатилен. Истина — реальные команды по SSH (`systemctl is-active …`, `nginx -T`, `yc …` при наличии доступа).
|
||||
|
||||
Reference in New Issue
Block a user