Skip to content

Security: wpalish/yummy

Security

SECURITY.md

Yummy — аудит безопасности и функционала

Разбор по присланным open-source проектам: что взято, что уже было, что осталось.

Источники

Безопасность: API-Security-Checklist, awesome-security, aspnet/Security (.NET), spring-security-oauth (Java), xapax/security. Фудтех/TGTG: enatega multivendor, enatega singlevendor, Tarikul flutter UI, TooGoodToGoNotifier, tgtg_client.

Код .NET/Java/Flutter/React-Native не портировался (несовместимый стек) — перенесены применимые паттерны и чеклисты.

Аудит по API-Security-Checklist

Пункт чеклиста Статус в Yummy
Не Basic Auth, стандартная авторизация ✅ JWT (HS256, stdlib)
Не изобретать хеш пароля ✅ PBKDF2-HMAC-SHA256, соль+200k, constant-time
Max Retry / jail на логине ✅ rate-limit 6/мин на /auth/*; 8/мин на /orders
Шифрование чувствительных данных ✅ пароли только хешем; хеш не в ответе
Throttling против DDoS/brute-force ✅ in-memory rate-limit по IP
HTTPS + HSTS ✅ HSTS-заголовок; TLS даёт Pages/Render
Все эндпоинты за аутентификацией добавлено: ролевые require_role на /boxes, /redeem, /admin/* (env YUMMY_ENFORCE_AUTH=1)
/me/orders вместо /user/{id}/orders 🟡 есть /partners/{id} (id — UUID); /auth/me реализован
UUID вместо auto-increment ✅ uuid4 для заказов и пользователей
Валидация content-type / входа ✅ Pydantic; FastAPI требует JSON
Нет секретов в URL ✅ логин/пароль в теле POST
Параметризованные запросы (нет SQLi) ✅ sqlite3 с плейсхолдерами
X-Content-Type-Options: nosniff ✅ middleware
X-Frame-Options: deny ✅ middleware (DENY)
Content-Security-Policy ✅ middleware
COOP / CORP / cross-domain-policy добавлено (из Arena-ревью): same-origin / cross-origin / none
Correlation ID X-Request-Id добавлено: уникальный на запрос, в ответе и аудит-логах (rid=)
Лимит тела запроса (анти-DoS) добавлено: 64 KiB (env YUMMY_MAX_BODY_BYTES), 413 на оверсайз
Host allowlist (Host-Header-Injection) добавлено: YUMMY_ALLOWED_HOSTS (пусто→* для демо)
Swagger/OpenAPI выключены в проде добавлено: авто при YUMMY_ENFORCE_AUTH=1
Убрать fingerprint Server добавлено: uvicorn --no-server-header
Force content-type ответа ✅ FastAPI JSON
Не возвращать секреты ✅ pw_hash никогда не в ответе
Правильные HTTP-статусы ✅ 201/401/403/409/422/429
DEBUG off ✅ FastAPI без debug; /docs можно скрыть в проде
Тесты (unit/integration) ✅ 31 тест (крипто/JWT/флоу/заголовки/ролевой доступ)
Централизованные логи, без секретов ✅ аудит-лог logging (register/login/IP, без паролей)
security.txt добавлено: /.well-known/security.txt
CORS без wildcard+credentials ✅ явные origin из env

Что уже было из фудтех-функционала (enatega/TGTG)

Сюрприз-бокс (magic bag), бронь+окно выдачи, QR/код, no-show/возврат, избранные кофейни (♥), рейтинги, поиск, история заказов, карта с точками, категории, фильтры (район/сеть/«забрать сейчас»), эко-вклад, passwordless-вход (Telegram), PWA-установка, маршрут в 2ГИС, deep-link на бокс, регистрация email+пароль.

Прод-этап (нужны внешние сервисы/ключи — отложено)

  • Уведомления о появлении боксов (ключевая фича TGTG-нотификаторов): нужен бот/почта — Telegram-бот (токен от @BotFather) + рассылка «твоя Zebra выставила боксы». Инфраструктура (auth_telegram.py) готова.
  • Реальные платежи — Kaspi Pay/QR (deep-link-слот готов).
  • Social-auth Google/Apple (enatega) — нужны OAuth-ключи.
  • Real-time трекинг/чат — WebSocket + сервер.
  • Sentry/мониторинг (enatega) — нужен DSN.

Пак «БД и безопасность» (доделка)

БД (SQLite production-grade):

  • WAL + busy_timeout=5000 + synchronous=NORMAL — параллельные запросы без «database is locked»; foreign keys включены, схема с REFERENCES.
  • Индексы под реальные запросы: boxes(partner,status), orders(partner/user/status).
  • Путь к БД — env YUMMY_DB_PATH (persistent-диск на хостинге).
  • make backup — консистентный бэкап через официальный SQLite backup API (безопасен при живом WAL), хранит последние 14 копий.

Безопасность:

  • Отзыв сессий: смена пароля инкрементирует users.token_ver → все ранее выданные JWT (в т.ч. украденные) мгновенно мертвы; текущему устройству возвращается свежий токен. Проверено live: 200 → 401 → 200.
  • Fail-fast конфиг: YUMMY_ENFORCE_AUTH=1 с dev-секретом — сервер отказывается стартовать с ясной ошибкой (проверено).
  • Rate-limiter: защита от роста памяти (purge стухших IP при >4096 записей).
  • Аудит возвратов админа (кто/какой заказ/IP).
  • Bandit-скан: 0 issues (единственный сигнал — намеренный dev-сентинел, nosec).
  • Секретов в репозитории нет (grep по паттернам токенов — чисто).

Карта по Sentinel-спеке (честно: что есть, чего нет)

Принцип из самой спеки: не заявлять функции, которых нет.

Sentinel-пункт Статус
Короткий access-токен (15 мин) _TOKEN_TTL = 15 мин
Refresh-токен с ротацией ✅ /auth/refresh; в БД только SHA-256-хеш; использованный сгорает (проверено live)
Выйти со всех устройств ✅ /auth/logout-all (token_ver+1 + отзыв всех refresh)
Argon2id не заявляем — PBKDF2-HMAC-SHA256 600k итераций (OWASP-уровень), без нативных зависимостей; старые хеши совместимы
Пароли не хранятся ✅ только солёный хеш
RBAC ✅ customer / partner / admin (require_role)
Brute force / credential stuffing ✅ rate-limit по IP + jail по email (5 неудач → 10 мин)
SQLi / XSS / Clickjacking ✅ параметризованный SQL / esc()+CSP / X-Frame-Options DENY
CSRF ✅ N/A — cookies не используются (токен в заголовке)
Аудит: регистрация/вход/смена пароля/заказы/возвраты ✅ логируются с IP, без паролей
Privacy: скачать свои данные ✅ GET /me/export (профиль + заказы, JSON)
Privacy: удалить аккаунт ✅ DELETE /me — вход невозможен, PII в заказах обезличен (проверено)
Резервные копии ✅ make backup (шифрование бэкапов — нет, не заявляем)
Email verification / OAuth / 2FA / CAPTCHA / гео-детект / уведомления о входе не реализовано — требуют внешних сервисов (SMTP, OAuth-ключи, SMS). Заготовка: Telegram-вход
TLS 1.3 / AES-256 at rest 🟡 TLS даёт хостинг; шифрование БД (SQLCipher) — прод-этап, не заявляем

Пак «AI-фичи» (описания, рекомендации, отзывы+модерация, скрипт продаж)

Принцип тот же: не заявлять AI там, где его нет. Интеграция — сырой httpx-клиент к Anthropic API (app/ai.py), без агентных фреймворков (LangChain/agno/crewAI и т.п.) — меньше зависимостей и предсказуемое поведение без ключа.

Фича Без ANTHROPIC_API_KEY С ключом
Описание бокса (POST /ai/describe-box) ✅ детерминированный шаблон (ai:false в ответе) AI-текст (ai:true)
Рекомендации (GET /me/recommendations) ✅ всегда — это не AI, детерминированный скоринг по истории покупок (категория×2 + партнёр×3), без AI не деградирует тот же алгоритм (AI не участвует)
Модерация отзыва (POST /partners/{id}/reviews) ✅ эвристика: мат-лист, спам-ссылки (http/www/t.me/@handle), капслок >70%, повтор символов эвристика + AI поверх (эвристика остаётся первым фильтром)
Скрипт продаж (tools/generate_pitch.py) ✅ шаблон по тексту из SALES.md, персонализирован именем/адресом AI-текст, другой при каждом вызове

Почему не 501, а фолбэк: в отличие от Kaspi/Telegram-слотов (которые честно отдают 501 без ключа, т.к. без реальной интеграции работать не могут), у всех четырёх AI-фич есть осмысленный офлайн-эквивалент — фича не исчезает, а становится проще.

Безопасность отзывов: заказ на владение проверяется через store.user_orders(user.id) (тот же WHERE user_id=?, что и в /me/orders) — id заказа из тела запроса никогда не доверяется напрямую; отзыв разрешён только по заказу в статусе issued (реально выдан по коду) и не более одного на заказ. Rate-limit на AI-эндпоинты отдельный и строже (6/60 сек), чем на заказы (8/60 сек) — дороже по деньгам при реальном ключе.

Не заявляем: AI-модерация не «финальная» — это фильтр перед публикацией, живой модератор всё ещё нужен для спорных случаев; описания и питчи с AI не проверяются на фактическую точность (партнёр видит текст перед публикацией).

Правовой слой (пункт «правовая база не закрыта»)

Однострочные заглушки заменены содержательными рабочими шаблонами (в репо legal/, в приложении — футер → «Документы»). Не заверены юристом — перед публичным запуском обязательна вычитка юристом РК.

  • legal/oferta.md — публичная оферта (агентская модель, оплата, выдача, возвраты, ответственность, права по Закону РК «О защите прав потребителей»).
  • legal/privacy.md — политика по Закону РК «О персональных данных» № 94-V (какие данные, согласие, передача только по заказу, права пользователя).
  • legal/partner-agreement.md — агентский договор с заведением (комиссия, выплаты по средам, обязанности, споры, расторжение).
  • legal/food-safety.md — стандарты пищевой безопасности (что продавать, хранение, аллергены, чек-лист, ответственность заведения).

Согласие на обработку ПДн (ст. 8 № 94-V) — обязательный чекбокс при регистрации (онбординг и лендинг): без галочки регистрация блокируется. У заведения дополнительно — принятие договора и стандартов пищевой безопасности. Проверено live: без согласия — ошибка и аккаунт не создаётся.

There aren't any published security advisories