Account Takeover API

Overview

Account takeover (ATO) is when a fraudster gains control of an account that belongs to a genuine customer. Fraudsters can monetize this in a number of ways, from making unauthorised transactions or selling the compromised accounts on to selling the personal information associated with the customer.

Phishing, spyware and malware can all be used to commit ATO attacks. However, for many merchants credential stuffing is the most common tactic used. Credential stuffing uses stolen username and password combinations to automate login requests in order to gain access to user accounts.

Following a breach, stolen credentials can be purchased on the darkweb or shared via cracking forums. Given that many people use the same password across multiple accounts, these credentials can then be used to launch a successful credential stuffing attack on other services.

Credential stuffing can be scripted by more skilled fraudsters. However, automated tools like Sentry MBA make credential stuffing very easy for anyone to commit.

How Ravelin Combats Account Takeover

Taking into account the nature of account takeover, Ravelin has introduced a number of different ways to combat the issue.

Machine learning

Using a custom machine learning model, we can distinguish between good and bad logins. We are able to look at the historic login behaviour for the customer and decide if the login looks unusual or not.

Rules

We can set account takeover specific rules around device, username and IP. For example, if the same device tries to access X accounts within a set time frame.

Breached Credentials

Credential stuffing relies on a list of username and password combinations. In order to mitigate against this, we maintain a breached credentials database. Calls can be made to the database either via the v3/login event at login or via /v2/lookup/credentials/check during registration or password change to verify if we’ve seen the credentials in the wild. Though we cannot guarantee that every breached credential will be in our database, this can go a long way to preventing ATO. You can also search for and view users that have logged in with credentials that appear in our database - providing additional context during investigations.

Collecting and surfacing login activity data

By leveraging data collected via v3/login event, we provide oversight of login activity within our dashboard. Login activity reporting is available to explore via our Analytics tool, making it easier to spot anomalies in ‘normal’ login activity across your customer base. In addition, you can interact with login data via a filterable login list and drill down to view login information for a specific customer on their customer profile.

Login Event

The point of login is a critical step in detecting account takeover. We want to prevent the malicious actor from being able to log in just like we want to prevent the fraudster from completing an order.

POST /v3/login?score=true

This is the endpoint used to tell Ravelin about every login attempt, both successful and unsuccessful. Unsuccessful logins can be important signals for an ATO attack. It will provide an ATO recommendation response if asked for via the query parameter score=true. Otherwise it will simply record the data without further processing.

{
  "timestamp": 1512828988826,
  "login": {
    "username": "jsmith123@example.com",
    "customerId": "abc-123-XYZ",
    "success": false,
    "authenticationMechanism": {
      "password": {
        "passwordHashed": "ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f",
        "success": false,
        "failureReason": "UNKNOWN_USERNAME"
      },
      "social": {
        "success": false,
        "failureReason": "SOCIAL_FAILURE",
        "socialProvider": "facebook"
      },
      "oneTimeCode": {
        "success": false,
        "failureReason": "AUTHENTICATION_FAILURE"
      },
      "u2f": {
        "success": false,
        "failureReason": "INVALID_KEY"
      },
      "rsaKey": {
        "success": false,
        "failureReason": "INTERNAL_ERROR"
      },
      "smsCode": {
        "phoneNumber": "+447907283546",
        "success": false,
        "failureReason": "INVALID_CODE"
      },
      "magiclink": {
        "transport": "email",
        "phoneNumber": "+447907283546",
        "success": false,
        "failureReason": "RATE_LIMIT"
      },
      "recaptcha": {
        "success": false,
        "failureReason": "TIMEOUT"
      },
      "bioMetric": {
        "success": false,
        "failureReason": "BANNED_USER"
      },
      "pushNotification": {
        "success": false,
        "failureReason": "INTERNAL_ERROR"
      }
    },
    "app": {
      "name": "Our App Lite",
      "platform": "web",
      "domain": "us.brand.com"
    }
  },
  "device": {
    "deviceId": "65fc5ac0-2ba3-4a3b-aa5e-f5a77b845260",
    "ipAddress": "81.152.92.84",
    "language": "en-US",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
    "model": "Pixel XL",
    "os": "android",
    "type": "phone",
    "manufacturer": "google",
    "location": {
      "country": "GBR",
      "postalCode": "E1 1AA",
      "latitude": 51.503252,
      "longitude": -0.127899,
      "addresseeName": "John Smith",
      "street1": "123 fake st.",
      "street2": "floor 4, flat 48",
      "neighbourhood": "Hackney",
      "zone": "1",
      "city": "London",
      "region": "California",
      "poBoxNumber": "1234"
    }
  },
  "location": {
    "country": "GBR",
    "postalCode": "E1 1AA",
    "latitude": 51.503252,
    "longitude": -0.127899,
    "addresseeName": "John Smith",
    "street1": "123 fake st.",
    "street2": "floor 4, flat 48",
    "neighbourhood": "Hackney",
    "zone": "1",
    "city": "London",
    "region": "California",
    "poBoxNumber": "1234"
  }
}
Name Type Description Example
timestamp
required
integer

