OAuth 2.0 アクセストークンはあなたの API 王国への鍵です。誰かが盗んだ場合、どこからでも使用できます。RFC 9449 で定義された DPoP(Demonstrating Proof of Possession)は、トークンをリクエストしたクライアントに暗号的に結び付けることでこの問題を解決します。トークンが傍受されても、クライアントの秘密鍵なしでは使用できません。
sgcWebSockets 2026.4.0 から、OAuth2 クライアントとサーバーコンポーネントに完全な DPoP サポートが含まれます。本記事では DPoP とは何か・仕組み・Delphi アプリケーションでの設定方法を説明します。
DPoP とは?
標準の OAuth 2.0 では、Bearer トークンは現金のように機能します — 持っている人なら誰でも使用できます。攻撃者が侵害されたネットワーク・漏洩したログ・悪意のあるプロキシ経由でトークンを傍受した場合、保護されたリソースへの完全なアクセスを得ます。
DPoP はトークンを PIN 付きクレジットカードのように機能させることでこれを変えます。トークン自体は公開鍵に結び付けられ、すべてのリクエストには対応する秘密鍵で署名されたプルーフ JWT が必要です。サーバーはリクエストを受け入れる前にプルーフが結び付けられた鍵と一致することを検証します。
|
Bearer トークン(従来型) トークンを持っている人なら誰でも使用できます。盗まれたトークンは完全に悪用可能です。身元証明は不要。 |
DPoP トークン(送信者制約型) トークンはキーペアに結び付けられています。すべてのリクエストに新鮮な署名済みプルーフが必要です。秘密鍵なしでは盗まれたトークンは無価値です。 |
DPoP の仕組み
DPoP フローは標準 OAuth2 プロセスに軽量な暗号化ステップを追加します:
1. キー生成 — クライアントが非対称キーペア(ES256 または RS256)を生成します。秘密鍵はクライアントに保持され、公開鍵は DPoP プルーフに JWK として含まれます。
2. トークンリクエスト — アクセストークンをリクエストする際、クライアントは署名済み JWT プルーフを含む DPoP HTTP ヘッダーを含めます。プルーフには HTTP メソッド・URL・タイムスタンプ・一意の識別子が含まれます。
3. トークンバインディング — 認可サーバーはプルーフ署名を検証し、JWK サムプリント(公開鍵の SHA-256 ハッシュ)を抽出して発行されたトークンに結び付けます。レスポンスには Bearer の代わりに token_type: DPoP が含まれます。
4. リソースアクセス — すべての API 呼び出しで、クライアントはトークンと新鮮な DPoP プルーフの両方を送信します。プルーフには ath クレーム(アクセストークンの SHA-256 ハッシュ)が含まれ、プルーフをその特定のトークンに結び付けます。
DPoP プルーフの内部
DPoP プルーフは 3 つの部分(ヘッダー・ペイロード・署名)を持つコンパクトな JWT です。
// 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..."
}
sgcWebSockets での DPoP の設定
ステップ 1:ES256 キーペアの生成
OpenSSL を使用して楕円曲線キーペアを生成します:
# 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
公開鍵の X と Y 座標を Base64URL に変換して JWK を構築します:
{"kty":"EC","crv":"P-256","x":"cWs37kZLJMej6fpdyKaI8Gz6CE...","y":"e2bkcQGaBERgSZUbAGR-iOOM..."}
ステップ 2: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;
以上です。コンポーネントは自動的に以下を行います:
- すべてのトークンリクエストに対して新鮮な DPoP プルーフ JWT を生成します
DPoP-Nonceサーバーチャレンジを透過的に処理して再試行します- 後続のリクエスト用にノンスを保存します
ステップ 3:API 呼び出しへの DPoP プルーフの使用
DPoP バインドのアクセストークンを取得した後、各 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;
サポートされるプロバイダー
DPoP はすでに主要な OAuth2 プロバイダーでサポートされています:
| Auth0 | アプリケーション設定で DPoP を有効にします。ノンスサポートが必要です(自動的に処理されます)。 |
| Okta | 認可サーバーのアクセスポリシーで DPoP を設定します。2024 年より GA です。 |
| Microsoft Entra ID | 機密クライアント向けの DPoP をサポートします。 |
| Ping Identity | PingOne と PingFederate で完全な DPoP サポート。 |
サーバー側 DPoP 検証
sgcWebSockets OAuth2 サーバーコンポーネントも DPoP 検証をサポートします。OAuth2Options.DPoP が有効の場合、サーバーは自動的に以下を行います:
- DPoP プルーフ JWT を検証します(署名・クレーム・鮮度)
- トークンをクライアントの JWK サムプリントに結び付けます
- DPoP バインドのトークンに
token_type: DPoPを返します - 無効または欠落したプルーフのリクエストを拒否します(400
invalid_dpop_proofを返す) - リソースリクエストの
athクレーム(アクセストークンハッシュ)を検証します
// 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;
DPoP API リファレンス
| プロパティ / メソッド | 説明 |
|---|---|
DPoPOptions.Enabled |
すべてのトークンリクエストに対して DPoP を有効にします。 |
DPoPOptions.Algorithm |
dpopES256(推奨)または dpopRS256。 |
DPoPOptions.PrivateKey |
DPoP プルーフの署名用 PEM エンコードの秘密鍵。 |
DPoPOptions.PublicKeyJWK |
公開鍵の JSON Web Key 表現。 |
GetDPoPProof() |
特定の HTTP メソッド・URL・アクセストークン用の DPoP プルーフ JWT を生成します。 |
GetDPoPJWKThumbprint() |
公開鍵の RFC 7638 SHA-256 サムプリントを返します。 |
DPoPNonce |
サーバーからの現在の DPoP-Nonce 値(読み取り専用)。 |
トークンセキュリティのアップグレード
DPoP は PKCE 以来の OAuth 2.0 トークンセキュリティにおける最も重要な改善です。最小限のコード変更でトークン盗難攻撃の全クラスを排除します — DPoPOptions.Enabled := True を設定し、キーを提供するだけで、sgcWebSockets コンポーネントが残りを処理します。
DPoP サポートは Delphi(DXE6 〜 D13)と .NET(.NET Framework 2.0 〜 .NET 9)の両方で sgcWebSockets 2026.4.0 から利用可能です。最新バージョンは esegece.com からダウンロードしてください。
