# ОТЧЁТ О БЕЗОПАСНОСТИ ## SeaFare Montana (seafare-montana.duckdns.org) — 28.02.2026 --- ## Executive Summary - **Находок: 8** (Критические: 0 | Высокие: 2 | Средние: 4 | Низкие: 2) - **Общая оценка: Удовлетворительная** - Критических уязвимостей не найдено. Основные проблемы: утечка внутренней информации через `/health`, dev-CORS конфиг в production, отсутствие email-верификации. Финансовые endpoints (wallet, subscription) защищены корректно. --- ## Scope | Параметр | Значение | |----------|----------| | **Цель** | https://seafare-montana.duckdns.org | | **IP** | 89.19.208.158 | | **Приложение** | SeaFare Montana — Maritime Logistics AI | | **Стек** | nginx/1.24.0 (Ubuntu) → Flask (Python) + PostgreSQL | | **Интеграции** | Google OAuth, Telegram Bot, AISStream, Digitraffic, USDT TRC20 (Tron) | | **Frontend** | Vanilla HTML/JS + Leaflet.js (карты) | | **Авторизация** | Flask itsdangerous signed tokens (Bearer) | | **Период** | 28.02.2026 | | **Тип** | Black-box → Grey-box | | **Авторизация на тест** | Подтверждена владельцем | --- ## Результаты сканирования портов | Порт | Сервис | Статус | |------|--------|--------| | **80** | HTTP (nginx) | **OPEN** (→ redirect to HTTPS) | | **443** | HTTPS (nginx) | **OPEN** | | 22 | SSH | closed | | 3306 | MySQL | closed | | 5432 | PostgreSQL | closed | | 6379 | Redis | closed | | 27017 | MongoDB | closed | | 5050 | Backend (Flask) | closed | | 8080-9200 | Все прочие (21 порт) | closed | **Вердикт**: минимальная поверхность атаки. Только HTTP/HTTPS наружу. --- ## API Attack Surface **Открытые эндпоинты (без auth):** | Эндпоинт | Данные | |----------|--------| | `GET /health` | Имя сервиса, версия, DB, users count, AIS status | | `GET /api/v1/auth/config` | Google OAuth Client ID | | `GET /api/v1/subscription/plans` | Планы подписки с ценами | | `GET /api/v1/ports/search?q=` | Поиск портов (116K+) | | `POST /api/v1/auth/register` | Открытая регистрация | | `POST /api/v1/auth/login` | Логин | **Авторизированные эндпоинты (21+):** auth/me, profile, chat/stream, chat/history, map/vessels, wallet, wallet/check, wallet/withdraw, wallet/withdrawals, subscription/upgrade, purchased-contacts, telegram/status, telegram/link, telegram/unlink, admin/revenue, admin/costs --- ## Находки --- ### [HIGH-001] Health endpoint — массивная утечка внутренней информации - **Severity**: High (CVSS 7.2) - **Категория**: Information Disclosure (OWASP A01:2021) - **CWE**: CWE-200 **Описание**: Публичный `/health` раскрывает: - Имя сервиса: "SeaFare API" v3.30.0 - Тип БД: PostgreSQL - Количество пользователей: 4 (до теста) - AISStream: статус подключения, bounding boxes, reconnect count, thread status - Digitraffic: статус конфигурации **POC:** ```bash curl -s https://seafare-montana.duckdns.org/health # → {"db":"ok","db_mode":"postgres","users":4,"version":"3.30.0", # "ais":{"aisstream":{"connected":true,"tracked_mmsis":0,...}}} ``` **Impact**: Атакующий узнаёт версию, стек, кол-во пользователей, состояние внутренних сервисов без авторизации. Упрощает целевую атаку. **Remediation:** 1. Убрать users count, AIS details, version из публичного health 2. Оставить только `{"status":"ok"}` для мониторинга 3. Детальный health — за авторизацией (admin only) --- ### [HIGH-002] CORS dev-конфиг в production - **Severity**: High (CVSS 6.8) - **Категория**: Security Misconfiguration (OWASP A05:2021) - **CWE**: CWE-942 **Описание**: Заголовок `Access-Control-Allow-Origin: http://127.0.0.1:5050` возвращается на все ответы. Раскрывает: 1. Внутренний backend на порту 5050 2. Протокол HTTP (не HTTPS) для внутренних запросов 3. Dev-конфигурация осталась в production **POC:** ```bash curl -sI https://seafare-montana.duckdns.org/ # → Access-Control-Allow-Origin: http://127.0.0.1:5050 ``` **Remediation:** 1. Заменить на `Access-Control-Allow-Origin: https://seafare-montana.duckdns.org` 2. Или динамически проверять Origin по whitelist --- ### [MED-001] Открытая регистрация без email-верификации - **Severity**: Medium (CVSS 5.3) - **CWE**: CWE-287 **Описание**: Регистрация мгновенная, без подтверждения email. Аккаунт сразу активен. Можно создавать аккаунты на чужие email. **POC:** ```bash curl -s https://seafare-montana.duckdns.org/api/v1/auth/register \ -X POST -H "Content-Type: application/json" \ -d '{"email":"anyone@example.com","password":"123456"}' # → {"success":true, "token":"...", "user":{"id":5,"is_active":true}} ``` **Remediation**: Email-верификация перед активацией аккаунта. --- ### [MED-002] nginx версия раскрыта в заголовках - **Severity**: Medium (CVSS 4.3) - **CWE**: CWE-200 **Описание**: `Server: nginx/1.24.0 (Ubuntu)` — точная версия и ОС. **Remediation**: `server_tokens off;` в nginx.conf --- ### [MED-003] Flask itsdangerous token — payload в cleartext - **Severity**: Medium (CVSS 4.5) - **CWE**: CWE-311 **Описание**: Токен аутентификации содержит base64-encoded payload без шифрования. Любой может декодировать: ``` eyJlbWFpbCI6InBlbnRlc3QuLi4iLCJuYW1lIjoiIn0= → {"email":"pentest.seafare@protonmail.com","name":""} ``` Подпись (HMAC) защищает от модификации, но email виден. **Remediation**: Рассмотреть шифрование payload или переход на JWT с минимальным payload (только user_id). --- ### [MED-004] Subscription upgrade — 500 Internal Server Error - **Severity**: Medium (CVSS 4.0) - **CWE**: CWE-209, CWE-755 **Описание**: `POST /api/v1/subscription/upgrade` возвращает 500 без обработки ошибки. Может раскрывать stack trace в debug-режиме. **POC:** ```bash curl -s https://seafare-montana.duckdns.org/api/v1/subscription/upgrade \ -X POST -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{"plan":"pro"}' # → 500 Internal Server Error ``` **Remediation**: Обработать ошибку, вернуть корректный JSON с описанием (недостаточно средств / невалидный план). --- ### [LOW-001] Google OAuth Client ID публично доступен - **Severity**: Low (CVSS 2.5) - **CWE**: CWE-200 **Описание**: `/api/v1/auth/config` возвращает Google OAuth Client ID. Сам по себе не секрет (нужен для frontend), но позволяет идентифицировать Google Cloud проект. ``` {"google_client_id":"199727561298-...apps.googleusercontent.com"} ``` --- ### [LOW-002] Wallet API раскрывает реальный blockchain-адрес - **Severity**: Low (CVSS 3.0) - **CWE**: CWE-200 **Описание**: `/api/v1/wallet` возвращает реальный USDT TRC20 адрес: `TRyQSvbE43RzZuuDaNSpbTvyxzaKqvTybR`. Адрес публично отслеживается на блокчейн-эксплорерах. **Remediation**: Генерировать адрес только при запросе на депозит, не показывать постоянно. --- ## Что НЕ уязвимо (хорошие практики) | Проверка | Результат | |----------|-----------| | Admin endpoints (revenue, costs) | **Заблокированы** — "Admin access required" | | Privilege escalation (is_admin, plan, balance через profile) | **Заблокировано** — поля не меняют auth-модель | | Wallet negative amount | **Заблокировано** — min $2 USDT | | SQL Injection (ports/search) | **Не обнаружена** — параметризация | | CORS reflection (evil origin) | **Не отражается** | | Network ports (DB, Redis, SSH) | **Все закрыты** | | HSTS | **Включен** — max-age=31536000 | | Security headers | **Все присутствуют** — XFO, XCT, XXP, RP | | TLS | **Валидный** сертификат | | Flask SECRET_KEY | **Не тривиальный** — 50 common secrets не подошли | | Subscription bypass | **Не работает** — план остался free | --- ## Рекомендации по приоритету ### Немедленно (High) 1. Убрать детальную информацию из `/health` — оставить только `{"status":"ok"}` 2. Заменить CORS `127.0.0.1:5050` на production домен ### В течение недели (Medium) 3. Добавить email-верификацию при регистрации 4. `server_tokens off` в nginx 5. Обработать 500 ошибку на subscription/upgrade 6. Шифровать payload токена или перейти на JWT ### В течение месяца (Low) 7. Генерировать wallet-адрес on-demand 8. Ограничить /api/v1/auth/config авторизацией --- ## Методология - **Инструменты**: curl, Python 3.14 - **Стандарты**: OWASP Top 10 (2021), OWASP API Security Top 10 (2023) - **Просканировано**: 21 порт, 20+ API-эндпоинтов - **Тестовый аккаунт**: `pentest.seafare@protonmail.com` (ID: 5) — **удалить** - **Ущерб**: нулевой --- *Отчёт подготовлен: 28.02.2026* *Классификация: КОНФИДЕНЦИАЛЬНО*