add user? new page? new web
This commit is contained in:
306
SECURITY.md
Normal file
306
SECURITY.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# Безопасность проекта — объяснение для чайников
|
||||
|
||||
Этот документ объясняет простым языком, **что и зачем** было сделано для защиты 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 при регистрации
|
||||
Reference in New Issue
Block a user