Unix timestamp with milliseconds (nanoseconds also accepted)

1512828988826
login
required
object

The login used by the customer.

Hide definition
Name Type Description Example
username
required
string

The username of the user. This is the unique string that ties this particular user to the authentication mechanism used. Typically this is the email address.

"jsmith123@example.com"
success
required
boolean

If the login attempt was successful and access was granted.

false
authenticationMechanism
required
object

At least one authentication mechanism must be provided. More than one may be provided. For example a password and a oneTimeCode may be used to authenticate.

Any of the following:
Hide definition
Name Type Description Example
password
optional
object

Provided if the user is using a password to authenticate

Show definition
social
optional
object

Provided if the user authenticates via a social login provider

Show definition
oneTimeCode
optional
object

Provided if a one time code such as those used by Google Authenticator is used

Show definition
u2f
optional
object

Provided if Universal 2nd Factor (U2F) authentication is used, as defined by the FIDO Alliance.

Show definition
rsaKey
optional
object

Provided if an RSA key is used, such as a Yubikey

Show definition
smsCode
optional
object

Provided if a one time code was used sent via SMS

Show definition
recaptcha
optional
object

Provided if reCAPTCHA was performed

Show definition
bioMetric
optional
object

Provided if biometric authentication is used

Show definition
pushNotification
optional
object

Provided if a push notification is sent within an app to authenticate a user

Show definition
customerId
optional
string

customerId is the unique identifier for a user. This identifier does not change even if the username is changed. This is optional for login events as you will see failed logins against usernames that do not exist in your system and therefore do not have a customerId. Please send this on login attempts where a customerId is available. If you are using our fraud solution, this should be the same customerId as used there.

"abc-123-XYZ"
app
important
object

The mobile or web app that this login was done from.

Show definition
custom
optional
object

Custom data that is relevant to your domain. This can be any JSON object.

device
optional
object

While Device is not a required field on the login event, it is highly recommended as it is a vital signal to account breaches. The most important Device fields are deviceId and ipAddress. deviceId can be generated using our JavaScript library. Details about that can be found in the device fingerprinting section. Any other fields can be very useful but are not strictly necessary. For more details about device tracking please see our device API and the Guide.

Show definition
location
optional
object

Location is optional as we are aware you will not typically have a location at login time. If you do have location as part of your flow this can be very useful to localise ongoing or repeat attackers.

Show definition

Above is the full login request object. This is similar to our /v2/login endpoint but with additional information regarding the authentication attempt. This request is expected to be sent after all successful and unsuccessful logins from your backend system.

Responses

Successful Example

You can see an example of a successful response from the /v3/login endpoint below. This response is identical to other responses given by the Ravelin platform for payment fraud endpoints. Note that we do provide payment fraud scores at login time as well. The Payment fraud and ATO responses are both included. Ensure you use the correct action when dealing with ATO vs. Payment Fraud.

{
  "status": 200,
  "success": "true",
  "timestamp": "2021-08-27T12:19:31Z",
  "data": {
    "customerId": "39f54f7c-8ed7-41f8-5260-09881f69f675",
    "action": "ALLOW",
    "score": 20,
    "source": "RAVELIN",
    "scoreId": "4fd50782-b9f7-48be-64e1-820c7cf6dd51",
    "ato": {
      "action": "PERMIT"
    }
  },
  "credentialStatus": {
    "passwordBreached": false,
    "usernameBreached": false
  },
  "customerChanges": [
    {
      "changeId": "9aa92ad0-ee23-4293-7fee-396dc738b195",
      "customerId": "39f54f7c-8ed7-41f8-5260-09881f69f675",
      "timestamp": "2021-08-27T12:19:31.035741497Z",
      "changeType": "DEVICE",
      "newValue": {
        "device": {
          "deviceId": "d61096b5-4ef8-4404-6666-eb9e8df1d754",
          "deviceType": "phone",
          "deviceManufacturer": "Samsung",
          "deviceModel": "SM-G960F",
          "deviceOS": "android",
          "ipAddress": "1c14:d7a:124f:3a00:400:cb7b:e4aa:af92",
          "userAgent": "Mozilla/5.0 (Linux; Android 10; SM-G960F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.152 Mobile Safari/537.36",
          "timestamp": "2021-08-27T12:19:31.035741497Z"
        }
      },
      "previousValue": {
        "device": {
          "deviceId": "0c999b50-db07-46a0-629f-f55b89dc1dc0",
          "deviceType": "phone",
          "deviceManufacturer": "Apple",
          "deviceModel": "iPhone",
          "deviceOS": "ios",
          "ipAddress": "92.41.191.36",
          "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1",
          "timestamp": "2021-08-27T12:19:27.26181816Z"
        }
      },
      "verificationURL": "http://api/v2/change/verify?id=cmF2vbWVRlc3RCpzlhYTkyYWQwLRjNzM4YjN1cFsR1dHWVLWxZueELZWxpbnLTlvZ2ltNDI5My03ZmVlE5NQuLW3TCp2yM5NmZ0MjM==",
      "changeSetId": "aeb87294-a02d-4746-7d45-34770d51f308"
    }
  ]
}
Name Type Description Example
status
integer 200
success
string "true"
timestamp
string "2021-08-27T12:19:31Z"
data
object Show definition
credentialStatus
object Show definition
customerChanges
array Show definition
Failure Example

You can see an example of a failed response from the /v3/login endpoint below. A failure response will indicate why the failure happened and will try to direct you to relevant documentation.

{
  "status": 500,
  "message": "Internal Server Error",
  "timestamp": "2021-08-27T12:19:31Z",
  "retryable": false
}

Action

Using the information supplied to Ravelin, logins and other events are categorised in one of three buckets: PERMIT WARN BLOCK

This information is sent in the response body to scored POST requests (with ?score=true) as defined above.

Requesting an action

Events sent to Ravelin using POST requests get an empty response body by default. The information is stored in our system and no further action is taken during the request. To force an immediate evaluation based on the information received up to and including an event add the ?score=true query parameter.

This will do two things:

  1. Process the event payload
  2. Force immediate recalculation of the action and return it

The score will be calculated based on the information in this event, and in all events previously received and met with a 200 OK status.

Suggested handling

The ?score=true query parameter is available on every event endpoint. This includes both ATO and Payment Fraud related endpoints. Specify it when you want to make a decision regarding a login attempt.

The most important field in the payload is action under the ATO object:

On this action In your system
PERMIT

Allow this login or account detail change to proceed - no action is required

