PKCE
PKCE (Proof Key for Code Exchange, произносится «пикси») — это расширение протокола авторизации OAuth 2.0, предназначенное для защиты от атаки с перехватом авторизационного кода. PKCE применяется в первую очередь в публичных клиентах (мобильные приложения, одностраничные веб-приложения, нативные десктопные программы), где невозможно гарантировать безопасное хранение секретного ключа клиента (client_secret). Механизм был разработан для устранения уязвимости, связанной с возможностью злоумышленника перехватить авторизационный код и обменять его на токен доступа.
История и предпосылки создания
Протокол OAuth 2.0, описанный в RFC 6749, изначально предполагал, что авторизационный код, полученный от сервера авторизации, может быть обменян на токен доступа только при предъявлении секретного ключа клиента (client_secret). Однако в публичных клиентах (например, в мобильном приложении) этот секрет невозможно скрыть — он может быть извлечён из бинарного кода или перехвачен в процессе работы. Это делало публичных клиентов уязвимыми к атаке, при которой злоумышленник перехватывает авторизационный код (например, через вредоносное приложение, установленное на том же устройстве, или через перехват URI перенаправления) и использует его для получения токена доступа.
В 2015 году инженеры компании Google и других организаций предложили решение этой проблемы, которое было стандартизировано в RFC 7636 в сентябре 2015 года. Механизм получил название PKCE (Proof Key for Code Exchange). Изначально он был разработан для мобильных приложений, но позже стал рекомендованным для всех типов публичных клиентов, включая одностраничные приложения (SPA) и нативные приложения.
Принцип работы
PKCE вводит два дополнительных параметра в процесс авторизации: code_verifier и code_challenge. Вместо того чтобы полагаться на секретный ключ клиента, PKCE использует криптографически сгенерированную пару значений, которая связывает конкретный запрос авторизации с последующим запросом на получение токена.
Этапы взаимодействия
- Генерация code_verifier. Клиент (приложение) создаёт случайную строку — code_verifier. Обычно это строка длиной от 43 до 128 символов, состоящая из букв, цифр и символов
-,.,_,~. Рекомендуется использовать криптостойкий генератор случайных чисел.
- Вычисление code_challenge. Клиент вычисляет code_challenge на основе code_verifier. Согласно RFC 7636, поддерживаются два метода:
- S256 (рекомендуемый): code_challenge = BASE64URL-кодированный SHA-256 хеш от code_verifier.
- plain (для обратной совместимости): code_challenge = code_verifier (без хеширования).
- Запрос авторизации. Клиент отправляет запрос на сервер авторизации, включая в него:
code_challenge— вычисленное значение.code_challenge_method— метод, использованный для вычисления (S256 или plain).
- Получение авторизационного кода. Сервер авторизации сохраняет
code_challengeиcode_challenge_methodвместе с выданным авторизационным кодом. Затем сервер перенаправляет пользователя обратно к клиенту с авторизационным кодом.
- Запрос на получение токена. Клиент отправляет на сервер авторизации запрос на обмен кода на токен, включая в него:
- Авторизационный код, полученный на предыдущем шаге.
code_verifier— исходную случайную строку.
- Проверка. Сервер авторизации вычисляет хеш от полученного
code_verifier(если метод S256) или использует его напрямую (если метод plain) и сравнивает результат с сохранённымcode_challenge. Если значения совпадают, сервер выдаёт токен доступа. Если нет — запрос отклоняется.
Ключевое отличие от стандартного потока
В стандартном потоке OAuth 2.0 с авторизационным кодом злоумышленник, перехвативший код, мог бы обменять его на токен, если бы он также знал client_secret (или если клиент не требовал client_secret). В PKCE даже если злоумышленник перехватывает авторизационный код, он не может обменять его на токен, так как не знает code_verifier, который был сгенерирован клиентом и не передавался по сети до момента обмена кода.
Варианты использования
Мобильные приложения
PKCE является обязательным требованием для мобильных приложений, использующих OAuth 2.0, согласно рекомендациям OAuth 2.0 for Native Apps (RFC 8252). В мобильных средах невозможно безопасно хранить client_secret, поэтому PKCE обеспечивает необходимый уровень защиты.
Одностраничные приложения (SPA)
Для одностраничных приложений, работающих в браузере, PKCE также рекомендуется. В таких приложениях client_secret не может быть скрыт от пользователя, так как весь код выполняется на стороне клиента. PKCE в сочетании с использованием Implicit Grant (который ранее был популярен для SPA) позволяет избежать уязвимостей, связанных с передачей токена доступа в URL.
Нативные десктопные приложения
Аналогично мобильным приложениям, нативные десктопные приложения, не имеющие серверного бэкенда, также используют PKCE для защиты авторизационного кода.
Безопасность и ограничения
Устойчивость к атакам
PKCE эффективно защищает от атаки с перехватом авторизационного кода, при которой злоумышленник получает доступ к коду через перехват URI перенаправления или через вредоносное приложение на том же устройстве. Однако PKCE не защищает от других типов атак, таких как:
- Атаки на клиентскую сторону (например, XSS в одностраничных приложениях, которые могут привести к краже токена).
- Атаки на сервер авторизации (например, компрометация сервера).
- Атаки на канал связи (если не используется HTTPS, злоумышленник может перехватить code_verifier при передаче).
Требования к реализации
Для корректной работы PKCE необходимо:
- Использовать криптостойкий генератор случайных чисел для создания code_verifier.
- Хранить code_verifier в безопасном месте на стороне клиента до момента обмена кода.
- Использовать HTTPS для всех запросов к серверу авторизации.
- Не передавать code_verifier в URL (он должен передаваться только в теле POST-запроса при обмене кода).
Ограничения
PKCE не решает проблему, связанную с возможностью злоумышленника получить токен доступа через вредоносное приложение, которое может перехватить URI перенаправления до того, как он будет обработан легитимным клиентом. В таких случаях требуется дополнительная защита, например, использование схемы URI с уникальным идентификатором приложения (App Claim) или использование системы проверки подлинности приложений на уровне операционной системы.
Критика и альтернативы
Критика
Некоторые эксперты отмечают, что PKCE добавляет сложность в реализацию OAuth 2.0, особенно для разработчиков, не знакомых с криптографией. Кроме того, существует мнение, что PKCE не является панацеей и должен использоваться в сочетании с другими мерами безопасности, такими как использование Proof of Possession (PoP) токенов или DPoP (Demonstration of Proof-of-Possession).
Альтернативы
В некоторых случаях вместо PKCE может использоваться Implicit Grant (поток с неявной передачей токена), который не требует обмена кода на токен. Однако Implicit Grant считается менее безопасным, так как токен доступа передаётся непосредственно в URL и может быть перехвачен. В современных рекомендациях (например, OAuth 2.0 Security Best Current Practice, RFC 9700) Implicit Grant не рекомендуется, и PKCE является предпочтительным методом для публичных клиентов.
Примеры реализации
Пример на JavaScript (для одностраничного приложения)
```javascript // Генерация code_verifier function generateCodeVerifier() { const array = new Uint8Array(32); crypto.getRandomValues(array); return base64urlencode(array); }
// Вычисление code_challenge (метод S256) async function generateCodeChallenge(verifier) { const encoder = new TextEncoder(); const data = encoder.encode(verifier); const hash = await crypto.subtle.digest('SHA-256', data); return base64urlencode(new Uint8Array(hash)); }
// Вспомогательная функция для BASE64URL-кодирования function base64urlencode(buffer) { const bytes = new Uint8Array(buffer); let binary = ''; bytes.forEach(b => binary += String.fromCharCode(b)); return btoa(binary) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); }
// Использование const codeVerifier = generateCodeVerifier(); const codeChallenge = await generateCodeChallenge(codeVerifier); // Далее codeChallenge передаётся в запрос авторизации ```
Пример на Python (для серверной части)
```python import hashlib import base64 import os
Генерация code_verifier
def generate_code_verifier(): return base64.urlsafe_b64encode(os.urandom(32)).rstrip(b'=').decode('utf-8')
Вычисление code_challenge
def generate_code_challenge(verifier): hash = hashlib.sha256(verifier.encode('utf-8')).digest() return base64.urlsafe_b64encode(hash).rstrip(b'=').decode('utf-8') ```
Стандартизация и нормативные документы
PKCE описан в следующих документах:
- RFC 7636 — Proof Key for Code Exchange by OAuth Public Clients (сентябрь 2015 года).
- RFC 8252 — OAuth 2.0 for Native Apps (октябрь 2017 года) — рекомендует использование PKCE для мобильных приложений.
- RFC 9700 — The OAuth 2.0 Security Best Current Practice (январь 2022 года) — рекомендует PKCE для всех публичных клиентов.
Влияние на экосистему OAuth 2.0
Внедрение PKCE существенно повысило безопасность публичных клиентов, использующих OAuth 2.0. Многие крупные платформы (Google, Microsoft, GitHub, Auth0) требуют использования PKCE для мобильных и одностраничных приложений. PKCE также стал обязательным элементом в стандарте OpenID Connect для публичных клиентов.
Источники
- RFC 7636: Proof Key for Code Exchange by OAuth Public Clients
- RFC 8252: OAuth 2.0 for Native Apps
- RFC 9700: The OAuth 2.0 Security Best Current Practice
- Документация OAuth 2.0 от Google, Microsoft, Auth0
- Статья «OAuth 2.0 for Mobile and Desktop Applications» (IETF)
- Книга «OAuth 2.0 in Action» (Justin Richer, Antonio Sanso)
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →