检测密码泄露和已泄露的凭据

本页介绍了如何使用 reCAPTCHA 的密码泄露检测功能 检测密码泄露和被盗用的凭据, 从而防止账号被盗 (ATO) 和凭据填充攻击。包含 您可以定期 在任何评估过程中对用户凭据(密码)进行审核,以确保 它们没有泄露或遭到破坏。为了进行这些评估,Google 会使用 密码安全检查功能。

准备工作

  1. 为 reCAPTCHA 准备好环境

  2. 设置 reCAPTCHA

  3. Make sure that billing is enabled for your Google Cloud project.

    reCAPTCHA 要求在项目上关联并启用结算功能才能使用密码泄露检测功能。您可以使用信用卡或 现有 Google Cloud 项目的结算 ID。如果您需要结算方面的帮助, 请与 Cloud Billing 支持团队联系。

检查是否存在已泄露和泄露的凭据

您可以使用加密函数或 Docker 容器来检查一组凭据是否已泄露。

Docker 容器是一个开源客户端,用于实现安全多方计算,以保护最终用户隐私并安全地查找密码泄露。如需了解详情,请参阅 GitHub 代码库。 Docker 容器 简化了实现加密的复杂性 并简化安装过程。它还可以托管 部署容器应用

加密函数

如需检查一组凭据是否已被泄露,请使用密码泄露检测功能 在针对登录、密码更改和 密码重置。

如需检查是否存在密码泄露和被盗用的凭据,请完成以下步骤:

  1. 生成请求参数
  2. 创建评估以检测密码泄露情况
  3. 验证评估中泄露的凭据
  4. 解读判定结果并采取行动

生成请求参数

  1. 使用 高隐私协议所要求的所有加密功能。 reCAPTCHA 提供 Java 和 TypeScript 库, 生成以下字段:

  2. 若要创建密码检查验证,请创建一个 PasswordCheckVerifier 对象。

    PasswordCheckVerifier verifier = new PasswordCheckVerifier();
    
  3. 如需发起验证,请调用 PasswordCheckVerifier#createVerification。此方法使用用户名和密码来计算 执行密码检查。

    PasswordCheckVerification verification = verifier.createVerification("username", "password").get();
    
  4. 创建 评估 验证这些技术

    byte[] lookupHashPrefix = verification.getLookupHashPrefix();
    byte[] encryptedUserCredentialsHash = verification.getEncryptedUserCredentialsHash();
    

    字节数组 lookupHashPrefixencryptedUserCredentialsHash 包含发起密码检查 Assessment 所需的参数。

创建评估以检测密码泄露

使用 projects.assessments.create 方法。

在使用任何请求数据之前,请先进行以下替换:

  • PROJECT_ID:您的 Google Cloud 项目 ID
  • LOOKUP_HASH_PREFIX:用户名 SHA-256 哈希前缀
  • ENCRYPTED_USER_CREDENTIALS_HASH:已加密的用户凭据 Scrypt 哈希

HTTP 方法和网址:

POST https://recaptchaenterprise.googleapis.com/v1/projects/PROJECT_ID/assessments

请求 JSON 正文:

{
  "private_password_leak_verification": {
    "lookup_hash_prefix": "LOOKUP_HASH_PREFIX",
    "encrypted_user_credentials_hash": "ENCRYPTED_USER_CREDENTIALS_HASH"
  }
}

如需发送请求,请选择以下方式之一:

curl

将请求正文保存在名为 request.json 的文件中,然后执行以下命令:

curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json; charset=utf-8" \
-d @request.json \
"https://recaptchaenterprise.googleapis.com/v1/projects/PROJECT_ID/assessments"

PowerShell

将请求正文保存在名为 request.json 的文件中,然后执行以下命令:

$cred = gcloud auth print-access-token
$headers = @{ "Authorization" = "Bearer $cred" }

