检测密码泄露和凭据盗用

本页面介绍了如何使用 reCAPTCHA 的 Password Defense 功能检测密码泄露和凭据盗用,以防范账户接管(攻击)(ATO) 和撞库攻击。借助 reCAPTCHA,您可以在进行任何评估过程中对用户凭据(密码)进行定期审核,以确保它们没有被泄露或盗用。为了执行这些评估,Google 会使用 密码安全检查功能。

准备工作

  1. 为 reCAPTCHA 准备环境

  2. 设置 reCAPTCHA

  3. 验证是否已为您的 Google Cloud 项目启用结算功能。

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

检查凭据是否被盗用和泄露

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

Docker 容器是一个开源客户端,用于实现 安全多方计算 ,以保护最终用户的隐私并安全地查找密码泄露情况。 如需了解详情,请参阅GitHub 代码库。Docker 容器抽象了实现加密算法的复杂性,并简化了安装过程。此外,您还可以在自己的基础架构中托管容器应用。

加密函数

如需检查一组凭据是否已遭泄露,请在为登录、密码更改和重设密码等操作创建评估时使用 Password Defense。

如需检查密码泄露和凭据盗用情况,请完成以下步骤:

  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());

代码示例

Node.js (TypeScript)

如需了解如何使用 Node.js (TypeScript) 实现密码泄露检测,请参阅 GitHub 上的 TypeScript 代码示例

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;

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 = verification.getLookupHashPrefix();
    byte[] encryptedUserCredentialsHash = 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 -> x.toByteArray())
            .collect(Collectors.toList());

    // Verify if the encrypted credentials are present in the obtained match list.
    PasswordCheckResult result =
        passwordLeak
            .verify(
                verification,
                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 或在仅限 localhost 的演示模式下运行。如需有关 HTTPS 配置的指南,请参阅 GitHub 上的 README

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

构建并运行 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) 流程。

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

后续步骤