Checking passwords

This page explains how to check passwords against our database of leaked credentials.

This is based on the same system Google uses in Password Checkup.

Our database contains more than 4 billion leaked credentials and our research has shown that 1.5% of logins on the web involve breached credentials.

Before you begin

Complete the steps in the Quickstart to gain access to the API.

API request

Create an assessment using the projects.assessments.create method.

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

  • project-id: your GCP 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"
  }
}

Username canonicalization

Usernames must be canoncalized before calling the API. Simply lowercase the username, strip the domain component, and remove all dots. Here are some examples:

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

An example of the canonicalization logic in Python:

#!/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

User credentials need to be hashed before calling the API. This consists of computing the Scrypt hash of the canonicalized username and the password with a fixed salt. You need to 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 from the previous section.

The fixed salt and Scrypt parameters are shown in the following Python 3 example:

#!/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)

Here is a sample expected base64 encoded hash, this can be used to verify that your implementation is correct.

  • compute_scrypt_hash(test@domain.com, s0m3passw0rd!) = 1rzih02go6/dNcr1CQu9Ne+x4CC8xqSVuGaSWe+WhWk=