# Безопасность проекта — объяснение для чайников Этот документ объясняет простым языком, **что и зачем** было сделано для защиты API. --- ## Оглавление 1. [Что такое «небезопасный» API и почему это плохо](#1-что-такое-небезопасный-api) 2. [Что было сделано — краткий список](#2-что-было-сделано) 3. [Аутентификация — кто ты такой?](#3-аутентификация) 4. [JWT-токены — цифровой пропуск](#4-jwt-токены) 5. [Хеширование паролей](#5-хеширование-паролей) 6. [Защита маршрутов (роутов)](#6-защита-маршрутов) 7. [Admin Key — ключ администратора](#7-admin-key) 8. [Защита документации `/docs`](#8-защита-документации-docs) 9. [CORS — кто может обращаться к API из браузера](#9-cors) 10. [Переменные окружения — секреты не в коде](#10-переменные-окружения) 11. [Как всё настроить — пошаговая инструкция](#11-как-настроить) 12. [Как использовать API — примеры](#12-примеры-использования) --- ## 1. Что такое «небезопасный» API Представь, что у тебя есть склад. **Небезопасный API** — это когда дверь склада открыта для всех: любой может зайти, взять что хочет или сломать что хочет. Конкретно, без защиты: - любой человек в интернете мог удалить любого пользователя - любой мог посмотреть чужие данные - пароли хранились бы в открытом виде — украли базу = украли все пароли - документация (Swagger) была видна всем, давая полную карту API для взломщика --- ## 2. Что было сделано | Что | Зачем | |-----|-------| | JWT-токены | Пользователь должен войти, чтобы делать что-то важное | | Хеширование паролей (bcrypt) | Даже если украдут БД — пароли не читаемы | | Проверка владельца при PUT/DELETE | Ты можешь менять только свои данные | | Admin Key | Опасные операции (инициализация БД) — только для администратора | | HTTP Basic Auth на `/docs` | Документация API скрыта за паролем | | CORS | Браузерные запросы принимаются только с разрешённых сайтов | | Секреты в переменных окружения | Пароли и ключи не хранятся в коде | --- ## 3. Аутентификация **Аутентификация** = подтверждение того, кто ты есть. Как это работает в нашем API: ``` 1. Ты отправляешь логин и пароль на POST /auth/login 2. Сервер проверяет: правильный ли пароль? 3. Если да — сервер выдаёт тебе "токен" (длинную строку-пропуск) 4. При каждом следующем запросе ты добавляешь этот токен в заголовок 5. Сервер смотрит на токен и понимает, кто ты ``` Аналогия: как билет в кино. Купил один раз — показываешь при входе, не нужно снова платить. **Регистрация** — `POST /auth/register`: ```json { "username": "ivan", "password": "мойпароль123", "first_name": "Иван", "last_name": "Иванов" } ``` **Вход** — `POST /auth/login` (формат form-data): ``` username=ivan password=мойпароль123 ``` Ответ: ```json { "access_token": "eyJhbGci...(длинная строка)...", "token_type": "bearer" } ``` --- ## 4. JWT-токены **JWT** (JSON Web Token) — это как паспорт, только цифровой. Токен состоит из трёх частей, разделённых точкой: ``` eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiIxMjMifQ . SflKxwRJSMeKKF2QT4 заголовок данные подпись ``` - **Заголовок**: тип токена и алгоритм шифрования - **Данные (payload)**: ID пользователя, время истечения токена - **Подпись**: криптографическая подпись — без секретного ключа сервера её нельзя подделать Аналогия: токен — это как печать на паспорте. Печать нельзя нарисовать самому (без настоящей печати), поэтому её нельзя подделать. **Как использовать токен в запросах:** В заголовке HTTP: ``` Authorization: Bearer eyJhbGci...(твой токен)... ``` **Срок жизни токена** задаётся переменной `ACCESS_TOKEN_EXPIRE_MINUTES` (по умолчанию 60 минут). --- ## 5. Хеширование паролей Пароли **никогда** не хранятся в открытом виде. Вместо этого хранится их хеш. ``` Пароль: "мойпароль123" ↓ bcrypt Хеш: "$2b$12$eImiTXuWVxfM37uY4JANjQ..." ``` **Почему это безопасно:** - Хеш нельзя "расшифровать" обратно в пароль - Даже если база данных будет украдена — злоумышленник увидит только хеши - При проверке он не расшифровывает, а снова хеширует введённый пароль и сравнивает хеши Алгоритм **bcrypt** специально медленный — это делает перебор паролей (brute force) очень долгим. --- ## 6. Защита маршрутов Некоторые действия требуют авторизации, другие открыты для всех. | Маршрут | Открыт? | Объяснение | |---------|---------|-----------| | `POST /auth/register` | ✅ Да | Регистрация открыта всем | | `POST /auth/login` | ✅ Да | Вход открыт всем | | `GET /groups/` | ✅ Да | Просмотр групп — публичное | | `POST /groups/` | 🔒 Нет | Создать группу — нужен токен | | `PUT /users/{id}` | 🔒 Нет | Изменить профиль — только свой | | `DELETE /users/{id}` | 🔒 Нет | Удалить — только свой аккаунт | | `POST /init_database` | 🔒 Admin | Только с Admin Key | **Проверка владельца**: при попытке изменить или удалить чужие данные сервер вернёт ошибку `403 Forbidden`. --- ## 7. Admin Key Некоторые операции очень опасны (например, инициализация базы данных). Для них нужен специальный ключ администратора. **Как использовать:** ``` Заголовок запроса: X-Admin-Key: твой_секретный_admin_ключ ``` Если `ADMIN_KEY` не задан в переменных окружения — такие операции полностью отключены (сервер вернёт `503`). --- ## 8. Защита документации `/docs` Swagger UI (`/docs`) — это интерактивная документация API. Она показывает все маршруты, параметры, позволяет делать запросы прямо из браузера. Это очень удобно для разработчика, но и очень удобно для взломщика. **Решение:** HTTP Basic Auth — браузер сам показывает окошко с запросом пароля. ``` Открываешь http://твой-сервер/docs ↓ Браузер показывает окошко: "Введите логин и пароль" ↓ Вводишь DOCS_USERNAME и DOCS_PASSWORD ↓ Видишь документацию ``` То же самое работает для `/redoc` и `/openapi.json`. --- ## 9. CORS **CORS** (Cross-Origin Resource Sharing) — механизм браузера, который контролирует, с каких сайтов можно делать запросы к API. Аналогия: CORS — это как охранник на входе, который проверяет твой бейдж (адрес сайта). Если бейдж не из списка — не пропустит. Разрешённые источники в нашем проекте: ``` http://localhost:3000 http://127.0.0.1:3000 ``` Это значит, что только фронтенд, запущенный локально на порту 3000, может обращаться к API из браузера. > ⚠️ **Важно для production**: если у тебя есть публичный домен, добавь его в список `allow_origins` в `main.py`. --- ## 10. Переменные окружения **Секреты никогда не должны быть в коде.** Если код попадёт на GitHub — все пароли станут публичными. Все секреты хранятся в переменных окружения (в `docker-compose.yml` или в `.env` файле). | Переменная | Что это | Как создать | |-----------|---------|------------| | `SECRET_KEY` | Ключ для подписи JWT-токенов | `python -c "import secrets; print(secrets.token_hex(32))"` | | `ADMIN_KEY` | Пароль для admin-операций | Придумай длинную случайную строку | | `DOCS_USERNAME` | Логин для доступа к `/docs` | Например: `admin` | | `DOCS_PASSWORD` | Пароль для доступа к `/docs` | Придумай надёжный пароль | | `ACCESS_TOKEN_EXPIRE_MINUTES` | Сколько минут живёт токен | По умолчанию 60 | --- ## 11. Как настроить ### Шаг 1. Сгенерируй SECRET_KEY Открой терминал и запусти: ```bash python -c "import secrets; print(secrets.token_hex(32))" ``` Скопируй результат — это твой `SECRET_KEY`. Сделай то же самое для `ADMIN_KEY`. ### Шаг 2. Задай пароль для документации Придумай любой логин и пароль для `DOCS_USERNAME` и `DOCS_PASSWORD`. ### Шаг 3. Обнови docker-compose.yml Найди блок `environment` в `docker-compose.yml` и замени все `REPLACE_ME_...` на реальные значения: ```yaml environment: - SECRET_KEY=abcdef1234... # результат из шага 1 - ADMIN_KEY=supersecret456 # результат из шага 1 - DOCS_USERNAME=admin - DOCS_PASSWORD=мойпарольдокументации ``` ### Шаг 4. Запусти проект ```bash docker-compose up --build ``` ### Шаг 5. Проверь Открой в браузере `http://localhost:8000/docs` — должно появиться окошко с запросом пароля. --- ## 12. Примеры использования ### Зарегистрироваться ```bash curl -X POST http://localhost:8000/auth/register \ -H "Content-Type: application/json" \ -d '{"username":"ivan","password":"пароль123","first_name":"Иван","last_name":"Иванов"}' ``` ### Войти и получить токен ```bash curl -X POST http://localhost:8000/auth/login \ -d "username=ivan&password=пароль123" ``` ### Использовать токен в запросе ```bash TOKEN="eyJhbGci..." # твой токен из предыдущего шага curl -X GET http://localhost:8000/users/me \ -H "Authorization: Bearer $TOKEN" ``` ### Создать группу (нужен токен) ```bash curl -X POST http://localhost:8000/groups/ \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name_group":"Группа 1","user_id":"uuid-пользователя"}' ``` ### Открыть документацию в браузере Просто зайди на `http://localhost:8000/docs` — браузер спросит логин/пароль. --- ## Что ещё можно улучшить (на будущее) - **Rate limiting** — ограничение количества запросов с одного IP (защита от брутфорса) - **Refresh tokens** — долгоживущие токены для обновления access token без повторного входа - **HTTPS** — шифрование соединения (критично в production, настраивается на уровне nginx/proxy) - **Логирование** — запись подозрительных действий (много неудачных попыток входа) - **Email-верификация** — подтверждение email при регистрации