WARN

We advise that you allow the login but take extra validation steps for this login or account detail change. Additional validation could include enforcing 2FA if you have a verified phone number, notifying the user that there has been suspicious account activity and asking them to confirm if the activity was legitimate or not or prompting the user to complete a recaptcha

BLOCK

We advise that you block this login or account change and show a generic error message which does not explain why the user was prevented from login. This is important as we do not want to make it obvious to the fraudster that we have detected the attempt. If we return a BLOCK response once a user is already logged in, we recommend that you invalidate their token immediately and review all recent activity on the account. You can also inform of the user and ask them to confirm if the activity was legitimate or not. However, it is important to remember that a fraudster may change the contact details associated with an account as part of an ATO attack so you may want to check if any account details were recently changed. If the email has been changed recently, we advise that you send any communications to the previously saved email.

Other fields are provided for analytic and debugging purposes.

Credentials Check

POST /v2/lookup/credentials/check

Ravelin maintains a list of breached credentials that can be found in the public domain. At login, you can view whether the login attempt was using breached credentials by looking at the credentialStatus object within the /v3/login response.

You can use the database to check if a user is attempting to register a new account, or update an existing one, with credentials that have been seen in the wild via our v2/lookup/credentials/check endpoint. The below request is expected to be sent at password creation or update. The response will tell you if the username and/or password is found in our breached credential database.

Request

Passwords are extremely sensitive, so please do not send us plaintext passwords. Instead, please hash the password with SHA256 and HEX encode it before sending it. We do not store the hashed password, and discard it immediately after processing your request.

If you are still concerned about sending us a hash of the password please contact us for alternative options.

{
  "username": "jsmith123@example.com",
  "passwordHash": "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"
}
Name Type Description Example
username
required
string

The username to be checked

"jsmith123@example.com"
passwordHash
required
string

Passwords are extremely sensitive, so please do not send us plaintext passwords. Instead, please hash the password with SHA256 and HEX encode it before sending it. We do not store the hashed password, and discard it immediately after processing your request.

"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"

Response

Successful Example
{
  "usernameBreached": true,
  "passwordBreached": true
}
Name Type Description Example
usernameBreached
boolean true
passwordBreached
boolean true

There are three distinct flows where we check if a users credentials have been breached. For each flow, you may want to take a different action. Below we have outlined what we suggest for each flow.

Failure Example
{
  "status": 400,
  "traceId": "7fffffff9ed70f0101575067d-1925c9bf-2e34-422a-5e18-71a4ba379756",
  "timestamp": 1630073086
}

Authentication

While your backend is processing a login request, you can call our v3/login endpoint. Our response will indicate within the credential status object whether or not the username and password appear in Ravelin’s breached credential database.

Credential Status Action
...
"credentialStatus": {
    "usernameBreached": false,
    "passwordBreached": false
  },
...
If the response is PERMIT and the credential status is FALSE for both username and password, we suggest you allow the login - no action is required.
...
"credentialStatus": {
    "usernameBreached": true,
    "passwordBreached": false
  },
...
If the credential status is only true for username, we suggest you take no action.
...
"credentialStatus": {
    "usernameBreached": true,
    "passwordBreached": true
  },
...
If the credential status is true for both username and password, we suggest you take at least one of the following actions taking into account your risk appetite:
  • Force the user to reset their password before allowing them to login
  • Prompt the user via email or text to reset their password after they login
  • Force the user to reset their password after they complete their session
  • Prompt the user to reset their password after they complete their session
  • Use 2FA if you have a verified phone number associated with the account

Registration

At registration, you can call our v2/lookup/credentials/check endpoint. We will return the responses outlined below.

Credential Status Action
...
"credentialStatus": {
    "usernameBreached": false,
    "passwordBreached": false
  },
...
No action is required if we return false.
...
"credentialStatus": {
    "usernameBreached": true,
    "passwordBreached": false
  },