Invoke-WebRequest `
-Method POST `
-Headers $headers `
-ContentType: "application/json; charset=utf-8" `
-InFile request.json `
-Uri "https://recaptchaenterprise.googleapis.com/v1/projects/PROJECT_ID/assessments" | Select-Object -Expand Content

您应该收到类似以下内容的 JSON 响应:

{
  "name": "projects/698047609967/assessments/fb22000000000000",
  "score": 0,
  "reasons": [],
  "privatePasswordLeakVerification": {
    "lookupHashPrefix": "zoxZwA==",
    "encryptedUserCredentialsHash": "AyRihRcKaGLj/FA/r2uqQY/fzfTaDb/nEcIUMeD3Tygp",
    "reencryptedUserCredentialsHash": "Aw65yEbLM39ww1ridDEfx5VhkWo11tzn/R1B88Qqwr/+"
    "encryptedLeakMatchPrefixes": [
      "n/n5fvPD6rmQPFyb4xk=", "IVQqzXsbZenaibID6OI=", ..., "INeMMndrfnlf6osCVvs=",
      "MkIpxt2x4mtyBnRODu0=", "AqUyAUWzi+v7Kx03e6o="]
  }
}

验证评估中泄露的凭据

从评估响应中提取字段 reEncryptedUserCredentialsencryptedLeakMatchPrefixes,并将它们传递给 验证程序对象,以确定凭据是否已泄露。

PasswordCheckResult result = verifier.verify(verification,
result.getReEncryptedUserCredentials(),
result.getEncryptedLeakMatchPrefixes()
).get();

System.out.println("Credentials leaked: " + result.areCredentialsLeaked());

代码示例

了解如何使用 TypeScript,请参阅 GitHub 上的 TypeScript 代码示例

以下代码示例展示了如何使用 Java 实现密码泄露检测:

Java

如需向 reCAPTCHA 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证


import com.google.cloud.recaptcha.passwordcheck.PasswordCheckResult;
import com.google.cloud.recaptcha.passwordcheck.PasswordCheckVerification;
import com.google.cloud.recaptcha.passwordcheck.PasswordCheckVerifier;
import com.google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseServiceClient;
import com.google.protobuf.ByteString;
import com.google.recaptchaenterprise.v1.Assessment;
import com.google.recaptchaenterprise.v1.CreateAssessmentRequest;
import com.google.recaptchaenterprise.v1.PrivatePasswordLeakVerification;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import org.bouncycastle.util.encoders.Base64;

public class CreatePasswordLeakAssessment {

  public static void main(String[] args)
      throws IOException, ExecutionException, InterruptedException {
    // TODO(developer): Replace these variables before running the sample.
    // Google Cloud Project ID.
    String projectID = "project-id";

    // Username and password to be checked for credential breach.
    String username = "username";
    String password = "password";

    checkPasswordLeak(projectID, username, password);
  }

  /*
   * Detect password leaks and breached credentials to prevent account takeovers
   * (ATOs) and credential stuffing attacks.
   * For more information, see:
   * https://cloud.google.com/recaptcha-enterprise/docs/check-passwords and
   * https://security.googleblog.com/2019/02/protect-your-accounts-from-data.html

   * Steps:
   * 1. Use the 'create' method to hash and Encrypt the hashed username and
   * password.
   * 2. Send the hash prefix (26-bit) and the encrypted credentials to create
   * the assessment.(Hash prefix is used to partition the database.)
   * 3. Password leak assessment returns a list of encrypted credential hashes to
   * be compared with the decryption of the returned re-encrypted credentials.
   * Create Assessment also sends back re-encrypted credentials.
   * 4. The re-encrypted credential is then locally verified to see if there is
   * a match in the database.
   *
   * To perform hashing, encryption and verification (steps 1, 2 and 4),
   * reCAPTCHA Enterprise provides a helper library in Java.
   * See, https://github.com/GoogleCloudPlatform/java-recaptcha-password-check-helpers

   * If you want to extend this behavior to your own implementation/ languages,
   * make sure to perform the following steps:
   * 1. Hash the credentials (First 26 bits of the result is the
   * 'lookupHashPrefix')
   * 2. Encrypt the hash (result = 'encryptedUserCredentialsHash')
   * 3. Get back the PasswordLeak information from
   * reCAPTCHA Enterprise Create Assessment.
   * 4. Decrypt the obtained 'credentials.getReencryptedUserCredentialsHash()'
   * with the same key you used for encryption.
   * 5. Check if the decrypted credentials are present in
   * 'credentials.getEncryptedLeakMatchPrefixesList()'.
   * 6. If there is a match, that indicates a credential breach.
   */
  public static void checkPasswordLeak(
      String projectID, String username, String password)
      throws ExecutionException, InterruptedException, IOException {

    // Instantiate the java-password-leak-helper library to perform the cryptographic functions.
    PasswordCheckVerifier passwordLeak = new PasswordCheckVerifier();

    // Create the request to obtain the hash prefix and encrypted credentials.
    PasswordCheckVerification verification =
        passwordLeak.createVerification(username, password).get();

    byte[] lookupHashPrefix = Base64.encode(verification.getLookupHashPrefix());
    byte[] encryptedUserCredentialsHash = Base64.encode(
        verification.getEncryptedUserCredentialsHash());

    // Pass the credentials to the createPasswordLeakAssessment() to get back
    // the matching database entry for the hash prefix.
    PrivatePasswordLeakVerification credentials =
        createPasswordLeakAssessment(
            projectID,
            lookupHashPrefix,
            encryptedUserCredentialsHash);

    // Convert to appropriate input format.
    List<byte[]> leakMatchPrefixes =
        credentials.getEncryptedLeakMatchPrefixesList().stream()
            .map(x -> Base64.decode(x.toByteArray()))
            .collect(Collectors.toList());

    // Verify if the encrypted credentials are present in the obtained match list.
    PasswordCheckResult result =
        passwordLeak
            .verify(
                verification,
                Base64.decode(credentials.getReencryptedUserCredentialsHash().toByteArray()),
                leakMatchPrefixes)
            .get();

    // Check if the credential is leaked.
    boolean isLeaked = result.areCredentialsLeaked();
    System.out.printf("Is Credential leaked: %s", isLeaked);
  }

  // Create a reCAPTCHA Enterprise assessment.
  // Returns:  PrivatePasswordLeakVerification which contains
  // reencryptedUserCredentialsHash and credential breach database
  // whose prefix matches the lookupHashPrefix.
  private static PrivatePasswordLeakVerification createPasswordLeakAssessment(
      String projectID,
      byte[] lookupHashPrefix,
      byte[] encryptedUserCredentialsHash)
      throws IOException {
    try (RecaptchaEnterpriseServiceClient client = RecaptchaEnterpriseServiceClient.create()) {

      // Set the hashprefix and credentials hash.
      // Setting this will trigger the Password leak protection.
      PrivatePasswordLeakVerification passwordLeakVerification =
          PrivatePasswordLeakVerification.newBuilder()
              .setLookupHashPrefix(ByteString.copyFrom(lookupHashPrefix))
              .setEncryptedUserCredentialsHash(ByteString.copyFrom(encryptedUserCredentialsHash))
              .build();

      // Build the assessment request.
      CreateAssessmentRequest createAssessmentRequest =
          CreateAssessmentRequest.newBuilder()
              .setParent(String.format("projects/%s", projectID))
              .setAssessment(
                  Assessment.newBuilder()
                      // Set request for Password leak verification.
                      .setPrivatePasswordLeakVerification(passwordLeakVerification)
                      .build())
              .build();

      // Send the create assessment request.
      Assessment response = client.createAssessment(createAssessmentRequest);

      // Get the reCAPTCHA Enterprise score.
      float recaptchaScore = response.getRiskAnalysis().getScore();
      System.out.println("The reCAPTCHA score is: " + recaptchaScore);

      // Get the assessment name (id). Use this to annotate the assessment.
      String assessmentName = response.getName();
      System.out.println(
          "Assessment name: " + assessmentName.substring(assessmentName.lastIndexOf("/") + 1));

      return response.getPrivatePasswordLeakVerification();
    }
  }
}

Docker 容器

如需检查凭据是否泄露,请使用 localhost 连接或在容器上设置 HTTPS,将用户名和密码凭据对安全地发送到容器。然后,容器会先对这些凭据进行加密,然后再向 reCAPTCHA 发出 API 请求,并在本地验证重新加密的结果。

如需向 Docker 容器发送请求,请完成以下步骤:

  1. 设置 Docker
  2. 为 Docker 容器准备环境
  3. 构建并运行容器
  4. 向容器发送 HTTP 请求
  5. 解读判定结果并采取措施

准备运行 Docker 容器

  1. 选择身份验证策略。

    该容器支持设置应用默认凭据,或可以接受 API 密钥进行身份验证。

  2. 将 PLD 容器配置为使用 HTTPS 或在仅限 localhost 的演示中运行 模式。

    由于该容器接受敏感的最终用户凭据(用户名和密码),因此必须使用 HTTPS 或在仅限本地主机的演示模式下运行。有关 HTTPS 配置的指导, GitHub 上的 README

以下步骤使用 API 密钥身份验证,并在仅限本地主机的演示模式下运行客户端。

构建和运行 Docker 容器

  1. 克隆代码库:

    git clone github.com/GoogleCloudPlatform/reCAPTCHA-PLD
    
  2. 构建容器。

    docker build . -t pld-local
    
  3. 启动容器:

    docker run --network host \
    -e RECAPTCHA_PROJECT_ID=PROJECT_ID \
    -e GOOGLE_CLOUD_API_KEY=API_KEY \
    pld-local
    

容器启动并开始在 localhost 的 8080 端口上处理请求。

发送 localhost 请求

在使用任何请求数据之前,请先进行以下替换:

  • LEAKED_USERNAME:已泄露的凭据对的用户名。
  • LEAKED_PASSWORD:已泄露的凭据对的密码。

HTTP 方法和网址:

POST http://localhost:8080/createAssessment/

请求 JSON 正文:

{
    "username":"LEAKED_USERNAME",
    "password":"LEAKED_PASSWORD"
}

如需发送请求,请选择以下方式之一:

curl

将请求正文保存在名为 request.json 的文件中,然后执行以下命令:

curl -X POST \
-H "Content-Type: application/json; charset=utf-8" \
-d @request.json \
"http://localhost:8080/createAssessment/"

PowerShell

将请求正文保存在名为 request.json 的文件中,然后执行以下命令:

$headers = @{  }

Invoke-WebRequest `
-Method POST `
-Headers $headers `
-ContentType: "application/json; charset=utf-8" `
-InFile request.json `
-Uri "http://localhost:8080/createAssessment/" | Select-Object -Expand Content

您应该收到类似以下内容的 JSON 响应:


  { "leakedStatus":"LEAKED" }


 OR


  { "leakedStatus":"NO_STATUS" }

解读判定结果并采取措施

评估响应会显示凭据是否已泄露,并为您提供 您可以参考这些信息来采取适当措施来保护自己的用户。

下表列出了在检测到泄露的密码时建议采取的操作:

检测到密码泄露 用于保护用户的操作
登录期间
  • 拒绝并要求客户选择其他密码,以及 并尽可能触发 MFA 验证。
  • 提醒用户为各个账号使用不同的密码。
  • 在您的网站上要求使用安全系数高的密码。
  • 如果用户未使用多重身份验证 (MFA),则提示用户启用多重身份验证。
在创建账号或重置密码时
  • 要求用户更改密码。
  • 触发多重身份验证 (MFA) 流程。

如果您尚未在网站上使用 MFA 提供程序,则可以使用 reCAPTCHA 的 MFA 功能

后续步骤