OAuth 2.0 PKCE

Co to jest

PKCE (Proof Key for Code Exchange) to rozszerzenie schematu OAuth 2.0 Authorization Code flow. Ten schemat jest przeznaczony dla aplikacji niezaufanych, które nie mają jak przechowywać sekretów w sposób bezpieczny.
PKCE wiąże żądanie o token z aplikacją, która rozpoczęła logowanie. Chroni to przed wykorzystaniem przechwyconego kodu autoryzacyjnego zwiększając bezpieczeństwo chronionych zasobów.

Jak działa Authorization Code z PKCE

Przed logowaniem aplikacja tworzy losowy, tajny code_verifier, a z niego wylicza code_challenge — zwykle metodą S256 (SHA-256 i kodowanie Base64URL). Wysyła challenge do serwera autoryzacji razem z żądaniem logowania. Po zalogowaniu dostaje kod autoryzacyjny, a następnie wymienia go na token, przesyłając oryginalny verifier. Serwer sprawdza, czy verifier pasuje do zapisanego challenge. Bez niego sam kod nie wystarczy do uzyskania tokenu. RFC 7636

Diagram obrazujący ten schemat

┌────────┐ ┌───────────────────┐
│ │ │ Server │
│ │ │ autoryzacyjny │
│ │ │ ┌───────────────┐ │
│ │──(A)─ Authorization Request ────►│ │ │
│ │ + t(code_verifier), t_m │ │ Endpoint │ │
│ │ │ │ auroryzacyjny │ │
│ │◄─(B)──── Authorization Code ─────│ │ │
│ │ │ └───────────────┘ │
│ Klient │ │ │
│ │ │ ┌───────────────┐ │
│ │──(C)── Access Token Request ────►│ │ │
│ │ + code_verifier │ │ Endpoint │ │
│ │ │ │ wymiany kodu │ │
│ │ │ │ na token │ │
│ │◄─(D)────── Access Token ─────────│ │ │
│ │ │ └───────────────┘ │
└────────┘ └───────────────────┘

Oznaczenia kroków w diagramie odnoszą się do opisów w RFC. Tam można doczytać szczegóły.

Schemat ataku

W opisanym w RFC scenariuszu złośliwa aplikacja na urządzeniu rejestruje ten sam własny schemat przekierowania co właściwa aplikacja. Może wtedy przechwycić zwracany kod i spróbować wymienić go na token.

Schemat ataku

┌────────────────────────────────┐ ┌──────────┐
│ Urządzenie końcowe │ │ │
│ (np. smartfon) │ │ │
│ │ │ │
│ ┌─────────────┐ ┌──────────┐ │ │ │
│ │ Uprawniona │ │ Złośliwa │ │ (6) Access Token │ │
│ │ aplikacja │ │ aplikacja│◄────────────────────│ │
│ │ OAuth 2.0 │ │ │────────────────────►│ │
│ └─────────────┘ └──────────┘ │ (5) Authorization │ │
│ │ ▲ ▲ │ Grant │ │
│ │ ╲ │ │ │ │
│ │ ╲ (4) │ │ │ Server │
│ (1) │ ╲ Authz │ │ │ autory- │
│ Authz│ ╲ Code │ │ │ zacyjny │
│ Request│ ╲ │ │ │ │
│ │ ╲ │ │ │ │
│ ▼ ╲ │ │ │ │
│ ┌────────────────────────────┐ │ │ │
│ │ │ │ (3) Authz Code │ │
│ │ System operacyjny/ │◄────────────────────│ │
│ │ Przeglądarka │────────────────────►│ │
│ │ │ │ (2) Authz Request │ │
│ └────────────────────────────┘ │ │ │
└────────────────────────────────┘ └──────────┘

PKCE zatrzymuje ten scenariusz na etapie wymiany: złośliwa aplikacja może zdobyć kod, ale nie zna code_verifier utworzonego przez właściwą aplikację. Dlatego serwer odrzuca jej żądanie o token.

Demo

Przygotowałem demo tego flow. Możesz obserwować poszczególne kroki procesu autoryzacji od rejestracji użytkownika do uzyskania autoryzacji do chronionego zasobu. Po klinięciu klawisza uruchomisz akcję np. wygenerowanie code_challenge i code_verifieri zobaczysz efekt, np. konfigurację, zdekodowany token JWT. Aplikacja pracuje na moim klastrze Kubernetes.

W demo wykorzystałem:

  • Usługę Azure EntraID
    • Musisz się zarejestrować w schemacie passwordless. Na email dostaniesz kod weryfikacyjny do logowania.
  • SPA klienta
    • Jest to aplikacja wykonanana jako prosty html. Dodatkowo wykorzystana jest biblioteka MSAL.js przygotowana przez Microsoft do obsługi Oauth2.
  • API z chronionym zasobem
    • Minimal API chronione JWT.

Backend API jest “w drodze”. Pewnie będzie gotowe po południu. Tymczasem mam inne zadania 🙂 Dlatego ostatni krok domo zwróci błąd. Niemniej flow uzyskiwania tokenu dostępowego działa.

Sprawdź jak działa Authorization Code flow z PKCE. Uruchom demo