montana/Русский/Логистика/report_seafare_2026-02-28.md

260 lines
10 KiB
Markdown
Raw Permalink Normal View History

# ОТЧЁТ О БЕЗОПАСНОСТИ
## 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 <TOKEN>" \
-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*
*Классификация: КОНФИДЕНЦИАЛЬНО*