diff --git a/package.json b/package.json index 56281ca1d..023127dec 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,6 @@ "https-proxy-agent": "^5.0.0", "jsonwebtoken": "^9.0.3", "qs": "^6.14.1", - "scmp": "^2.1.0", "xmlbuilder": "^13.0.2" }, "devDependencies": { diff --git a/src/webhooks/webhooks.ts b/src/webhooks/webhooks.ts index a0cffb689..85e84f25b 100644 --- a/src/webhooks/webhooks.ts +++ b/src/webhooks/webhooks.ts @@ -1,4 +1,3 @@ -const scmp = require("scmp"); import crypto from "crypto"; import urllib from "url"; import { IncomingHttpHeaders } from "http2"; @@ -257,7 +256,10 @@ function validateSignatureWithUrl( params ); - return scmp(Buffer.from(twilioHeader), Buffer.from(signatureWithoutPort)); + return timingSafeEqual( + Buffer.from(twilioHeader), + Buffer.from(signatureWithoutPort) + ); } export function validateBody( @@ -265,7 +267,19 @@ export function validateBody( bodyHash: any[] | string | Buffer ): boolean { var expectedHash = getExpectedBodyHash(body); - return scmp(Buffer.from(bodyHash), Buffer.from(expectedHash)); + return timingSafeEqual(Buffer.from(bodyHash), Buffer.from(expectedHash)); +} + +function timingSafeEqual(userValue: Buffer, secretValue: Buffer) { + // Do not return early when lengths differ — that leaks the secret's + // length through timing. Instead, always perform a constant-time + // comparison: when the lengths match compare directly; otherwise + // compare the user input against itself (always true) and negate. + // Source: https://developers.cloudflare.com/workers/examples/protect-against-timing-attacks/ + const lengthsMatch = userValue.length === secretValue.length; + return lengthsMatch + ? crypto.timingSafeEqual(userValue, secretValue) + : !crypto.timingSafeEqual(userValue, userValue); } /**