Tokeny dostępu OAuth 2.0 to klucze do twojego królestwa API — a jeśli ktoś je ukradnie, może ich używać z dowolnego miejsca. DPoP (Demonstrating Proof of Possession), zdefiniowany w RFC 9449, rozwiązuje ten problem przez kryptograficzne powiązanie tokenów z klientem, który je zażądał. Nawet jeśli token zostanie przechwycony, staje się bezużyteczny bez klucza prywatnego klienta.
Począwszy od sgcWebSockets 2026.4.0, komponenty klienta i serwera OAuth2 zawierają pełne wsparcie dla DPoP. Ten artykuł wyjaśnia, czym jest DPoP, jak działa i jak go skonfigurować w aplikacji Delphi.
Czym jest DPoP?
W standardowym OAuth 2.0 token Bearer działa jak gotówka — kto go posiada, może go używać. Jeśli atakujący przechwyci token przez skompromitowaną sieć, wyciek logów lub złośliwy serwer proxy, uzyskuje pełny dostęp do chronionych zasobów.
DPoP zmienia to, sprawiając że tokeny działają jak karta kredytowa z PIN-em. Token jest powiązany z kluczem publicznym, a każde żądanie musi zawierać proof JWT podpisany odpowiadającym kluczem prywatnym. Serwer weryfikuje, czy proof pasuje do powiązanego klucza, zanim zaakceptuje żądanie.
|
Token Bearer (tradycyjny) Każdy, kto posiada token, może go użyć. Skradzione tokeny są w pełni podatne na nadużycia. Nie jest wymagany żaden dowód tożsamości. |
Token DPoP (ograniczony do nadawcy) Token jest powiązany z parą kluczy. Każde żądanie wymaga świeżego podpisanego proofа. Skradzione tokeny są bezwartościowe bez klucza prywatnego. |
Jak działa DPoP
Przepływ DPoP dodaje lekki krok kryptograficzny do standardowego procesu OAuth2:
1. Generowanie klucza — Klient generuje asymetryczną parę kluczy (ES256 lub RS256). Klucz prywatny pozostaje na kliencie; klucz publiczny jest dołączany jako JWK w proofach DPoP.
2. Żądanie tokenu — Przy żądaniu tokenu dostępu klient dołącza nagłówek HTTP DPoP zawierający podpisany proof JWT. Proof zawiera metodę HTTP, URL, znacznik czasu i unikalny identyfikator.
3. Powiązanie tokenu — Serwer autoryzacji weryfikuje sygnaturę proofu, wyodrębnia odcisk JWK (skrót SHA-256 klucza publicznego) i wiąże go z wydanym tokenem. Odpowiedź zawiera token_type: DPoP zamiast Bearer.
4. Dostęp do zasobów — Przy każdym wywołaniu API klient wysyła zarówno token, jak i świeży proof DPoP. Proof zawiera roszczenie ath (skrót SHA-256 tokenu dostępu), wiążące proof z tym konkretnym tokenem.
Budowa proofu DPoP
Proof DPoP to kompaktowy JWT z trzema częściami: nagłówkiem, ładunkiem i sygnaturą.
// Header - identifies the key and algorithm
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "cWs37kZLJMej6fpd...",
"y": "e2bkcQGaBERgSZUb..."
}
}
// Payload - proves freshness and binds to the request
{
"htm": "POST",
"htu": "https://auth.example.com/oauth/token",
"iat": 1774950263,
"jti": "F1AFCD1F-95F7-401B-A2F5-195A31DB1802",
"ath": "fUHyO2r2Z3DZ53EsNr..."
}
Konfiguracja DPoP w sgcWebSockets
Krok 1: Generowanie pary kluczy ES256
Użyj OpenSSL, aby wygenerować parę kluczy krzywej eliptycznej:
# Generate EC private key
openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem
# Extract public key parameters
openssl ec -in dpop_private.pem -text -noout
Przekonwertuj współrzędne X i Y klucza publicznego do Base64URL, aby zbudować JWK:
{"kty":"EC","crv":"P-256","x":"cWs37kZLJMej6fpdyKaI8Gz6CE...","y":"e2bkcQGaBERgSZUbAGR-iOOM..."}
Krok 2: Konfiguracja klienta OAuth2
// Configure OAuth2 as usual
OAuth2.OAuth2Options.GrantType := auth2CodePKCE;
OAuth2.OAuth2Options.ClientId := 'your-client-id';
OAuth2.OAuth2Options.ClientSecret := 'your-client-secret';
OAuth2.AuthorizationServerOptions.AuthURL := 'https://auth.example.com/authorize';
OAuth2.AuthorizationServerOptions.TokenURL := 'https://auth.example.com/oauth/token';
// Enable DPoP
OAuth2.DPoPOptions.Enabled := True;
OAuth2.DPoPOptions.Algorithm := dpopES256;
OAuth2.DPoPOptions.PrivateKey.LoadFromFile('dpop_private.pem');
OAuth2.DPoPOptions.PublicKeyJWK := '{"kty":"EC","crv":"P-256","x":"...","y":"..."}';
// Start the OAuth2 flow - DPoP headers are added automatically
OAuth2.Start;
To wszystko. Komponent automatycznie:
- Generuje świeży proof JWT DPoP dla każdego żądania tokenu
- Obsługuje wyzwania serwera
DPoP-Noncei ponawia próby w sposób przezroczysty - Przechowuje nonce dla kolejnych żądań
Krok 3: Używanie proofów DPoP do wywołań API
Po uzyskaniu tokenu dostępu powiązanego z DPoP, generuj proof dla każdego żądania API:
var
vProof: String;
begin
// Generate a DPoP proof for the API call
vProof := OAuth2.GetDPoPProof(
'GET',
'https://api.example.com/userinfo',
OAuth2.AccessToken
);
// Include both headers in your HTTP request:
// Authorization: DPoP <access_token>
// DPoP: <proof_jwt>
HTTPClient.Request.CustomHeaders.AddValue('Authorization',
'DPoP ' + OAuth2.AccessToken);
HTTPClient.Request.CustomHeaders.AddValue('DPoP', vProof);
HTTPClient.Get('https://api.example.com/userinfo');
end;
Obsługiwani dostawcy
DPoP jest już obsługiwany przez głównych dostawców OAuth2:
| Auth0 | Włącz DPoP w ustawieniach aplikacji. Wymaga obsługi nonce (obsługiwanej automatycznie). |
| Okta | Skonfiguruj DPoP w politykach dostępu serwera autoryzacji. Dostępne ogólnie od 2024 r. |
| Microsoft Entra ID | Obsługuje DPoP dla klientów poufnych. |
| Ping Identity | Pełna obsługa DPoP w PingOne i PingFederate. |
Walidacja DPoP po stronie serwera
Komponent serwera OAuth2 dla sgcWebSockets obsługuje również walidację DPoP. Gdy OAuth2Options.DPoP jest włączone, serwer automatycznie:
- Weryfikuje proofу JWT DPoP (sygnatura, roszczenia, świeżość)
- Wiąże tokeny z odciskiem JWK klienta
- Zwraca
token_type: DPoPdla tokenów powiązanych z DPoP - Odrzuca żądania z nieprawidłowymi lub brakującymi proofami (zwraca 400
invalid_dpop_proof) - Weryfikuje roszczenie
ath(skrót tokenu dostępu) przy żądaniach do zasobów
// Enable DPoP on the OAuth2 server
OAuth2Server.OAuth2Options.DPoP := True;
// Optional: custom validation via event
OAuth2Server.OnOAuth2ValidateDPoP := procedure(Sender: TObject;
Connection: TsgcWSConnection;
const DPoPProof, AccessToken: String;
var IsValid: Boolean)
begin
// Add custom checks here (e.g., verify against a key registry)
IsValid := True;
end;
Dokumentacja API DPoP
| Właściwość / metoda | Opis |
|---|---|
DPoPOptions.Enabled |
Włącza DPoP dla wszystkich żądań tokenów. |
DPoPOptions.Algorithm |
dpopES256 (zalecany) lub dpopRS256. |
DPoPOptions.PrivateKey |
Klucz prywatny w formacie PEM do podpisywania proofów DPoP. |
DPoPOptions.PublicKeyJWK |
Reprezentacja klucza publicznego jako JSON Web Key. |
GetDPoPProof() |
Generuje proof JWT DPoP dla konkretnej metody HTTP, URL i tokenu dostępu. |
GetDPoPJWKThumbprint() |
Zwraca odcisk SHA-256 klucza publicznego zgodnie z RFC 7638. |
DPoPNonce |
Aktualna wartość DPoP-Nonce z serwera (tylko do odczytu). |
Zwiększ bezpieczeństwo tokenów
DPoP to najważniejsze usprawnienie bezpieczeństwa tokenów OAuth 2.0 od czasu PKCE. Eliminuje całą klasę ataków kradzieży tokenów przy minimalnych zmianach kodu — wystarczy ustawić DPoPOptions.Enabled := True, podać klucze, a komponent sgcWebSockets zajmie się resztą.
Obsługa DPoP jest dostępna w sgcWebSockets 2026.4.0 zarówno dla Delphi (DXE6 do D13), jak i .NET (.NET Framework 2.0 do .NET 9). Pobierz najnowszą wersję na esegece.com.
