Third parties knowing what is said in SMS conversations can be scary, but in a world filled with increasingly false and misleading information being presented as news, not knowing what was said can be just as scary. In this tutorial, learn how to add cryptographically verifiable hashes of Twilio powered SMS conversations to an immutable public ledger or blockchain powered by Pangea’s tamperproof audit and logging service.
By publishing conversation hashes to the blockchain you can provide irrefutable evidence of what was said between two parties without actually exposing the contents of the conversation publicly. With Twilio Programmable SMS and Pangea’s Javascript SDK you can deliver the power of the blockchain to your users with a few lines of code. In this tutorial, you’ll set up a free Pangea account and, using Node.js, build an SMS forwarding service that logs messages between two parties using Twilio Functions.
Requirements
- A free or paid Twilio account
- A Twilio phone number
- A free Pangea account
- Node.js (version 14.16.1 or higher)
Set up your Pangea account and Access Token
Once you’ve signed up for Pangea, log in and access the Pangea Console. Set up the Audit service by selecting Secure Audit Log from the left-hand navigation menu.
Review the benefits of the service and select Next to continue.
Create an Access Token
by selecting a token name, expiration date, and token scope or use the default values by selecting Done.
Make a note of the Config ID, service Domain, and access Token. You will use each of these values to interact with the service from your app’s code in the next step.
Note: You can quickly copy each value to your system’s clipboard using the shortcuts.
Get the code
In this section, you will configure your app to communicate with Twilio, and Pangea, with your account credentials. You’ll also configure which phone numbers your proxy will forward each message to and examine the code that does so.
Navigate to your terminal and enter the following command to clone the SMS proxy app:
git clone https://github.com/pangeacyber/audit-twilio-proxy.git
Now change the working directory to your new Node.js project, audit-twilio-proxy, with the following command:
cd audit-twilio-proxy
Open up your project directory in your preferred IDE and take a moment to explore the project files and configure it to your environments.
- package.json – Describes the project and lists dependencies. Both the Twilio and Pangea SDKs for Node.js are listed. The
devDependencies
includestwilio-run
which you’ll use to deploy to code to Twilio Functions. - .env – Contains the environment variables the app will reference.
- functions/sms-audit-proxy.js – The application source file that contains a single function to handle incoming SMS messages.
Configure and deploy the app
The application source reads 7 variables from the .env file.
ACCOUNT_SID
andAUTH_TOKEN
to authenticate your app with the Twilio service.PANGEA_DOMAIN
,PANGEA_CONFIG_ID
, andPANGEA_AUTH_TOKEN
to authenticate with the Pangea service.TARGET_NUMBER
andOWNER_NUMBER
are used to determine where to forward incoming SMS messages.
Modify the .env file by replacing each {REPLACE}
tag with its corresponding value.
For the PANGEA_
specific variables, use the three values you noted in the previous section, or retrieve them from the Pangea Console by navigating to the Secure Audit Log tab.
The Twilio values for ACCOUNT_SID
and AUTH_TOKEN
can be found on the landing page of the Twilio Console.
OWNER_NUMBER
and TARGET_NUMBER
should each be a valid E.164 phone number you’d like to test with. For example, OWNER_NUMBER
can be set to your mobile phone number and TARGET_NUMBER
to a friend’s number who you’d like to start and record an auditable message thread with. An example of an E.164 formatted number in the US is +16502223333.
Note: You can also set both OWNER_NUMBER
and TARGET_NUMBER
to your personal mobile number and reply to your own messages.
Verify your changes to the .env file with the git diff
sub-command:
git diff
The output should look similar to this:
diff --git a/.env b/.env
index 7442c6a..9fd8304 100644
--- a/.env
+++ b/.env
@@ -1,7 +1,7 @@
-ACCOUNT_SID={REPLACE}
-AUTH_TOKEN={REPLACE}
-PANGEA_DOMAIN={REPLACE}
-PANGEA_CONFIG_ID={REPLACE}
-PANGEA_AUTH_TOKEN={REPLACE}
-OWNER_NUMBER={REPLACE}
-TARGET_NUMBER={REPLACE}
+ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXX
+AUTH_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXX
+PANGEA_DOMAIN=aws.us.pangea.cloud
+PANGEA_CONFIG_ID=pci_XXXXXXXXXXXXXXXXXXXXXX
+PANGEA_AUTH_TOKEN=pts_XXXXXXXXXXXXXXXXXXXXXXXX
+OWNER_NUMBER=+13056042322
+TARGET_NUMBER=+13056042322
Code walkthrough
Inspect the contents of the /function/sms-audit-proxy.js source file before deploying the app to a Twilio Serverless environment. The file contains a single function. You will configure Twilio to invoke this function when your Twilio owned number receives an SMS. The function’s response and actions taken will be determined by the contents of the object passed in as the event
parameter.
exports.handler = function(context, event, callback) {
console.log("Event:", event);
The user defined environment variables set in the .env file will be passed into the function via the context
parameter. The Twilio account variables are read and set to accountSid
and authToken
, respectively, and used to represent an instance of the twilio
class defined in the SDK.
// Read the Twilio SID and Auth Token from the environment variables
const accountSid = context.ACCOUNT_SID;
const authToken = context.AUTH_TOKEN;
// Import the Twilio SDK
const TwilioClient = require('twilio')(accountSid, authToken);
TwilioClient
is then used to create a MessagingResponse
object which may be used to reply to the sender. Note that a reply will only be necessary when reporting an error back to the sender.
const twiml = new TwilioClient.twiml.MessagingResponse();
The Pangea SDK is imported and the PANGEA_
values are assigned to their respective variables. A PangeaConfig
object is created and used to create an AuditService
instance.
// Import the Pangea SDK
const Pangea = require('node-pangea');
// Read the Pangea Config Id and Auth Token from the environment variables
const pangeaDomain = context.PANGEA_DOMAIN;
const auditToken = context.PANGEA_AUTH_TOKEN;
const auditConfigId = context.PANGEA_CONFIG_ID;
// Instantiate a Pangea Configuration object with the endpoint domain and configId
const auditConfig = new Pangea.PangeaConfig({ domain: pangeaDomain,
configId: auditConfigId});
const auditService = new Pangea.AuditService(auditToken, auditConfig);
Before invoking the Audit Service, determine if the inbound message should be added to the secure audit log and if so who to relay the message to after it is logged. Only messages between the two numbers configured in the .env file should be logged. Each of the allowed recipient numbers are read into local variables.
const ownerNumber = context.OWNER_NUMBER;
const targetNumber = context.TARGET_NUMBER;
Determine the destinationNumber
to relay the message to:
- If the message originated from the owner, send it to the target.
- If the message came from the target, send it to the owner.
- If the message came from any other number, notify the sender that the message will be ignored by populating and returning the
MessageResponse
.
var destinationNumber;
// Determine the destination number
if(event.From.endsWith(ownerNumber)) {
// If the message is from the owner, send it to target
destinationNumber = targetNumber;
} else if(event.From.endsWith(targetNumber)) {
// If the message is from the target, send it to owner
destinationNumber = ownerNumber;
} else {
// If the message is from any other number, reply to the sender
twiml.message("AUTOMATED RESPONSE: This is a private communication channel to securely record auditable conversations. Your message will be ignored!");
return callback(null, twiml);
}
Create a JavaScript object and map the relevant event details to the log entry. Set the actor
field to the Twilio owned number that is logging and forwarding the message. The source
is set to the number that sent the incoming SMS, read from the event.From
field, and target
is set to the intended recipient or destinationNumber
. The message
is set to the body or contents of the SMS, which is also read from the event object.
// The number the original message was sent to is the number of the proxy
var proxyNumber = event.To;
// Map the event details to the auditData object, for example, the source
// is set to the number that sent the message and the target is the recipient
const auditData = {
actor: proxyNumber,
source: event.From,
target: destinationNumber,
message: event.Body,
status: event.SmsStatus,
action: "forwarded"
};
Call the log
method of the AuditService
with the auditData
. This will invoke the Pangea Service to create the log entry. The hash of the message will also be recorded on a tamper proof blockchain which can then be used to prove the conversation has not been modified. You can explore the log viewer on the Pangea Console to verify the message integrity later.
// Log the message using the Pangea Audit service. Hashes of each message will
// be recorded on a tamper proof blockchain so the conversation can be
// cryptographically proven to be unmodified.
auditService.log(auditData, auditOption)
.then(function(response) {
console.log("Response:", response.data);
When the asynchronous log
function completes the callback function is called with a single response
parameter. If the response is marked successful, use the TwilioClient
to relay the contents of the message to the destinationNumber
and return a blank response to complete the function execution.
if(response.success) {
console.log("Forwarding message to: ", destinationNumber);
// Send the logged message to the destination number
TwilioClient.messages
.create({body: event.Body, from: proxyNumber, to: destinationNumber})
.then((response) => {
console.log('SMS successfully sent');
return callback(null, twiml);
})
.catch((error) => {
console.error(error);
return callback(error);
});
}
Deploy to Twilio Functions & Assets
Twilio Functions is a serverless environment that empowers developers to quickly create and host applications. Because the package.json file lists the twilio-run
module as a development dependency, you can quickly deploy your function using the following command:
npx twilio-run deploy
The module uses the same ACCOUNT_SID
and AUTH_TOKEN
used by the application in the .env file to authenticate with your Twilio account when deploying the project.
A successful deploy will yield output similar to:
Deploying functions & assets to the Twilio Runtime
Username ACXXXXXXXXXXXXXXXXXXXXXXXXX
Password fXXX****************************
Service SID ZSXXXXXXXXXXXXXXXXXXXXXXXXXX
Environment dev
Root Directory /Users/Pangea/Development/Source/twilio-audit-proxy
Dependencies twilio, @twilio/runtime-handler, node-pangea
Env Variables PANGEA_DOMAIN, PANGEA_CONFIG_ID, PANGEA_AUTH_TOKEN, OWNER_NUMBER, TARGET_NUMBER
Runtime node14
✔ Serverless project successfully deployed
Deployment Details
Domain: pangea-audit-proxy-XXXX-dev.twil.io
Service:
pangea-audit-proxy (ZSXXXXXXXXXXXXXXXXXXXXXXXXXX)
Environment:
dev (ZEXXXXXXXXXXXXXXXXXXXXXXXXXX)
Build SID:
ZBXXXXXXXXXXXXXXXXXXXXXXXXX
Runtime:
node14
View Live Logs:
https://www.twilio.com/console/functions/editor/ZSXXXXXXXXXXXXXXXXXXXXXXXXXX/environment/ZEXXXXXXXXXXXXXXXXXXXXXXXXXX
Functions:
https://pangea-audit-proxy-XXXX-dev.twil.io/sms-audit-proxy
Assets:
Congratulations! You just deployed a Pangea enabled Twilio service. Make a note of the Functions URL as it will be needed to configure your Twilio programmable number.
If you do not already have a Twilio number, follow these instructions:
- Go to your Phone Numbers Dashboard.
- Click Buy a Number.
- Search for a number that suits you.
- Click Buy
- Confirm your purchase, then click Setup Number.
Otherwise, navigate to the Active numbers panel of the Twilio console and select the number you’d like to use with this service.
Under Messaging, look for the line that says “A message comes in.” Change the first box to “Webhook” and add the function URL to the second box, then click Save to save your configuration. The function you deployed will now be invoked every time an SMS is sent to this number.
Test and verify the conversation audit trail
That’s it! You now have a secure audit trail between the OWNER_NUMBER
and TARGET_NUMBER
you configured in the .env file. Use a cellphone with either number to send an SMS to the Twilio number you purchased and configured to invoke your function. The SMS message will be logged by the Pangea service and forwarded to the other participant, similarly their replies will be forwarded back to you creating a conversation thread on the SMS apps on both your phones.
To later view the conversation or present the verified proof that messages were not altered or deleted, navigate back to the Pangea Console, select Secure Audit Log from the left-hand navigation menu, and then select View Logs.
You can expand each message to view the sender and recipient details, labeled as source and target, respectively.
The green lock to the left of each message indicates that its hash has been published to the ARWeave blockchain and verified. To view the transaction on ViewBlock.io, click the lock icon and then click the View button next to Published Root.
Note: Publishing occurs once per hour so it may take up to an hour for a record to show as Verified.
Conclusion
In this article you learned how to build and deploy an SMS proxy that records conversations and provides users with verifiable proof that the conversation was not altered. Most telecom providers maintain records of the messages sent over their network, but even the largest carriers are susceptible to tampered records on their centralized data sources. It may also be impractical for your users to request conversation logs from carriers as the process may require a legal process or subpoena. Utilizing a Blockchain with your Twilio and Pangea powered proxy you are able to quickly solve both these problems for your user.
Nicolas Vautier is a Developer Advocate at Pangea Cyber and is a privacy and data security enthusiast. If you have questions, comments, or ideas for future posts, Nicolas can be reached at nicolas.vautier@pangea.cloud. Follow him on Twitter @DeveloperEnvY to join his journey as he helps unify security services for app builders @PangeaCyber.