How to detect a SIM Swap before sending an SMS OTP

October 24, 2022
Written by

How to detect a SIM Swap before sending an SMS OTP

While SMS one-time passwords (OTP) are a great solution for a lot of users, SMS is also vulnerable to SIM swap attacks. These attacks happen when a bad actor gains access to your cellular account through social engineering or bribery. Once the fraudster has a mobile operator convinced that they're you, they can have a new SIM card issued with your mobile number, gaining the ability to access your two-factor authentication (2FA) codes.

SIM swaps are a normal activity if you're switching phones. However, SIM swaps are not a normal activity before making a large external funds transfer or other high value transaction. Luckily, research shows that checking for a SIM swap before sending an OTP "reduced SIM swap-based banking fraud to nearly zero overnight."

Twilio's new SIM swap detection through the Lookup API will give you the tools to reduce fraud and keep your customers secure while still taking advantage of one of the most user friendly 2FA solutions.

This blog post will show you how to detect a SIM swap before sending an OTP using the Lookup API's new SIM Swap package.

Prerequisites for detecting a SIM Swap

You'll need a Twilio account for using the Lookup API. Grab your Account SID and Auth Token (found in the Console) and use them in your API requests.

Because our SIM swap detection queries real-time carrier data, Twilio requires carrier approvals before we can provision access. Reach out to start the onboarding process.

Detect a SIM swapped phone number

The SIM swap package will return:

  • The carrier's name, such as Vodafone or O2 or Verizon
  • An MNC and MCC, three digit codes to identify the mobile network operator
  • Details about the last SIM swap, if applicable, in the following format:

Here's an example of a typical response:

{ 
  "last_sim_swap_date": "2020-04-27T10:18:50Z",
  "swapped_period": "PT15H33M44S",
  "swapped_in_period": true
}

During onboarding, you will configure the swapped period for all countries which do not support "last SIM swap date". The swapped period is an ISO 8601 duration. For countries that do return the last SIM swap date, the swapped period will be automatically derived and "swapped in period" will be set to true. Check out the documentation for more information about response values.

Here's how to make a request to the Lookup API with your Account SID and Auth Token using cURL.

curl -X GET \ 'https://lookups.dublin.ie1.twilio.com/v2/PhoneNumbers/+447772000001?Fields=sim_swap' \ -u $TWILIO_API_KEY:$TWILIO_API_KEY_SECRET

You can find this code sample in more languages in the documentation.

This request was made to the Ireland region for data residency. Learn more about using Lookup with Twilio Regions.

Here's an example of a SIM swap that happened in 2020 - the swapped period has been calculated based on the last SIM swap date.

{
  "calling_country_code": "44",
  "country_code": "GB",
  "phone_number": "+447772000001",
  "national_format": "07772 000001",
  "valid": true,
  "validation_errors": null,
  "caller_name": null,
  "sim_swap": {
    "last_sim_swap": {
      "last_sim_swap_date": "2020-04-27T10:18:50Z",
      "swapped_period": "PT15282H33M44S",
      "swapped_in_period": true
    },
    "carrier_name": "Vodafone UK",
    "mobile_country_code": "276",
    "mobile_network_code": "02",
    "error_code": null
  },
  "call_forwarding": null,
  "live_activity": null,
  "line_type_intelligence": null,
  "url": "https://lookups.twilio.com/v2/PhoneNumbers/+447772000001"
}

Here's an example of a French number that doesn't have a SIM swap date. The customer configured a 24 hour period.

{
    "phone_number": "+33777000001",
    "validation_errors": [],
    "call_forwarding": null,
    "url": "https://lookups.twilio.com/v2/PhoneNumbers/+33777000001",
    "calling_country_code": "33",
    "live_activity": null,
    "enhanced_line_type": null,
    "sim_swap": {
        "mobile_network_code": "01",
        "last_sim_swap": {
            "swapped_period": "PT24H",
            "last_sim_swap_date": null,
            "swapped_in_period": false
        },
        "mobile_country_code": "208",
        "carrier_name": "ORANGE FRANCE",
        "error_code": null
    },
    "caller_name": null,
    "national_format": "07 77 00 00 01",
    "country_code": "FR",
    "line_type_intelligence": null,
    "valid": true
}

You can get a sneak peak of some of the other new packages available like Line Type Intelligence which is useful for detecting carriers and phone number types.

Error handling

Errors will be nested inside the package response:

{
  "phone_number": "+15017122661",
  "validation_errors": [],
  "call_forwarding": null,
  "url": "https://lookups.twilio.com/v2/PhoneNumbers/+15017122661",
  "calling_country_code": "1",
  "live_activity": null,
  "sim_swap": {
    "mobile_network_code": null,
    "last_sim_swap": null,
    "mobile_country_code": null,
    "carrier_name": null,
    "error_code": 60606
  },
  "caller_name": null,
  "national_format": "(501) 712-2661",
  "country_code": "US",
  "line_type_intelligence": null,
  "valid": true
}

These are some of the errors you might encounter while using the SIM swap package:

Refer to the Error and Warning dictionary for other error codes.

Combine the Lookup and Verify APIs to filter SIM swapped numbers

Now that you know how to query the Lookup API you can combine it with the Verify API to detect SIM swapped numbers before sending an OTP. Then, either require a non-phone number based verification like TOTP to continue, or put a hold on the account and try again later.

First, create a Verify Service in the Twilio Console.

The General Settings of a Verify service

Then, head over to Twilio's CodeExchange to deploy a one-time passcode verification project. We'll use the pre-built sample JavaScript app from the CodeExchange for doing phone verification and add the Lookup validation on top of it.

The Code Exchange deploy screen

Paste in your Verify service SID and click "Deploy my application". After a minute or so you can navigate to the newly deployed example app. Once you're in the live application, click on "Edit this application" which will take you back to the Twilio Console. Then, open up the start-verify.js function file.

Above const verification = … add the following code. This is the same as the cURL request above but uses Twilio's Node SDK.

const lookup = await ​​client.lookups.phoneNumbers(to).fetch({fields: 'sim_swap'});

Then add a check for the swapped in period and throw an error if the SIM was recently swapped.

if (lookup.lastSimSwap.swappedInPeriod) {
 throw new Error(
   'Unable to verify your phone number at this time.'
 );
}

Save and deploy your changes. If someone with a recently SIM-swapped number attempts to verify their phone number, the verification will fail.

What's next with Lookup

Lookup is a handy API for all sorts of phone number validation use cases including line type, carrier, call forwarding, and more.

Twilio offers a variety of secure verification options beyond telephony. Check out our Verify API channels for push, TOTP, and more. Or use Lookup to clean up a database of phone numbers with our free formatting API. Learn more in our blog post about best practices for phone number validation.