WebAuthn 高度な使用例

· コンポーネント
WebAuthn 高度な使用例 | eSeGeCe ブログ

以下は、カスタムエンドポイント・チャレンジポリシー・データベース連携の資格情報ストレージ・FIDO メタデータ検証・クロスオリジン iframe サポートを示す包括的な Delphi サンプルです。セキュリティポリシーを適用するための高度なイベント処理を示しています。 

sgcWebSockets WebAuthn サーバーサンプル

procedure TForm1.ConfigureWebAuthn;
begin
  // Component setup
  FWebAuthn := TsgcWSServer_API_WebAuthn.Create(nil);
  FWebAuthn.Server := FHTTPServer;
  FWebAuthn.Enabled := True;
  // Endpoint remapping
  FWebAuthn.EndpointsOptions.AuthenticationOptions.Endpoint := '/auth/options';
  FWebAuthn.EndpointsOptions.AuthenticationVerify.Endpoint  := '/auth/verify';
  FWebAuthn.EndpointsOptions.RegistrationOptions.Endpoint   := '/reg/options';
  FWebAuthn.EndpointsOptions.RegistrationVerify.Endpoint    := '/reg/verify';
  // Relying-party definition
  with FWebAuthn.WebAuthnOptions do
  begin
    RelyingParty := 'secure.example.com';
    Origins.Origins.Text      := 'https://app.example.com'#13#10'https://login.example.net';
    Origins.TopOrigins.Text   := 'https://host.example.org';
    Origins.AllowCrossOrigins := True;
    // Cryptographic & UX policies
    Algorithms                                   := [waunalgES256, waunalgRS256];
    DefaultOptions.Registration.UserVerification := waunuvPreferred;
    DefaultOptions.Registration.Attestation      := waunaDirect;
    Timeout                                      := 60000;
    // Challenge: 32 CSPRNG-random bytes, hex-encoded, generated internally by
    // the server; not configurable. Override Response.Challenge instead, in
    // OnWebAuthnRegistrationOptionsResponse / OnWebAuthnAuthenticationOptionsResponse.
    // Metadata Service configuration
    MDS.Enabled            := True;
    MDS.MDS_FileName       := 'mds.json';
    MDS.RootCert_FileName  := 'root.pem';
  end;
  // Hook events
  FWebAuthn.OnWebAuthnRegistrationOptionsRequest := AuthnRegOptionsRequest;
  FWebAuthn.OnWebAuthnRegistrationOptionsResponse := AuthnRegOptionsResponse;
  FWebAuthn.OnWebAuthnRegistrationValidateCertificate := AuthnRegVerify;
  FWebAuthn.OnWebAuthnRegistrationSuccessful     := AuthnRegSuccess;
  FWebAuthn.OnWebAuthnAuthenticationOptionsRequest := AuthnOptionsRequest;
  FWebAuthn.OnWebAuthnAuthenticationSuccessful     := AuthnSuccess;
end;

イベント実装

procedure TForm1.AuthnRegOptionsRequest(Sender: TObject;
  const Request: TsgcWebAuthn_RegistrationOptions_Request; var Accept: Boolean);
begin
  // Verify user is eligible for registration
  Accept := not UserExists(Request.Username);
end;
procedure TForm1.AuthnRegOptionsResponse(Sender: TObject;
  const Request: TsgcWebAuthn_RegistrationOptions_Request;
  const Response: TsgcWebAuthn_RegistrationOptions_Response);
begin
  // Optionally assign a user handle (binary identifier)
  Response.User.Id := HexToBin(UserGUIDToHex(GenerateGUID));
  Response.AuthenticatorSelection.AuthenticatorAttachment := 'platform';
end;
procedure TForm1.AuthnRegVerify(Sender: TObject;
  const Request: TsgcWebAuthn_RegistrationVerify_Request;
  const Validate: TsgcWebAuthnValidateAttestationStatement; var Accept: Boolean);
begin
  // Perform extra attestation validation against MDS entries
  Accept := ValidateAttestationWithMDS(Validate);
end;
procedure TForm1.AuthnRegSuccess(Sender: TObject;
  const Registration: TsgcWebAuthn_Registration;
  const CredentialRecord: TsgcWebAuthn_CredentialRecord; var Accept: Boolean);
begin
  // Persist credential details in database
  SaveCredentialToDB(
    CredentialRecord.Username,
    CredentialRecord.CredentialId,
    CredentialRecord.PublicKey,
    CredentialRecord.SignCount,
    CredentialRecord.UserId
  );
  Accept := True;
end;
procedure TForm1.AuthnOptionsRequest(Sender: TObject;
  const Request: TsgcWebAuthn_AuthenticationOptions_Request;
  var CredentialRecords: TsgcWebAuthn_CredentialRecords; var Accept: Boolean);
begin
  // Retrieve all credential records for user
  CredentialRecords := LoadCredentialRecordsFromDB(Request.Username);
  Accept := True;
end;
procedure TForm1.AuthnSuccess(Sender: TObject;
  const Request: TsgcWebAuthn_AuthenticationVerify_Request;
  const Authentication: TsgcWebAuthn_Authentication; var Accept: Boolean);
var
  StoredCounter: Cardinal;
begin
  // Ensure sign counter increases
  StoredCounter := GetSignCounterFromDB(Authentication.Credential.CredentialRecord.CredentialId);
  Accept := Authentication.Credential.CredentialRecord.SignCount > StoredCounter;
  if Accept then
  begin
    UpdateSignCounterInDB(Authentication.Credential.CredentialRecord.CredentialId,
      Authentication.Credential.CredentialRecord.SignCount);
    IssueSessionToken(Authentication.Credential.CredentialRecord.Username);
  end;
end;

重要なポイント

  1. チャレンジのランダム性 – すべてのチャレンジはサーバー内部で生成される暗号学的に安全な32バイトの乱数で、16進数でエンコードされます。追加の設定なしにリプレイ攻撃への耐性を備えています。
  2. カスタムユーザーハンドル – 一意のバイナリユーザーハンドルを割り当てることで、オーセンティケーターがユーザー名とは独立したプライバシー保護識別子を保存できます。
  3. メタデータベースの構成証明検証 – ValidateAttestationWithMDS ルーティンがオーセンティケーターモデル・ステータスレポート・失効リストを照合し、信頼済みデバイスのみが登録されるようにします。
  4. 署名カウンター強制 – AuthnSuccess はオーセンティケーターのカウンターを厳密にインクリメントしないレスポンスを拒否し、クローン認証情報を検出します。
  5. データベース統合 – 資格情報データ・署名カウンター・セッショントークンが外部永続化関数を介して保存・更新され、実際のバックエンドとコンポーネントを統合する方法を示しています。
  6. クロスオリジン iframe サポート – AllowCrossOrigins と設定された TopOrigins によって有効化され、埋め込みフレーム(例:別ドメインのログインウィジェット)から開始される WebAuthn フローを許可します。
  7. 構成証明ポリシー – MDS と組み合わせた直接構成証明により、承認済みのオーセンティケーターのみが登録でき、エンタープライズコンプライアンスシナリオに有用です。
  8. トランスポート選択 – 未表示ですが、イベントで許容されるトランスポート(例:USB,NFC,BLE)を制限し、許可するオーセンティケーターの種類を調整できます。