# 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
// Телефон показывает:
Войти на новом устройстве как:
{userData.username}
{userData.email}
```
- ✅ Пользователь **видит** от чьего имени происходит вход
- ✅ Может **отказаться**, если это не он
### 🔒 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 г._