...
If the credential status is only true for username, we suggest you take no action.
...
"credentialStatus": {
    "usernameBreached": true,
    "passwordBreached": true
  },
...
If a user is trying to register using credentials that appear in the breached database, we advise that you do NOT allow the user to set that password at registration.

Password Updates

During an account update (e.g. forgot password, password change), you can call our v2/lookup/credentials/check endpoint. We will return the responses outlined below.

Credential Status Action
...
"credentialStatus": {
    "usernameBreached": false,
    "passwordBreached": false
  },
...
No action is required if we return false.
...
"credentialStatus": {
    "usernameBreached": true,
    "passwordBreached": false
  },
...
If the credential status is only true for username, we suggest you take no action.
...
"credentialStatus": {
    "usernameBreached": true,
    "passwordBreached": true
  },
...
If a user is trying to update their account using credentials that appear in the breached database we advise that you do NOT allow the user to use the password at time of update.

Reclaiming A Customer Account

POST /v2/reclaim

In cases where an account is reclaimed by the legitimate owner you need to notify us that the account has been secured. This will prevent the account from being blocked due to activity that happened while the account was compromised.

Accounts should be reclaimed if:

  • A customer login has been blocked by Ravelin because of an ATO attempt and the password has been reset.
  • A customer experiences and reports an ATO and the account has now been secured.
  • An internal investigation identifies an ATO and results in the password being reset.

We strongly recommend notifying us about account reclaims only when you are sure that the 3rd party has lost access to the account. A reclamation on a customer account is taken into consideration during the decision making process when detecting account takeover. This means that at least for a short time, new logins are likely going to be permitted for that customer even in cases that might otherwise look suspicious.

If you use social login, you may need to consider unlinking social accounts as part of a reclaim. This is in the event that a fraudster logs in and links a social account which can then be used to continue accessing the account.

Request

A request has a limit of 1000 customers per batch. If more customers are present in the batch, we will respond with an error.

{
  "timestamp": 1512828988826,
  "customers": [
    {
      "customerId": "abc-123-ZYZ",
      "method": "Other",
      "reportedBy": "CUSTOMER",
      "atoEvents": [
        {
          "loginId": "42bd8af9-1ac2-7b2c-cd6a-a6a33c723510"
        }
      ]
    }
  ]
}
Name Type Description Example
timestamp
required
integer

Unix timestamp with milliseconds (nanoseconds also accepted)

1512828988826
customers
required
array

An array of between 1 and 1000 accounts.

Hide definition
Name Type Description Example
method
required
string

Indicates the method by which the customer account was reclaimed.

One of: PasswordReset, AccountDeleted, or Other.

"Other"
customerId
required
string

The unique identifier of the customer.

"abc-123-ZYZ"
reportedBy
optional
string

Whether the account takeover was reported by a CUSTOMER, MERCHANT or RAVELIN

One of: CUSTOMER, MERCHANT, or RAVELIN.

"CUSTOMER"
atoEvents
optional
array

The array of identifiers (loginId, deviceId or orderId) for the first account takeover event seen on a customer account. Each array's object may contain only one id.

Show definition
source
deprecated
string

Indicates the source of the request. This field is deprecated. Please, use customer.reportedBy instead.

Only: ATO.

Response

Successful Example

On a successful request, we will respond with a message containing the number of accounts processed. This will always be the same as the amount provided to us in request payload.

{
  "status": 200,
  "message": "5 customer accounts reclaimed successfully"
}

Name Type Description Example
status
integer 200
message
string "5 customer accounts reclaimed successfully"
Failure Example
{
  "status": 500,
  "message": "No customer accounts provided. Check https://developer.ravelin.com/apis/ato/#reclaiming for more details",
  "traceId": "7fffffff9ed70c8001b334488-a9363b6f-fb7d-4f52-7c76-a8d1ab42b712",
  "timestamp": 1630073727
}