Detecting password leaks and breached credentials

reCAPTCHA Enterprise can detect password leaks and breached credentials to prevent account takeovers (ATOs) and credential stuffing attacks. With reCAPTCHA Enterprise, you can conduct regular audits of user credentials (passwords) as part of any assessment to ensure that they have not been leaked or breached. To perform these assessments, Google uses the Password Checkup feature.

Before you begin

  1. Choose the best method for setting up reCAPTCHA Enterprise in your environment and complete the setup.

  2. Password leak detection is accessible after a security review. Contact our sales team to onboard your site to this feature.

Checking for breached and/or leaked credentials

To check if a set of credentials has been compromised, query the Password Checkup database during assessments for actions, such as logins and password changes or resets.

When you create an assessment, modify the username as per the canonicalization process, and hash the password using Scrypt with a specific salt.

Canonicalizing usernames

To canonicalize a username, use lowercase for the username, strip the domain component, and remove all dots as shown in the following examples:

  • canonicalize(foo.bar@COM) = foobar
  • canonicalize(TEST@MAIL.COM) = test

The following code shows an example of canonicalization logic in Python 3.

#!/usr/bin/env python3

def canonicalize_username(username):
  """Canonicalize a username which must be a UTF-8 encoded string."""
  if "@" in username:
    username = username[:username.rfind("@")]
  return username.lower().replace(".", "")

Computing hashed_user_credentials

  1. Compute Scrypt(canonicalized_username + password + username_updated_salt) where username_updated_salt = canonicalized_username + fixed_salt and canonicalized_username is the canonicalized version of the username.

    The following Python 3 example shows fixed salt and Scrypt parameters.

    #!/usr/bin/env python3
    import base64
    import hashlib
    
    # Scrypt hash salt
    USER_CREDENTIALS_HASH_SALT = [
        48, 118, 42, 210, 63, 123, 161, 155, 248, 227, 66, 252, 161, 167, 141, 6,
        230, 107, 228, 219, 184, 79, 129, 83, 197, 3, 200, 219, 189, 222, 165, 32
    ]
    
    # Scrypt hash parameters and constants
    SCRYPT_HASH_CPU_MEM_COST = 1 << 12
    SCRYPT_HASH_BLOCK_SIZE = 8
    SCRYPT_HASH_PARALLELIZATION = 1
    SCRYPT_MAX_MEMORY = 1024 * 1024 * 32
    SCRYPT_HASH_KEY_LENGTH = 32
    
    def process_credentials(username, password):
      """Process user credentials to be used with the credentials check service."""
    
      canonicalized_username = canonicalize_username(username)
    
      # Compute the salt by appending the username to the fixed hash salt.
      salt = bytes([ord(character) for character in list(canonicalized_username)] +
                   USER_CREDENTIALS_HASH_SALT)
    
      # Compute the data to be hashed
      data = bytes(canonicalized_username + password, encoding="utf8")
    
      # Compute Scrypt hash using hashlib.
      scrypt_hash = hashlib.scrypt(
          password=data,
          salt=salt,
          n=SCRYPT_HASH_CPU_MEM_COST,
          r=SCRYPT_HASH_BLOCK_SIZE,
          p=SCRYPT_HASH_PARALLELIZATION,
          maxmem=SCRYPT_MAX_MEMORY,
          dklen=SCRYPT_HASH_KEY_LENGTH)
      return canonicalized_username, base64.b64encode(scrypt_hash)
    
  2. To verify that your implementation is correct, use the following sample expected base64 encoded hash.

     compute_scrypt_hash(`process_credentials`) = `1rzih02go6/dNcr1CQu9Ne+x4CC8xqSVuGaSWe+WhWk=`
    

API request

Creating an assessment for detecting password leaks and breached credentials is supported only on the v1beta1 endpoint.

Use the projects.assessments.create method.

Before using any of the request data below, make the following replacements:

  • PROJECT_ID: your Google Cloud project ID
  • CANONICALIZED_USERNAME: canonicalized username
  • HASHED_USER_CREDENTIALS: credentials hashed with Scrypt

HTTP method and URL:

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

Request JSON body:

{
  "password_leak_verification": {
    "canonicalized_username": "CANONICALIZED_USERNAME"
    "hashed_user_credentials": "HASHED_USER_CREDENTIALS"
  }
}

To send your request, choose one of these options:

curl

Save the request body in a file called request.json, and execute the following command:

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

PowerShell

Save the request body in a file called request.json, and execute the following command:

$cred = gcloud auth application-default 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/v1beta1/projects/PROJECT_ID/assessments" | Select-Object -Expand Content

You should receive a JSON response similar to the following:

{
  "name": "projects/698047609967/assessments/fb22000000000000",
  "score": 0,
  "reasons": [],
  "passwordLeakVerification": {
    "hashedUserCredentials": "IDuS/soXlsLHmOm1A8zw+mChTI561MufdTaqL3k+zC4=",
    "credentialsLeaked": [true | false],
    "canonicalizedUsername": "test"
  }
}