This content originally appeared on Twilio Blog and was authored by Matt Coser
Sick of being SHAKEN down by robocallers? In this post, we will STIR it up by using Caller ID Attestation levels to route inbound calls with various Programmable Voice features.
What is SHAKEN/ STIR?
Let’s start from the beginning. Well, maybe not the very beginning – but let’s start with a basic understanding of the SHAKEN and STIR protocols and how they are currently implemented.
You may notice these protocols being used simultaneously as SHAKEN/STIR or STIR/SHAKEN. Either is correct, and both mean the same thing.
- STIR - Secure Telephone Identity Revisited
- STIR is a protocol developed by IETF, describing the process of verifying if a party is allowed to use a certain number.
- SHAKEN - Signature-based Handling of Asserted Information Using toKENs
- SHAKEN focuses on how STIR should be implemented and deployed to carrier networks.
Before the early 2000s, caller ID spoofing was much more difficult. As SIP started gaining popularity, spoofing increased because SIP From numbers could be very easily changed without the recipient knowing.
You may also have noticed robocalls coming from numbers with the same area code or exchange as your own number. This tactic, while less egregious than using the IRS 800 number as a From
, can still deceive end users by making them think the caller is familiar or from their area. For instance, I might think a call from the same local exchange is from my doctor, school, or someone in my community.
The Do Not Originate (DNO) List came a long way in preventing the spoofing of high-profile and recognizable numbers. SHAKEN/STIR goes further, and enables originating carriers to digitally sign the call with an Attestation Level. The Attestation Level applied to the call indicates the certainty level of the originating provider and whether the caller is actually allowed to use the Caller ID.
- Full Attestation (A) - The identity of the caller is known, and they have the right to use the Caller ID for this call.
- Partial Attestation (B) - The originator knows the identity of the caller, but cannot confirm their right to use the Caller ID.
- Gateway Attestation (C) - The identity of the caller is not known, as the call originated outside of the network, or internationally. C level is applied when A or B cannot be.
Due to the flexibility of Twilio's platform, users can take advantage of SHAKEN/STIR attestation in a few different ways.
Outbound Calls
For calls originating on Twilio’s platform, a From number is required.
When you make a POST
to /Calls
, a new Call resource will be created.
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);
client.calls
.create({
twiml: '<Response><Say>Ahoy there!</Say></Response>',
to: '+15558675310',
from: '+15552223214'
})
.then(call => console.log(call.sid));
This From
number is what is displayed on the recipient’s device. By default, Twilio accounts can only make calls using a from number which is either:
- A Twilio number owned by said account
- A non Twilio number, validated using either
- the Outgoing Caller ID API
- or the Caller ID validation tool in Console
If an outbound call is made using a non validated Caller ID, or a Twilio Number not belonging to the account, the call will fail with a 21210 Error.
Otherwise, Twilio will cryptographically sign the call with an attestation level that best represents the state of the Twilio number (or verified Caller ID) used to make the call. The best part - you are in complete control of the attestation level of the From number.
By setting up a Business Profile and adding your selected Phone Numbers to the SHAKEN/STIR TrustProduct, you can enjoy A level attestation on all outbound calls using those Phone Numbers.
Using a Verified CallerID as the From number on outbound calls will result in B level attestation.
Using a non verified Caller ID, or a Twilio Phone Number not added to a SHAKEN/STIR Trust Product will result in a C level attestation.
Bottom line - if you want high deliverability of your Outbound API calls, use SHAKEN/STIR.
Inbound Calls
Calls terminating on Twilio’s platform use the From number assigned by the originating carrier.
In your call logs, the From number is assigned by the originating carrier, and the To number is your Twilio Inbound number. When a call comes in to your Twilio number, we make a webhook to the defined URL. As part of this HTTP request, we include the StirVerstat
parameter,
Caller ID Attestation is still being rolled out, so there may be cases where the StirVerstat
is missing or undefined. These cases should diminish over time.
We also send the CallToken
parameter, which includes the actual jwt token containing the signed attestation information. This can be decoded with various libraries, or on jwt.io.
Let’s Build an App
This is where the fun begins!
Now that we know what SHAKEN/STIR is and how it works with Twilio, we should build something neat!
Let’s leverage the StirVerstat
parameter to route the inbound calls in interesting ways using some super powerful Programmable Voice features.
Mobile providers use attestation as one metric to apply ‘nuisance labeling’, or to block calls on behalf of their subscribers.
Why can’t you do something similar with your Twilio Programmable Voice app?
Prerequisites
Disclaimer: Your app and use case will have different requirements – don’t just copypasta this code into prod. These examples are only meant to demonstrate the power of combining Twilio’s diverse programmable call routing capabilities with the cutting edge SHAKEN/STIR attestation.
If you want to follow along, you will also need to perform a few steps:
- Sign up for a free Twilio account, if you don’t have one already.
- In the Twilio Console, make a Service, and copy the js files as Functions, using this structure:
- Purchase a Phone Number and point it at the
inbound-gateway.js
Function URL. - While it is not required for inbound calls, I recommend onboarding to TrustHub and going through these steps to set up SHAKEN/STIR functionality for outbound calls.
Inbound calls trigger a webhook to the inbound-gateway
Function. The StirVerstat
value determines the ‘route’ to take, which then triggers ‘callbacks’.
In this scenario, I chose to route differently for every attestation level simply to demonstrate the power of Programmable Voice. In your case, you may decide another approach is more appropriate or practical.
And with that, we can begin.
Attestation Level Routing
To implement these call flows based on the Attestation level of the inbound caller, we must first create a ‘router’ Function which inspects the StirVerstat
parameter.
Using a switch statement, we can check the parameter against our known list of values and have fine-grained control over each possible scenario.
<Redirect> is one of my favorite TwiML verbs. It is simple, and powerful. You might think of this router function like an API gateway in a microservice architecture. The incoming requests are analyzed and forwarded according to the payload. Without <Redirect>, all of this logic would need to be in one Function.
Call Forwarding
If the caller is stamped with A attestation, then we know the caller id is trusted. Let’s just send them straight through to our cell phone - no action required. A simple <Dial> is all you need.
Keep in mind, the CallerID of the subsequent outbound <Dial> call will be the From number of the originating inbound call. You can override this by providing the CallerId attribute.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial>415-123-4567</Dial>
</Response>
Call Screening
‘It’s all your fault. I screen my phone calls’ - Gwen Stefani
We’ve all been there. But Twilio wasn’t around when Gwen and Tony wrote “Spiderwebs”, so their idea of call screening was probably "let the answering machine get it, and I’ll pick it up if they sound cool."
Today, Gwen Stefani is still writing bangers, but we have Twilio to help us with call screening. In this example, if the call is stamped with B attestation, the recipient will hear a recording of the caller’s name before they are connected.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say> Hello. Please record your name. </Say>
<Record action="myapp.com/callbacks/record-action" recordingStatusCallback="myapp.com/callbacks/record-status" />
</Response>
The inbound caller will hear the <Say> message and will be prompted to record a message notifying the recipient who is calling.
As soon as the caller is done leaving a message, the caller will hear hold music while the recording is processed and the call is forwarded.
As soon as the recording is ready, the call is updated with a <Dial>. The new RecordingURL is populated as the <Number> URL. When the recipient picks up, they will hear the recording and can decide to hang up or wait to be connected.
Voicemail
If the Attestation level of the inbound call is C, we can send them straight to a voicemail program. This Function returns a <Record> verb, and then sends a SMS to a number of your choice with a link to the recording file.
/**
* Send the caller direct to voicemail, then notify me via SMS
*/
exports.handler = function(context, event, callback) {
if (event.RecordingUrl) {
console.log('this is the action callback');
const twilioClient = context.getTwilioClient();
const msg_from = event.To;
const msg_to = context.PERSONAL_CELL;
const msg_body = `You have a new voicemail from ${event.From} - ${event.RecordingUrl}`
twilioClient.messages
.create({ to: msg_to, from: msg_from, body: msg_body })
.then((result) => {
console.log('Created message using callback');
console.log(result.sid);
return callback();
})
.catch((error) => {
console.error(error);
return callback(error);
});
}
else {
console.log('This is the initial TwiML fetch...')
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say('Thank you for calling. I am not available right now. Please leave a message after the tone. ');
twiml.record({
maxLength: 20,
finishOnKey: '*'
});
twiml.say('I did not receive a recording. Good bye.');
return callback(null, twiml);
}
};
By taking advantage of a little known feature of the <Record> verb, we can make this single Function dual purpose.
If you do not provide a <Record> action URL, then Twilio will hit the same document with the action callback.
The RecordingURL parameter is not present in the initial TwiML fetch, so we return <Record>.
Whereas, the presence of RecordingURL in the request indicates the action callback, so we send an SMS with the link.
Human Detection
Human Detection is similar in logic to Answering Machine Detection in that we want to determine if the caller is a human or a machine. In the context of outbound calls, Answering Machine detection makes a lot of sense. For example, we might want to use a different call flow if a human picks up vs. a machine. In the context of inbound calls, Human Detection is more useful to root out robocalls.
As mentioned before, the StirVerstat
parameter may be missing, or have a value like “Validation-Failed” or “No-TN-Validation”. In these cases, we will use Human Detection.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather action='/callbacks/gather-action' num_digits='2'>
<Say>To prove you are human, please enter the number after nine.</Say>
</Gather>
<Say>We didn't receive any d t m f input. Goodbye</Say>
</Response>
Before the call is bridged, the caller will hear a <Say> message asking them to solve a captcha or puzzle.
In this example, we ask the caller to “Enter the number that comes after 9”
This can be anything, though - the harder for computers, the better.
After the user enters digits, Twilio makes a request to the <Gather> action url.
/**
* <Gather> action callback
* https://www.twilio.com/docs/voice/twiml/gather#action
*
* if the digit doesn't match, hangup. otherwise, send them through.
*/
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
if (event.Digits != '10') {
twiml.hangup()
}
else {
twiml.dial(context.PERSONAL_CELL)
}
return callback(null, twiml);
};
If the correct digits are not entered, the call is ended using <Hangup>. Otherwise, we use a <Dial> to forward the call to our cell.
You may decide to include these in another route, or just log them for visibility. Either way, SHAKEN/STIR rollout is still ongoing, so a missing or unexpected StirVerstat
value is no cause for immediate concern.
Conclusion
If you were building along, you can now call your number and test it out! You may need to adjust the switch statement in the router function if you don't have access to enough From numbers to test each path.
Either way, you are now one step closer to becoming a SHAKEN/STIR aficionado! Whether you are trying to improve call deliverability, dynamically route incoming calls, or just analyze what types of numbers are calling your app, then Twilio has you covered with Trusted Calling.
We can’t wait to see what you STIR up!
Matt Coser is a Senior Field Security Engineer at Twilio. His focus is on telecom security, and empowering Twilio’s customers to build safely. Hit him up on linkedin to connect and discuss more.
This content originally appeared on Twilio Blog and was authored by Matt Coser
Matt Coser | Sciencx (2023-04-26T17:00:09+00:00) Fun Call Flows Using SHAKEN/STIR Caller ID Attestation Levels. Retrieved from https://www.scien.cx/2023/04/26/fun-call-flows-using-shaken-stir-caller-id-attestation-levels/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.