Skip to content
Назад в блог

Защита OAuth 2.0 в SPA и мобильных приложениях через PKCE

3 мин. чтения
Защита OAuth 2.0 в SPA и мобильных приложениях через PKCE

В первые годы существования стандарта OAuth 2.0 (RFC 6749) основным способом получения access token был серверный Authorization Code Flow с использованием client_id и client_secret. Однако с бурным ростом фронтенд-фреймворков (React, Vue, Nuxt) и мобильных приложений возникла критическая уязвимость: секрет приложения (client_secret), встроенный в клиентский код, легко компрометировался.

Проблема безопасности в публичных клиентах (Public Clients)

Публичные клиенты (SPA в браузере, мобильные и десктопные приложения) физически не способны защитить секретный ключ. Любой пользователь может открыть Chrome DevTools или декомпилировать приложение и извлечь client_secret.

Кроме того, при перенаправлении пользователя обратно на redirect_uri злоумышленник (через вредоносное расширение браузера или кастомный схематичный URI на мобильном устройстве) мог перехватить code и обменять его на токен, используя украденный client_secret.

Скандальное рождение OAuth 2.0 (2012)

OAuth 2.0 появился не тихо. Эран Хаммер (Eran Hammer), ведущий автор и редактор спецификации, в июле 2012 года покинул рабочую группу — за несколько месяцев до публикации RFC 6749 — и опубликовал пост под названием «OAuth 2.0 and the Road to Hell» («OAuth 2.0 и дорога в ад»). Его претензия: спецификация стала «сложнее, менее совместимой, менее полезной, более незавершённой и, главное, менее безопасной», чем OAuth 1.0, отказавшись от обязательных подписей в пользу bearer-токенов и оставив слишком много деталей на усмотрение каждого вендора. RFC 6749 всё равно вышел в октябре того же года — уже без его имени. PKCE во многом существует, чтобы залатать именно ту дыру, о которой предупреждала эта критика.

Как работает PKCE (RFC 7636)

В сентябре 2015 года Nat Sakimura, John Bradley и Naveen Agarwal опубликовали RFC 7636, и изначально он писался вовсе не для SPA — мотивирующий сценарий был про нативные мобильные приложения. Два приложения могли зарегистрироваться на одну и ту же кастомную URI-схему (myapp://callback), и вредоносное приложение могло перехватить код авторизации, предназначенный для легитимного. PKCE (произносится «пикси») закрыл эту дыру, а позже выяснилось, что та же техника отлично подходит и браузерным публичным клиентам — как только индустрия осознала, что client_secret в JavaScript ничем не безопаснее, чем в мобильном бинарнике.

Proof Key for Code Exchange исключает необходимость хранить client_secret на клиенте и работает по следующему алгоритму:

  Client Browser (SPA)                    Auth Server (IDP)
        |                                       |
        |---- (A) Authorization Request ------->|
        |     + code_challenge (S256)           |
        |                                       |
        |<--- (B) Authorization Code -----------|
        |                                       |
        |---- (C) Token Request --------------->|
        |     + code_verifier                   |
        |                                       |
        |<--- (D) Access Token -----------------|
        |                                       |
  1. Генерация пары на клиенте:
    • Браузер генерирует криптографически стойкую случайную строку code_verifier (от 43 до 128 символов).
    • Вычисляется code_challenge: Base64URL(SHA256(code_verifier)).
  2. Запрос авторизации:
    • Клиент отправляет пользователя на сервер авторизации, передавая client_id, redirect_uri, code_challenge и code_challenge_method=S256.
  3. Обмен кода на токен:
    • После успешного входа сервер авторизации возвращает code.
    • Клиент делает POST-запрос на /token, передавая исходный необработанный code_verifier.
    • Сервер авторизации сам вычисляет SHA256(code_verifier) и сравнивает с сохраненным code_challenge. Если хэши совпали — выдается access_token.

Возможности нашего OAuth PKCE Generator

Инструмент OAuth 2.0 PKCE Generator & Auth Flow Sandbox позволяет отлаживать весь цикл авторизации:

  • Генерация PKCE пар (S256): мгновенное вычисление code_verifier и code_challenge через стойкий Web Crypto API (window.crypto.subtle).
  • Сборщик Auth URL: пресеты для Google OAuth 2.0, GitHub, Microsoft Entra ID (Azure AD) и Auth0.
  • Генератор cURL запроса обмена: готовая команда для терминала для выполнения POST /token.
  • Парсер Callback URL: разбор параметров code, state и error из адресной строки после перенаправления.

Быстрый старт: откройте тул → сгенерируйте новую PKCE-пару → перейдите на вкладку Auth URL Builder → выберите пресет провайдера → скопируйте URL и откройте его в новой вкладке → после входа вставьте callback-URL в парсер → используйте полученный code вместе с вашим code_verifier во вкладке Token Exchange cURL.

Похожие инструменты

Частые вопросы

Зачем нужен PKCE (Proof Key for Code Exchange)?

В одностраничных веб-приложениях (SPA) и мобильных клиентах невозможно безопасно хранить Client Secret (он может быть извлечен из JS-кода или декомпилированного APK). PKCE предотвращает перехват авторизационного кода (Authorization Code Interception Attack) за счет генерации одноразовой динамической криптографической пары верификатора и хэша вызова.

Чем отличаются методы code_challenge_method plain и S256?

При методе plain значение code_challenge передается в открытом виде как копия code_verifier, что уязвимо к прослушиванию трафика. Метод S256 наносит SHA-256 хэш на code_verifier и кодирует результат в Base64URL — это золотой стандарт безопасности RFC 7636.

Какая допустимая длина у code_verifier?

Согласно спецификации RFC 7636, строка code_verifier должна иметь длину от 43 до 128 символов и состоять только из не зарезервированных символов URL [A-Z, a-z, 0-9, -, ., _, ~].

Автор

Fullstack Engineer

В коммерческой разработке с 2011 года — больше 15 лет. Автор двух SaaS-продуктов: TeleGo.io и LingvoHabit.com. Статьи здесь выросли из реальной клиентской и продуктовой работы.

Подробнее об автореНаписать в Telegram

Похожие статьи

GZIP VS BROTLI
GZIP VS BROTLI · '26

Как патенты на LZW привели к gzip, а веб к Brotli

История gzip, DEFLATE и Brotli, устройство двух форматов сжатия и практический выбор между размером результата, скоростью кодирования и совместимостью.

#GZip vs Brotli #история сжатия данных #Jean-loup Gailly #Brotli Google #Deflate алгоритм #LZ77 Huffman
5 мин. чтения
ФОРМАТИРОВАНИЕ RTL ТЕКСТА
ФОРМАТИРОВАНИЕ RTL ТЕКСТА · '26

Почему иврит и английский меняются местами

Как Unicode BiDi отображает иврит и арабский вместе с латиницей и кириллицей и когда использовать isolates, LRM или RLM.

#Форматирование RTL текста #Иврит и английский #Unicode BiDi controls #LRM и RLM #LRI RLI PDI #Смешанное направление текста
3 мин. чтения
СЖАТИЕ ИЗОБРАЖЕНИЙ
СЖАТИЕ ИЗОБРАЖЕНИЙ · '26

Оптимизация SVG, экспорт кода и сжатие изображений

Оптимизируйте и трансформируйте SVG, экспортируйте React, React Native, Vue, PNG или Data URI и сжимайте растровые изображения локально в браузере.

#сжатие изображений #WebP формат #оптимизация SVG #PNG vs JPEG #AVIF #SVGO
4 мин. чтения