配置多重身份验证

本页介绍了如何配置多重身份验证 (MFA),以便通过电子邮件或短信发送验证码来验证用户的身份。使用此功能,您可以验证用户是否拥有与其帐号关联的电子邮件地址或电话号码。MFA 有助于保护您的用户免受凭据填充攻击和帐号盗用 (ATO) 攻击。

MFA 仅适用于基于得分的网站密钥,不适用于复选框网站密钥。

了解 MFA 的配置过程

reCAPTCHA Enterprise MFA 在常规的 reCAPTCHA Enterprise 工作流的基础上运行。概括来讲,MFA 工作流如下所示:

  1. 对客户端进行关键工作流程插桩
  2. 使用客户端发出的 execute() 调用返回的令牌创建评估。创建评估时,请包含 MFA 参数以获取 MFA requestToken
  3. 根据您要使用的渠道(短信或电子邮件),使用 requestToken 触发 MFA 质询
  4. 在您的网页或移动应用中验证最终用户输入的 PIN 码
  5. 使用验证请求返回的令牌来请求新的评估

准备工作

  1. 选择在您的环境中设置 reCAPTCHA Enterprise 的最佳方法,并完成设置。

  2. 安全审核后,可访问 MFA。请与我们的销售团队联系,在您的网站上启用此功能。 向销售团队提供以下新手入门信息:

    • Google Cloud 项目编号
    • 要初始配置的网站密钥
    • 平均 QPS(每秒发送的电子邮件/短信数)
    • QPS 峰值(每秒发送的电子邮件/短信数)
    • 对于电子邮件 MFA,您在测试期间所需的发件人地址、电子邮件地址或域名
    • 对于短信 MFA,每个目的地国家/地区(您的用户所在的国家/地区)的峰值和平均 QPS
  3. 按照平台专用说明,在要为其启用 MFA 的平台上集成 reCAPTCHA Enterprise:

在客户端上对关键工作流进行插桩

通过 execute() 函数将必要的信息传递给 reCAPTCHA Enterprise,以用于风险评估。execute() 函数返回生成令牌时解析的 promise。

网站

execute() 函数附加另一个 twofactor 参数,如以下示例代码所示:

grecaptcha.enterprise.execute(SITE_KEY, {
  action: 'login',
  twofactor: true
}).then(token => {
  // Handle the generated token.
});

Android

按照为您的 Android 应用生成令牌的说明执行操作。不需要其他参数。

iOS

按照为您的 iOS 应用生成令牌的说明执行操作。不需要其他参数。

申请评估

使用 execute() 函数生成的令牌,使用 reCAPTCHA Enterprise 客户端库或 Google Cloud CLI 从后端发出身份验证请求。如需了解如何申请评估,请参阅创建评估

提供经过哈希处理的帐号标识符以及一个或多个端点(例如电子邮件地址和电话号码),在评估中进行验证。经过哈希处理的帐号标识符是您的网站专属的用户帐号的匿名永久性标识符。使用稳定帐号标识符的单向哈希。我们建议将 sha256-hmac 结合不与我们共享的稳定密钥使用。

{
  "event": {
    "token": "token",
    "siteKey": "key",
    "hashedAccountId": "BP3ptt00D9W7UMzFmsPdEjNH3Chpi8bo40R6YW2b"
  },
  "accountVerification": {
    "endpoints": [{
      "emailAddress": "foo@bar.com",
    },
    {
      "phoneNumber": "+11111111111",
    }]
  }
}

如果与该功能的集成成功,则评估必须包含与 MFA 相关的更多信息,如以下示例所示:

{
  [...],
  "accountVerification": {
    "endpoints": [{
      "emailAddress": "foo@bar.com",
      "requestToken": "tplIUFvvJUIpLaOH0hIVj2H71t5Z9mDK2RhB1SAGSIUOgOIsBv",
      "lastVerificationTime": "",
    },
    {
      "phoneNumber": "+11111111111",
      "requestToken": "fwdgk0kcg1W0mbpetYlgTZKyrp4IHKzjgRkb6vLNZeBQhWdR3a",
      "lastVerificationTime": "",
    }],
    "latestVerificationResult": "RESULT_UNSPECIFIED"
  }
}

