alexa-verifier
Advanced tools
Comparing version 3.0.2 to 4.0.0
@@ -0,1 +1,6 @@ | ||
# 4.0.0 | ||
* BREAKING: use sha256 for signature verification, as per https://developer.amazon.com/en-US/docs/alexa/custom-skills/host-a-custom-skill-as-a-web-service.html#check-request-signature. fixes #68 | ||
* update `sinon` and `unroll` dev deps | ||
# 3.0.1 | ||
@@ -2,0 +7,0 @@ * fix `engines` field in `package.json` |
@@ -11,2 +11,3 @@ import crypto from 'crypto' | ||
const SIGNATURE_FORMAT = 'base64' | ||
const CHARACTER_ENCODING = 'utf8' | ||
@@ -21,2 +22,3 @@ | ||
fetchCert(options, function (er, pem_cert) { | ||
if (er) | ||
@@ -36,4 +38,4 @@ return callback(er) | ||
function isValidSignature (pem_cert, signature, requestBody) { | ||
const verifier = crypto.createVerify('RSA-SHA1') | ||
verifier.update(requestBody, 'utf8') | ||
const verifier = crypto.createVerify('RSA-SHA256') | ||
verifier.update(requestBody, CHARACTER_ENCODING) | ||
return verifier.verify(pem_cert, signature, SIGNATURE_FORMAT) | ||
@@ -40,0 +42,0 @@ } |
{ | ||
"name": "alexa-verifier", | ||
"version": "3.0.2", | ||
"version": "4.0.0", | ||
"description": "Verify HTTP requests sent to an Alexa skill are sent from Amazon", | ||
@@ -25,9 +25,10 @@ "main": "index.js", | ||
"node-forge": "^1.2.1", | ||
"validator": "^9.0.0" | ||
"validator": "^13.7.0" | ||
}, | ||
"devDependencies": { | ||
"nock": "^9.0.2", | ||
"sinon": "^4.1.5", | ||
"tap": "^15.0.9", | ||
"unroll": "1.4.0" | ||
"esmock": "^2.6.0", | ||
"nock": "^13.0.0", | ||
"sinon": "^17.0.1", | ||
"tap": "^16.0.0", | ||
"unroll": "1.6.0" | ||
}, | ||
@@ -34,0 +35,0 @@ "engines": { |
# alexa-verifier | ||
[](https://travis-ci.org/mreinstein/alexa-verifier) | ||
 | ||
Verify HTTP requests sent to an Alexa skill are sent from Amazon. | ||
@@ -6,0 +7,0 @@ |
import { test } from 'tap' | ||
import crypto from 'crypto' | ||
import esmock from 'esmock' | ||
import fs from 'fs' | ||
import url from 'url' | ||
import verifier from '../index.js' | ||
import sinon from 'sinon' | ||
import { dirname } from 'path' | ||
import { fileURLToPath } from 'url' | ||
const cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-10.pem' // latest valid cert | ||
const __dirname = dirname(fileURLToPath(import.meta.url)) | ||
const cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-12.pem' // latest valid cert | ||
const rsaSha256Key = fs.readFileSync(`${__dirname}/mocks/rsa_sha256`).toString() | ||
const validPem = fs.readFileSync(`${__dirname}/mocks/rsa_sha256_pub`).toString() | ||
test('handle missing cert_url parameter', function (t) { | ||
@@ -115,8 +126,23 @@ const signature = 'JbWZ4iO5ogpq1NhsOqyqq/QRrvc1/XyDwjcBO9wWSk//c11+gImmtWzMG9tDEW40t0Xwt1cnGU93DwUZQzMyzJ5CMi+09qVQUSIHiSmPekKaQRxS0Ibu7l7cXXuCcOBupbkheD/Dsd897Bm5SQwd1cFKRv+PJlpmGKimgh2QmbivogsEkFl8b9SW48kjKWazwj/XP2SrHY0bTvwMTVu7zvTcp0ZenEGlY2DNr5zSd1n6lmS6rgAt1IPwhBzqI0PVMngaM0DQhB0wUPj3QoIUh0IyMVAQzRFbQpS4UGrA4M9a5a+AGy0jCQKiRCI+Yi9iZYEVYvfafF/lyOUHHYcpOg==' | ||
test('handle valid signature', function (t) { | ||
const ts = '2017-02-10T07:27:59Z' | ||
test('handle valid signature', async function (t) { | ||
const verifier = await esmock('../index.js', { | ||
'../fetch-cert.js': { | ||
default: function fetchCert (options, callback) { | ||
callback(undefined, validPem) | ||
} | ||
}, | ||
'../validate-cert.js': { | ||
default: function validateCert (pem_cert) { | ||
// we're using our mocked sha256 pub/private keypair, so skip all the validation unrelated to | ||
// signature checking. | ||
} | ||
}, | ||
}) | ||
const ts = '2019-09-01T07:27:59Z' | ||
const now = new Date(ts) | ||
const clock = sinon.useFakeTimers(now.getTime()) | ||
const cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem' | ||
const signature = 'Qc8OuaGEHWeL/39XTEDYFbOCufYWpwi45rqmM2R4WaSEYcSXq+hUko/88wv48+6SPUiEddWSEEINJFAFV5auYZsnBzqCK+SO8mGNOGHmLYpcFuSEHI3eA3nDIEARrXTivqqbH/LCPJHc0tqNYr3yPZRIR2mYFndJOxgDNSOooZX+tp2GafHHsjjShCjmePaLxJiGG1DmrL6fyOJoLrzc0olUxLmnJviS6Q5wBir899TMEZ/zX+aiBTt/khVvwIh+hI/PZsRq/pQw4WAvQz1bcnGNamvMA/TKSJtR0elJP+TgCqbVoYisDgQXkhi8/wonkLhs68pN+TurbR7GyC1vxw==' | ||
const body = { | ||
@@ -147,6 +173,10 @@ "version": "1.0", | ||
verifier(cert_url, signature, JSON.stringify(body), function (er) { | ||
const requestEnvelope = JSON.stringify(body) | ||
const signer = crypto.createSign('RSA-SHA256') | ||
signer.update(requestEnvelope) | ||
const signature = signer.sign(rsaSha256Key, 'base64'); | ||
verifier(cert_url, signature, requestEnvelope, function (er) { | ||
t.equal(er, undefined) | ||
clock.restore() | ||
t.end() | ||
}) | ||
@@ -156,8 +186,21 @@ }) | ||
test('handle valid signature with double byte utf8 encodings', function (t) { | ||
test('handle valid signature with double byte utf8 encodings', async function (t) { | ||
const verifier = await esmock('../index.js', { | ||
'../fetch-cert.js': { | ||
default: function fetchCert (options, callback) { | ||
callback(undefined, validPem) | ||
} | ||
}, | ||
'../validate-cert.js': { | ||
default: function validateCert (pem_cert) { | ||
// we're using our mocked sha256 pub/private keypair, so skip all the validation unrelated to | ||
// signature checking. | ||
} | ||
}, | ||
}) | ||
const ts = '2017-04-05T12:02:36Z' | ||
const now = new Date(ts) | ||
const clock = sinon.useFakeTimers(now.getTime()) | ||
const cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem' | ||
const signature = 'WLShxe8KMwHUt8hVD5+iE4tDO+J8Li21yocDWnq8LVRpE2PMMWCxjQzOCzyoFm4i/yW07UKtKQxcnzB44ZEdP6e6HelwBwEdP4lb8jQcc5knk8SuUth4N7cu6Em8FPOdOJdd9idHbO/p8BTb14wgua5n+1SDKHm+wPikOVsfCMYsXcwRWx5FsgP1wVPrDsCHN/ISiCXz+UuMnd6H0uRNdLZ/x/ikPkknh+P1kuFa2a2LN4r57IwBDAxkdf9MzXEexSOO0nWLnyJY2VAFB+O7JKE39CwMJ1+YDOwTTTLjilkCnSlfnr6DP4HPGHnYhh2HQZle8UBrSDm4ntflErpISQ==' | ||
const body = { | ||
@@ -195,2 +238,7 @@ "version":"1.0", | ||
const requestEnvelope = JSON.stringify(body) | ||
const signer = crypto.createSign('RSA-SHA256') | ||
signer.update(requestEnvelope) | ||
const signature = signer.sign(rsaSha256Key, 'base64'); | ||
verifier(cert_url, signature, JSON.stringify(body), function (er) { | ||
@@ -206,6 +254,3 @@ t.equal(er, undefined) | ||
const ts = '2017-04-05T12:02:36Z' | ||
const now = new Date(ts) | ||
const clock = sinon.useFakeTimers(now.getTime()) | ||
const cert_url = 'https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem' | ||
const signature = 'Qc8OuaGEHWeL/39XTEDYFbOCufYWpwi45rqmM2R4WaSEYcSXq+hUko/88wv48+6SPUiEddWSEEINJFAFV5auYZsnBzqCK+SO8mGNOGHmLYpcFuSEHI3eA3nDIEARrXTivqqbH/LCPJHc0tqNYr3yPZRIR2mYFndJOxgDNSOooZX+tp2GafHHsjjShCjmePaLxJiGG1DmrL6fyOJoLrzc0olUxLmnJviS6Q5wBir899TMEZ/zX+aiBTt/khVvwIh+hI/PZsRq/pQw4WAvQz1bcnGNamvMA/TKSJtR0elJP+TgCqbVoYisDgQXkhi8/wonkLhs68pN+TurbR7GyC1vxw==' | ||
const signature = '' | ||
const body = { | ||
@@ -212,0 +257,0 @@ "version": "1.0", |
@@ -121,3 +121,3 @@ import fs from 'fs' | ||
test('fails on expired certificate (Not After)', function (t) { | ||
const pem = fs.readFileSync(__dirname + '/cert-expired.pem') | ||
const pem = fs.readFileSync(__dirname + '/mocks/cert-expired.pem') | ||
t.ok(validate(pem) === 'invalid certificate validity (past expired date)') | ||
@@ -128,5 +128,5 @@ t.end() | ||
test('approves valid certifcate', function (t) { | ||
const pem = fs.readFileSync(__dirname + '/echo-api-cert-10.cer') | ||
const pem = fs.readFileSync(__dirname + '/mocks/echo-api-cert-12.cer') | ||
t.ok(validate(pem) === undefined, 'Certificate should be valid') | ||
t.end() | ||
}) |
45098
17
591
48
5
2
+ Addedvalidator@13.12.0(transitive)
- Removedvalidator@9.4.1(transitive)
Updatedvalidator@^13.7.0