Detect and prevent account takeovers

Google Cloud Fraud Defense lets you detect and prevent account takeovers (ATOs) across your websites and mobile applications. To help protect accounts against credential stuffing and automated takeovers, you can combine account-level behavioral analysis in Account defense with compromised-credential checks in Password defense and step-up multi-factor authentication (MFA).

ATO attacks, such as credential stuffing, typically occur when an attacker sends requests to sign in by using credentials exposed in data breaches and password dumps. Because users frequently reuse passwords across unrelated sites, automated stuffing attacks can succeed even against properly configured authentication endpoints unless you evaluate behavioral risk and leaked credentials.

Before you begin

Prepare your environment for Google Cloud Fraud Defense.

Detect and prevent ATOs

With Fraud Defense, you can detect and prevent ATOs by using the following capabilities:

Use Account defense and the ATO risk score

Account defense is the primary capability for detecting and preventing ATOs across websites and mobile apps.

Account defense provides a dedicated ATO risk score (accountDefenderAssessment.accountTakeoverVerdict.risk, ranging from 0.0 to 1.0) and explainability signals (riskReasons and trustReasons). These signals evaluate account-level behavioral history alongside session signals, providing approximately four times higher ATO detection precision than bot scores alone.

To integrate Account defense, pass a stable account identifier in the user_info.account_id field (event.userInfo.accountId when you call CreateAssessment. If the account identifier is available only after authentication, pass account_id when you call AnnotateAssessment.

Detect leaked passwords

Because ATOs frequently rely on credential stuffing with credentials exposed in password dumps, use Password defense on sign-in, password-change, and password-reset flows to detect compromised credentials.

When you check credentials during an assessment, Password defense evaluates the canonicalized username and password hash prefix against known data breaches without exposing plaintext credentials to Google. When you call CreateAssessment, pass the username (credentials.username) hash prefix (lookupHashPrefix) and encrypted credentials hash (encryptedUserCredentialsHash) in privatePasswordLeakVerification. If the verdict (leakedPasswordVerdict) indicates compromised credentials, require a password reset or trigger a step-up MFA challenge.

Use score-based keys and step-up MFA

When the ATO risk score (accountDefenderAssessment.accountTakeoverVerdict.risk) or bot score (riskAnalysis.score) indicates elevated risk, use score-based keys to trigger step-up authentication or multi-factor authentication (MFA) in your application as your enforcement mechanism. For example, prompt the user for an additional factor, require re-authentication, or trigger one-time password (OTP) verification.

To implement score-based keys and step-up MFA, complete the following steps:

  • Install score-based keys to collect session signals and detect automation in large-scale ATOs:

  • When accountTakeoverVerdict.risk exceeds your risk threshold or riskAnalysis.score falls below your bot threshold, trigger step-up authentication or an MFA challenge in your identity provider to verify account ownership on sign-in or for sensitive actions. Triggering step-up challenges selectively on elevated-risk requests reduces friction for legitimate users.

The following example shows how to integrate score-based keys in a sign-in workflow:

function submitForm() {
  grecaptcha.enterprise.ready(function() {
    grecaptcha.enterprise.execute(
      'reCAPTCHA_site_key', {action: 'account_login'}).then(function(token) {
       document.getElementById("token").value = token;
       document.getElementByID("loginForm").submit();
    });
  });
}
<form id="loginForm" action="?" method="POST">
  Username: <input type="text" name="username"/><br/>
  Password: <input type="password" name="password"/><br/>
  <input type="hidden" id="token" name="recaptcha_token"/>
  <button onclick="submitForm()">Login</button>
</form>
<script src="https://www.google.com/recaptcha/enterprise.js" async defer></script>

You can experiment with this code in JSFiddle by clicking the <> icon in the top-right corner of the code window.

<html>
  <head>
    <title>Account Login - Score</title>
    <script src="https://www.google.com/recaptcha/enterprise.js"></script>
    <script>
    function submitForm() {
      grecaptcha.enterprise.ready(function() {
        grecaptcha.enterprise.execute(
          'reCAPTCHA_site_key', {action: 'account_login'}).then(function(token) {
           document.getElementById("token").value = token;
           document.getElementByID("loginForm").submit();
        });
      });
    }
    </script>
  </head>
  <body>
    <form id="loginForm" action="?" method="POST">
      Username: <input type="text" name="username"/><br/>
      Password: <input type="password" name="password"/><br/>
      <input type="hidden" id="token" name="recaptcha_token"/>
      <button onclick="submitForm()">Login</button>
    </form>
  </body>
</html>

What's next