Files
ospab.host/QR-AUTH-SECURITY.md
2025-11-23 14:35:16 +03:00

237 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# QR-аутентификация — Безопасность
## Обзор
QR-аутентификация реализована по модели **OAuth2-подобного flow**, аналогично Google/Яндекс/Telegram Login.
---
## Архитектура безопасности
### ✅ Правильный flow (текущая реализация)
```
1. ПК (неавторизованный)
POST /api/qr-auth/generate
← Получает уникальный code (без привязки к пользователю)
Показывает QR: https://ospab.host/qr-login?code=XXX
Polling: GET /api/qr-auth/status/:code каждые 2 секунды
2. Телефон (пользователь УЖЕ авторизован)
Сканирует QR → открывается /qr-login?code=XXX
POST /api/qr-auth/scanning (с Bearer token)
→ Backend обновляет статус QR на "scanning"
← ПК видит "Ожидание подтверждения на телефоне..."
GET /api/auth/me (с Bearer token)
← Получает данные ТЕКУЩЕГО пользователя телефона
Показывает экран подтверждения:
"Войти на новом устройстве как [Ваше имя]?"
Пользователь нажимает "Подтвердить"
POST /api/qr-auth/confirm + Bearer token + code
→ Backend привязывает userId к QR-запросу
→ Обновляет статус на "confirmed"
3. ПК (polling получает confirmed)
Получает JWT токен ЭТОГО пользователя
Вызывает login(token) → обновляет AuthContext
Редирект на /dashboard
```
---
## Защита от уязвимостей
### 🔒 1. Анонимный QR-код
- ✅ QR создаётся **БЕЗ** привязки к пользователю
-`userId` присваивается **только после подтверждения**
- ❌ Невозможно "угадать" чей токен получит ПК
### 🔒 2. Требование авторизации на телефоне
-`/api/qr-auth/scanning` требует `authMiddleware`
-`/api/qr-auth/confirm` требует `authMiddleware`
- ❌ Неавторизованный пользователь НЕ может подтвердить вход
### 🔒 3. Экран подтверждения
```tsx
// Телефон показывает:
<div>
<p>Войти на новом устройстве как:</p>
<p className="text-xl font-bold">{userData.username}</p>
<p className="text-sm text-gray-500">{userData.email}</p>
</div>
<button onClick={handleConfirm}>Подтвердить</button>
<button onClick={handleCancel}>Отмена</button>
```
- ✅ Пользователь **видит** от чьего имени происходит вход
- ✅ Может **отказаться**, если это не он
### 🔒 4. Время жизни QR-кода
```typescript
const QR_EXPIRATION_SECONDS = 60; // 60 секунд
```
- ✅ QR истекает через 60 секунд
- ✅ После использования (confirmed/rejected) — удаляется
- ✅ Cleanup устаревших кодов каждые 24 часа
### 🔒 5. Статусы и переходы
```
pending → scanning → confirmed/rejected/expired
↓ ↓ ↓
Создан Открыт Финальный статус
```
-`pending``scanning`: пользователь открыл страницу
-`scanning``confirmed`: подтвердил вход
-`scanning``rejected`: отклонил вход
-`pending/scanning``expired`: истёк таймаут
### 🔒 6. Polling на ПК
```typescript
// Каждые 2 секунды:
GET /api/qr-auth/status/:code
// Ответы:
{ status: 'pending' } // Ещё не сканировали
{ status: 'scanning' } // Пользователь открыл страницу подтверждения
{ status: 'confirmed', token: 'JWT', user: {...} } // Подтвердили
{ status: 'rejected' } // Отклонили
{ status: 'expired' } // Истёк
```
- ✅ ПК **не генерирует токен** сам
- ✅ ПК **получает токен** только после подтверждения с телефона
- ✅ Токен содержит `userId` пользователя с телефона
---
## Защита от атак
### ❌ Атака: Перехват QR-кода
**Сценарий:** Злоумышленник фотографирует QR с чужого экрана
**Защита:**
- ✅ QR живёт 60 секунд
- ✅ Требуется авторизация на телефоне атакующего
- ✅ Экран подтверждения показывает имя/email входящего пользователя
- ✅ Жертва видит что в её аккаунт пытаются войти
### ❌ Атака: MITM (Man-in-the-Middle)
**Сценарий:** Злоумышленник перехватывает сетевой трафик
**Защита:**
-Все запросы через HTTPS (`https://ospab.host:5000`)
- ✅ JWT токены передаются в `Authorization: Bearer`
- ✅ Токены хранятся в `localStorage` (HttpOnly cookie было бы лучше, но требует серверный рендеринг)
### ❌ Атака: Replay Attack
**Сценарий:** Злоумышленник повторно отправляет перехваченный запрос
**Защита:**
- ✅ QR-код одноразовый (удаляется после confirm/reject)
-`status !== 'pending' && status !== 'scanning'` → ошибка
- ✅ JWT токены имеют `expiresIn: '24h'`
### ❌ Атака: Session Fixation
**Сценарий:** Злоумышленник пытается навязать свой QR-код
**Защита:**
- ✅ ПК генерирует QR **локально** через `/api/qr-auth/generate`
- ✅ Невозможно "навязать" чужой QR (каждый code уникален)
- ✅ Backend не принимает "предустановленные" коды
---
## Сравнение с другими методами
| Метод | Безопасность | Удобство | Скорость |
|------------------------|--------------|----------|----------|
| **QR-аутентификация** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Логин + пароль | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| Email magic link | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| SMS OTP | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| OAuth (Google/Yandex) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
---
## Рекомендации по улучшению (будущее)
### 1. Rate Limiting
```typescript
// Ограничить количество попыток генерации QR с одного IP
// Пример: максимум 10 QR в минуту
```
### 2. Device Fingerprinting
```typescript
// При создании QR запоминать fingerprint ПК
// При polling проверять что запросы идут с того же устройства
```
### 3. Geolocation Check
```typescript
// Если расстояние между IP адресами ПК и телефона > 1000 км → предупреждение
// "Попытка входа из другой страны. Подтвердите что это вы"
```
### 4. WebSocket вместо Polling
```typescript
// Вместо GET /status/:code каждые 2 секунды
// Использовать WebSocket для реального времени
```
### 5. Push Notifications
```typescript
// Отправлять пуш на телефон: "Вход на новом устройстве. Подтвердите?"
// Не требует открывать браузер
```
---
## Заключение
Текущая реализация QR-аутентификации **безопасна** и соответствует индустриальным стандартам (Google, Яндекс, Telegram).
**Ключевые принципы:**
1. ✅ Анонимный QR без привязки к пользователю
2. ✅ Требование авторизации на подтверждающем устройстве
3. ✅ Явный экран подтверждения с информацией о пользователе
4. ✅ Короткое время жизни кодов (60 сек)
5. ✅ Одноразовое использование
6. ✅ HTTPS + JWT токены
**Защищает от:**
- ❌ Перехвата QR
- ❌ MITM атак
- ❌ Replay атак
- ❌ Session Fixation
- ❌ Несанкционированного доступа
---
_Документ обновлён: 10 ноября 2025 г._