评估将包括设备上发布令牌的给定端点最近一次成功验证的日期和时间(如果有的话)。它还为每个端点包含一个 requestToken 字段,其中包含一个加密字符串。如果您决定触发该端点的 MFA 挑战,则必须将此加密的字符串发送回客户端。请求令牌的有效期为 15 分钟。

除了与 MFA 相关的信息之外,评估响应还包含与帐号卫士相关的信息。 recommended_action 字段显示触发 MFA 质询之前可以执行的操作。

以下示例展示了一个示例评估,其中显示建议跳过 MFA:

{
  [...],
  "accountDefenderAssessment": {
    labels: ["PROFILE_MATCH"],
    "recommended_action": "SKIP_2FA"
  }
}

recommended actions 字段可以包含以下任何值:

价值 说明
RECOMMENDED_ACTION_UNSPECIFIED 表示帐号卫士无法对此请求做出判断。
SKIP_2FA 表示帐号卫士认为在进行此项评估时跳过 MFA 是安全的。这通常表示,用户最近已在此设备上验证了您的网站。
REQUEST_2FA 表示您触发了用户的 MFA 质询。如需了解详情,请参阅帐号卫士评估回复

在客户端上触发 MFA 挑战

如果您已决定根据评估中所包含的信息挑战用户,则请将您要验证的端点的 MFA 请求令牌从评估发送回客户端。要触发 MFA 挑战,需要使用此请求令牌。

通过调用 challengeAccount() 触发 MFA 质询。challengeAccount() 函数返回一个在挑战完成后被解析或在出现错误或超时时被拒绝的 promise。完成后,系统会生成包含更新信息的新令牌,然后发送该令牌进行评估。

测试 MFA 集成

为了快速测试 MFA 集成,您可以通过提供以下值来调用 challengeAccount() 来触发 MFA 质询:

  • REQUEST_TOKEN_FROM_ASSESSMENT:评估响应中的 requestToken 字段的值。
  • CONTAINER_HTML_COMPONENT_ID:必须在其中呈现验证挑战的 HTML 组件的 ID。 如果您未指定此参数,则挑战会呈现在页面顶部的叠加层中。

以下示例展示了如何通过调用“challengeAccount()”来触发 MFA 质询:

grecaptcha.enterprise.challengeAccount(SITE_KEY, {
  'account-token': REQUEST_TOKEN_FROM_ASSESSMENT,
  'container': CONTAINER_HTML_COMPONENT_ID
}).then(newToken => {
  // Handle the new token.
});

如果 challengeAccount() 请求成功,系统会显示一个 HTML 组件以输入收到的 PIN 码。输入正确的 PIN 码后,系统会将 newToken 变量传递给包含判定令牌的链式函数,以便通过后端创建的评估对其进行验证。

网站

使用以下参数创建验证句柄并发起质询:

// Initialize verification handle.
const verificationHandle = grecaptcha.enterprise.eap.initTwoFactorVerificationHandle(
  SITE_KEY,
  REQUEST_TOKEN_FROM_ASSESSMENT
);

// Call the challenge API.
verificationHandle.challengeAccount().then(
  (challengeResponse) => {
    if (challengeResponse.isSuccess()) {
      // Handle success: Usually this means displaying an input for the user to
      // enter the pin that they received and then call the `verifyAccount(pin)`
      // method described below.
    } else {
      // Handle API failure
    }
  });

Android

通过从应用中的 Context 调用 challengeAccount() 来触发 MFA 挑战。

Recaptcha.getClient(this)
   .challengeAccount(recaptchaHandle, requestToken)
   .addOnSuccessListener(
       this,
       new OnSuccessListener<VerificationHandle>() {
         @Override
         public void onSuccess(VerificationHandle handle) {
           VerificationHandle verificationHandle = handle;
           // Handle success ...
         }
       })
   .addOnFailureListener(
       this,
       new OnFailureListener() {
         @Override
         public void onFailure(@NonNull Exception e) {
           if (e instanceof ApiException) {
              ApiException apiException = (ApiException) e;
              Status apiErrorStatus = apiException.getStatusCode();
              // Handle API errors ...
           } else {
              // Handle other failures ...
           }

         }
       });

iOS

调用 challengeAccount() 来触发 MFA 挑战。

recaptchaClient.challengeAccount(withRequestToken: "Request Token").then { verificationHandler in
  // Show your UI and retrieve the pin from the user.
}.catch { error in
  // Handle error.
}

从客户端验证 MFA 代码

获得最终用户的 PIN 码后,您必须验证 PIN 码是否正确:

网站

使用最终用户输入的 PIN 码调用 verificationHandle.verifyAccount() 以验证其是否正确。

verificationHandle.verifyAccount(pin).then(
  (verifyResponse) => {
    if (verifyResponse.isSuccess()) {
      // Handle success: Send the result of `verifyResponse.getVerdictToken()`
      // to the backend in order to determine if the code was valid.
    } else {
      // Handle API failure
    }
  },
  (error) => {
    // Handle other errors
  }
);

Android

从应用中的 Activity 调用 verifyAccount() 以验证输入的 PIN 码是否正确。

Recaptcha.getClient(this)
   .verifyAccount("userProvidedPIN", recaptchaHandle)
   .addOnSuccessListener(
       this,
       new OnSuccessListener<VerificationResult>() {
         @Override
         public void onSuccess(VerificationResult result) {
           if (result.getVerificationStatus().isSuccess()) {
             String recaptchaToken = result.recaptchaToken().orNull()
             // Handle success ...
           } else {
             VerificationHandle verificationHandle =
                              result.verificationHandle().orNull();
             Status errorStatus = result.getVerificationStatus();
             // Handle retries ...
           }
         }
       })
   .addOnFailureListener(
       this,
       new OnFailureListener() {
         @Override
         public void onFailure(@NonNull Exception e) {
           if (e instanceof ApiException) {
              ApiException apiException = (ApiException) e;
              Status apiErrorStatus = apiException.getStatusCode();
              // Handle API errors ...
           } else {
              // Handle other failures ...
           }
         }
       });

iOS

调用 verificationHandler 对象的 verifyPin() 方法即可完成验证。

handler.verifyPin("PIN") { recaptchaToken, recaptchaError in
  // Handle token/error.
}

请求新评估

通过经过哈希处理的帐号标识符和 endpoints 字段请求新的评估。

在客户端完成工作流后,您将获得一个新令牌,可以用来获取触发的验证结果。此评估包含有关最新成功验证的最近时间戳,以及成功结果状态。

以下示例显示了一个使用从客户端获得的新令牌请求新评估时收到的示例评估。

{
  [...],
  "accountVerification": {
    "endpoints": [{
      "emailAddress": "foo@bar.com",
      "requestToken": "tplIUFvvJUIpLaOH0hIVj2H71t5Z9mDK2RhB1SAGSIUOgOIsBv",
      "lastVerificationTime": "2020-03-23 08:27:12 PST",
    }],
    "latestVerificationResult": "SUCCESS_USER_VERIFIED"
  }
}

字段 latestVerificationResult 可以显示如下表中列出的不同的状态:

验证结果状态 说明
SUCCESS_USER_VERIFIED 已成功验证用户。
ERROR_USER_NOT_VERIFIED 用户未通过验证挑战。
ERROR_SITE_ONBOARDING_INCOMPLETE 您的网站未正确配置,因此无法使用此功能。
ERROR_RECIPIENT_NOT_ALLOWED 此收件人未获准向其发送短信或电子邮件(仅在测试期间)。
ERROR_RECIPIENT_ABUSE_LIMIT_EXHAUSTED 此收件人短时间内收到了过多的验证码。
ERROR_CUSTOMER_QUOTA_EXHAUSTED 您已超出可用的 MFA 配额。
ERROR_CRITICAL_INTERNAL 由于我们的系统出现内部错误,因此未完成验证。
RESULT_UNSPECIFIED 没有与最新验证相关的信息(从未验证)。