# 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 г._