diff --git a/cspell-words.txt b/cspell-words.txt index ca6ee9c6..898654b7 100644 --- a/cspell-words.txt +++ b/cspell-words.txt @@ -1617,6 +1617,11 @@ SMTPS бакет MTA алиас +прода +попап +COEP +Самобана +CDP волатилен синке субдомен diff --git a/ПИЛОТ.md b/ПИЛОТ.md index a52559bc..dffb3730 100644 --- a/ПИЛОТ.md +++ b/ПИЛОТ.md @@ -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 …` при наличии доступа).