WebAuthn 高级使用示例

· 组件
WebAuthn 高级使用示例 | eSeGeCe 博客

以下是一个更全面的 Delphi 示例,演示了自定义端点、挑战策略、数据库支持的凭据存储、FIDO 元数据验证和跨域 iframe 支持。代码重点展示了用于执行安全策略的高级事件处理。 

sgcWebSockets WebAuthn 服务器示例

procedure TForm1.ConfigureWebAuthn;
begin
  // 组件配置
  FWebAuthn := TsgcWSServer_API_WebAuthn.Create(nil);
  FWebAuthn.Server := FHTTPServer;
  FWebAuthn.Enabled := True;
  // 端点重映射
  FWebAuthn.EndpointsOptions.AuthenticationOptions.Endpoint := '/auth/options';
  FWebAuthn.EndpointsOptions.AuthenticationVerify.Endpoint  := '/auth/verify';
  FWebAuthn.EndpointsOptions.RegistrationOptions.Endpoint   := '/reg/options';
  FWebAuthn.EndpointsOptions.RegistrationVerify.Endpoint    := '/reg/verify';
  // 信赖方定义
  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;
    // 加密和用户体验策略
    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.
    // 元数据服务配置
    MDS.Enabled            := True;
    MDS.MDS_FileName       := 'mds.json';
    MDS.RootCert_FileName  := 'root.pem';
  end;
  // 绑定事件
  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
  // 验证用户是否有资格注册
  Accept := not UserExists(Request.Username);
end;
procedure TForm1.AuthnRegOptionsResponse(Sender: TObject;
  const Request: TsgcWebAuthn_RegistrationOptions_Request;
  const Response: TsgcWebAuthn_RegistrationOptions_Response);
begin
  // 可选:分配用户句柄(二进制标识符)
  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
  // 对 MDS 条目进行额外的认证验证
  Accept := ValidateAttestationWithMDS(Validate);
end;
procedure TForm1.AuthnRegSuccess(Sender: TObject;
  const Registration: TsgcWebAuthn_Registration;
  const CredentialRecord: TsgcWebAuthn_CredentialRecord; var Accept: Boolean);
begin
  // 将凭据详细信息持久化到数据库
  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
  // 检索用户的所有凭据记录
  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
  // 确保签名计数器递增
  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 字节的密码学安全随机数,以十六进制编码,无需任何额外配置即可抵御重放攻击。
  2. 自定义用户句柄 – 分配唯一的二进制用户句柄允许认证器存储与用户名无关的隐私保护标识符。
  3. 基于元数据的认证验证 – ValidateAttestationWithMDS 例程交叉验证认证器型号、状态报告和吊销列表,确保只有受信任的设备才能注册。
  4. 签名计数器强制执行 – AuthnSuccess 拒绝未严格递增认证器计数器的响应,检测克隆的凭据。
  5. 数据库集成 – 凭据数据、签名计数器和会话令牌通过外部持久化函数存储和更新,演示了如何将组件与实际后端集成。
  6. 跨域 Iframe 支持 – 通过 AllowCrossOrigins 和配置的 TopOrigins 启用,允许从嵌入的框架(例如不同域上的登录部件)发起 WebAuthn 流程。
  7. 认证策略 – 直接认证结合 MDS 确保只有经批准的认证器才能注册,适用于企业合规场景。
  8. 传输选择 – 虽然未显示,但事件可以约束可接受的传输(例如 USB,NFC,BLE)以定制允许的认证器类型。