Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Twilio SDK Not Validating Signature #1068

Open
dcaponi opened this issue Jan 27, 2025 · 6 comments
Open

Twilio SDK Not Validating Signature #1068

dcaponi opened this issue Jan 27, 2025 · 6 comments

Comments

@dcaponi
Copy link

dcaponi commented Jan 27, 2025

Hi - Please see details below

The Setup

I'm catching requests from Twilio on a SvelteKit API endpoint. I get the request and all the goodies off of it just fine with the following code

const validateExtractMessage = (url: URL, request: Request): TwilioMessage => {
  const twilio_sig = request.headers.get('x-twilio-signature') ?? '';

  const from = url.searchParams.get('From');
  const text = url.searchParams.get('Body');
  const sms_sid = url.searchParams.get('MessageSid');

  const params: Record<string, string> = {};
  url.searchParams.forEach((value, key) => {
    params[key] = value
  });

  const messageRequestValid = twilio.validateRequest(
    TWILIO_AUTH_CREDENTIAL,
    twilio_sig,
    url.toString(),
    params
  );

  console.log("twilio valid", messageRequestValid)
  return { from, text, sms_sid, twilio_sig, messageRequestValid }
}

The Problem

I've logged out every variable and everything looks correct, and according to all the documentation I could find this seems to be the correct way to validate a signature. I know I have the correct auth credential exported to my environment as I can send texts no problem. The signature is definitely there and all I got from the docs was to send back the url.toString() and params like so.

According to the security docs Im supposed to be sending back like so

const params = {
  CallSid: 'CA1234567890ABCDE',
  Caller: '+12349013030',
  Digits: '1234',
  From: '+12349013030',
  To: '+18005551212',
};

however my requests dont have digits or callers (Is there a messaging centric doc I should be looking at 🤔)? I also noticed a Very subtle callout that these need to be alphabetized?

Then, sort the list of POST variables by the parameter name (using Unix-style case-sensitive sorting order):

What I tried

  1. The code you see above
  2. Setting the url to be my callback url configured in the portal without trailing slash
  3. passing {} for params, alphabetizing params.

If there's a fix or if this is a known issue I'd really appreciate it.

@manisha1997
Copy link
Contributor

Can you please mention the version where you found this bug? With the latest release, we have been introduced a fix for twilio signature validation. https://github.com/twilio/twilio-node/releases/tag/5.4.3

@dcaponi
Copy link
Author

dcaponi commented Feb 4, 2025

Can you please mention the version where you found this bug? With the latest release, we have been introduced a fix for twilio signature validation. https://github.com/twilio/twilio-node/releases/tag/5.4.3

I'm on 5.4.2 so I suppose a bump is required in order to catch your fix.

@dcaponi
Copy link
Author

dcaponi commented Feb 8, 2025

@manisha1997 it looks like that wasn't it. Same code as above now running 5.4.3 and it still comes back as invalid.

@leon19
Copy link
Contributor

leon19 commented Feb 10, 2025

Hi @dcaponi, are you using a URL without a path as a callback?

For example, something like https://example.com?

The validate function relies on new URL(), so when you do new URL("https://example.com").toString(), you'll get https://example.com/ (mind the ending /), meaning that the URL the Twilio server is using to sign the request is not the same that the validation function is using.

Adding a path or an ending slash to your URL callback should fix your issue.

@dcaponi
Copy link
Author

dcaponi commented Feb 10, 2025

Hey @leon19

Are you using a URL without a path as a callback?

No, the callback URL I entered into the Messaging Configuration section for my number is https://example.com/callback

Adding a path or an ending slash to your URL callback should fix your issue.

I did try that to no avail though. The URL with the / isn't even being hit on my app side. I did try just setting the url to whatever I have configured in the Twilio dashboard as well (without trailing slash or query params)

const messageRequestValid = twilio.validateRequest(
    TWILIO_AUTH_CREDENTIAL,
    twilio_sig,
    "https://example.com/callback",
    alphabetizeParams(params)
);

@dcaponi
Copy link
Author

dcaponi commented Feb 16, 2025

So anyway if I had to guess, one or more of the following must be happening -

  • Twilio's backend is creating a signature from all the params I get back in the query string in some order that is not alphabetical or as is when I unpack the query params.
    • I'd try to crack this myself but there's something like 19 params and 19! combinations which would take me like 300 years to figure out 😅
  • the backend is generating the signature from some subset of those params.
  • the backend is creating the signature from some other params that Im not getting back with the request.

Im fairly certain if there's any guidance in addition to the existing security doc that's focused on SMS validation (this one seems to be focused on call validation and the advice given isn't helping) that outlines the params (in order) required for signature validation that would pretty much get me over the hump and I can stop worrying about hackers pinging the crap out of my webhook and I have no way of knowing if its coming from Twilio 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants