# Captcha

{% hint style="info" %}
对应的[官方页面地址](https://contributing.bitwarden.com/architecture/deep-dives/captchas/)
{% endhint %}

{% hint style="info" %}
\[**译者注**]：[CAPTCHA](https://zh.wikipedia.org/zh-cn/%E9%AA%8C%E8%AF%81%E7%A0%81)，又称验证码。（Completely Automated Public Turing test to tell Computers and Humans Apart）全自动区分计算机和人类的图灵测试，是一种区分用户是机器或人类的公共全自动程序。
{% endhint %}

## 何时需要 Captcha？ <a href="#when-are-captchas-required" id="when-are-captchas-required"></a>

Bitwarden 代码库中的两个位置需要 Captcha：

* 通过 `[CapchaProtected]` 属性的中注册
* 通过 `ResourceOwnerPasswordValidator` 的登录

对于是否需要 Captcha，每种情况的验证都略有不同。

### 注册：\[CaptchaProtected] 匿名端点 <a href="#registration-captchaprotected-anonymous-endpoint" id="registration-captchaprotected-anonymous-endpoint"></a>

使用 `[CaptchaProtected]` 属性保护的唯一端点是身份服务上的 `/register` 端点。该端点是匿名的，因此没有身份验证中间件可以执行 Captcha 检查。

对于这些请求，如果满足以下任一条件，则服务器需要 Captcha：

* CloudFlare `x-Cf-Is-Bot` 标头出现在请求中，或者
* `ForceCaptchaRequired` 设置已启用

### 登录：使用资源所有者密码身份验证对令牌请求进行身份验证 <a href="#login-token-requests-authenticated-with-resource-owner-password-authentication" id="login-token-requests-authenticated-with-resource-owner-password-authentication"></a>

身份服务中的 `/identity/connect/token` 的请求通过 `ResourceOwnerPasswordValidator` 进行验证。在此验证器中，我们执行不同的检查以查看是否需要 Captcha，因为端点已通过身份验证并且我们从请求中了解用户（假设他们已成功通过身份验证）。

{% hint style="info" %}
对于已知设备，不要求 Captcha。此检查是在应用以下任何规则之前执行的。
{% endhint %}

对于这些请求，如果满足以下任一条件，则服务器需要 Captcha：

* CloudFlare `x-Cf-Is-Bot` 标头出现在请求中
* `ForceCaptchaRequired` 设置已启用
* 登录失败次数大于 `MaximumFailedLoginAttempts` 的设置
* 该请求针对的是云托管用户，其电子邮件未经验证，且在过去 24 小时内未进行过注册

CLI 会对 `bw login` 命令执行相同的 Captcha 检查，但不会提示 Captcha，而是接受 API 客户端机密。在下面的逻辑中，服务器会将 API 客户端机密作为「Captcha 响应」来处理。

## Captcha 在我们的代码中如何工作？ <a href="#how-do-captchas-work-in-our-code" id="how-do-captchas-work-in-our-code"></a>

在较高级别上，服务器**发起**并**验证** Captcha 请求，客户端通过**显示Captcha** 并**收集响应**以验证服务器端来处理该要求。

更详细地，该过程如下：

1. 服务器使用上面定义的规则接收需要 Captcha 的请求。
2. 然后，服务器响应包含 `HCaptcha_SiteKey` 的响应，客户端将其解释为「此请求需要 Captcha」。
3. 前端使用 `CaptchaProtectedComponent` 上的 `showCaptcha()` 方法根据此响应显示 Captcha `<div>` 。
4. `CaptchaProtectedComponent` 将 `CaptchaIFrame` 组件作为输入，其中包含实际的 Captcha UI。
5. `CaptchaProtectedComponent` 通过将 `captchaToken` 属性设置为 Captcha 响应中的值来处理 `CaptchaIFrame` 的 `successCallback`。
6. 然后，用户可以再次发起操作（登录、注册等）。`captchaToken` 将作为正文中的另一个元素与请求一起发送。
7. 服务器通过 `POST` 将包含令牌和我们的站点密钥的消息发送到 <https://hcaptcha.com/siteverify> 来验证 `HCaptchaValidationService` 中的请求。

### 什么是 Captcha 绕行令牌？ <a href="#what-is-a-captcha-bypass-token" id="what-is-a-captcha-bypass-token"></a>

在某些情况下，同一用户向受 Captcha 保护的端点之一发出多个请求。我们希望基于后续请求之前已经填写过 Captcha 的事实来「绕过」Captcha 请求，即使根据上述规则的定义，这些后续请求需要 Captcha。

我们使用 Captcha 绕行令牌来完成此操作。这是验证 Captcha 时在服务器上为第一个请求创建的令牌，它包含：

* 用户 ID
* 用户邮箱
* 到期日期（生成后 5 分钟）

由于 Captcha 绕行令牌是使用用户信息生成的，因此仅在登录的 Captcha 验证时对其进行检查。检查注册的 Captcha 验证没有意义，因为注册用户没有有效的用户 ID。

该令牌使用服务器上的数据保护密钥进行保护，并以 `BWCaptchaBypass_{tokenContents}` 格式在 `CaptchaBypassToken` 元素中返回。

然后，客户端可以在接下来的 5 分钟时间，对内来自同一用户的后续请求中使用该 `captchaResponse`，以绕过 Captcha 请求。服务器端代码处理此绕行令牌的方式与处理从 Captcha UI 显示中收集的 `captchaResponse` 的方式相同。

大多数用户流程中不需要 Captcha 绕行令牌，因为用户是从已知设备登录的。从已知设备登录会绕过所有 Captcha 请求，因此不需要令牌。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.ppgg.in/architecture/deep-dives/captchas.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
