OAuth2 DPoP Delphi

· 機能

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;

以上です。コンポーネントは自動的に以下を行います:

ステップ 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 が有効の場合、サーバーは自動的に以下を行います:

// 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 からダウンロードしてください。