add user? new page? new web

This commit is contained in:
2026-03-23 21:28:10 +05:00
parent 04bfff3d3f
commit 9cd6d83428
26 changed files with 917 additions and 65 deletions

306
SECURITY.md Normal file
View 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 при регистрации