这份文档还在翻译中,预期年底前完成。欢迎您提供宝贵的意见及建议。

Signed webhooks

Signed webhooks are supported by both the Messages and Dispatch APIs and are enabled by default. They provide a method for your application to verify a request is coming from Vonage and its payload has not been tampered with during transit. When receiving a request, the incoming webhook will include a JWT token in the authorization header which is signed with your signature secret.

Validating signed webhooks provides a number of security benefits, including:

  • Ability to verify a request originates from Vonage
  • Ensures that the message has not been tampered with while in transit
  • Defends against interception and later replay

Validating signed webhooks

There are two parts to validating signed webhooks:

  1. Verify the request
  2. Verify the payload (optional)

Verify the request

Webhooks for both inbound messages and message status will include a JWT in the authorization header. Use the API key included in the JWT claims to identify which of your signature secrets has been used to sign the request. The secret used to sign the request corresponds to the signature secret associated with the api_key included in the JWT claims. You can identify your signature secret on the Dashboard. It's recommended that signature secrets be no less than 32 bits to ensure their security.

Note: The signature method drop down does not affect the method used for signing Messages API webhooks, SHA-256 is always used.

Verify the payload has not been tampered with in transit

Once you have verified the authenticity of the request, you may optionally verify the request payload has not been tampered with by comparing a SHA-256 hash of the payload to the payload_hash field found in the JWT claims. If they do not match, then the payload has been tampered with during transit. You only need to verify the payload if you are using HTTP rather than HTTPS, as Transport Layer Security (TLS) prevents MITM attacks.

NOTE: In the rare case of an internal error, it is possible that the callback service will send an unsigned callback. By returning an HTTP 5xx response, a retry will be triggered giving the system time to resolve the error and sign future callbacks.

The code example below shows how to verify a webhook signature. It is recommended you use HTTPS protocol as it ensures that the request and response are encrypted on both the client and server ends.

Prerequisites

Install dependencies

npm install jsonwebtoken
Initialize your dependencies

Create a file named verify-signed-webhook.js and add the following code:

Copy to Clipboard
const NEXMO_API_SIGNATURE_SECRET = process.env.NEXMO_API_SIGNATURE_SECRET || ''
const jwt = require("jsonwebtoken");
const sha256 = require('js-sha256');
const app = require('express')()
const bodyParser = require('body-parser')
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
  extended: true
}))

View full source

Write the code

Add the following to verify-signed-webhook.js:

Copy to Clipboard
app
    .route('/webhooks/inbound-message')    
    .post(handleInboundMessage);
function handleInboundMessage(request, response){
    const payload = Object.assign(request.query, request.body)
    let token = request.headers.authorization.split(" ")[1]
    try{
        var decoded = jwt.verify(token, NEXMO_API_SIGNATURE_SECRET, {algorithms:['HS256']});
        if(sha256(JSON.stringify(payload))!=decoded["payload_hash"]){
            console.log("tampering detected");
            response.status(401).send();
        }
        else{
            console.log("Success");
            response.status(204).send();
        }
    }
    catch(err){
        console.log('Bad token detected')
        response.status(401).send()
    }
}
app.listen(process.env.PORT || 3000)

View full source

Run your code

Save this file to your machine and run it:

node verify-signed-webhook.js

Sample Signed JWT

// header
{
  "alg": "HS256",
  "typ": "JWT",
}
// payload
{
  "iat": 1587494962,
  "jti": "c5ba8f24-1a14-4c10-bfdf-3fbe8ce511b5",
  "iss": "Vonage",
  "payload_hash" : "d6c0e74b5857df20e3b7e51b30c0c2a40ec73a77879b6f074ddc7a2317dd031b",
  "api_key": "a1b2c3d",
  "application_id": "aaaaaaaa-bbbb-cccc-dddd-0123456789ab"
}

Signed JWT Header

The contents of the signed JWT header are described in the following table:

Header Value
alg HS256
typ JWT

Signed JWT Payload

The contents of the signed JWT payload are described in the following table, using the values included in the sample signed JWT shown previously:

Field Example Value Description
iat 1587494962 The time at which the JWT was issued. Unix timestamp in SECONDS.
jti c5ba8f24-1a14-4c10-bfdf-3fbe8ce511b5 A unique ID for the JWT.
iss Vonage The issuer of the JWT. This will always be 'Vonage'.
payload_hash d6c0e74b5857df20e3b7e51b30c0c2a40ec73a77879b6f074ddc7a2317dd031b A SHA-256 hash of the request payload. Can be compared to the request payload to ensure it has not been tampered with during transit.
api_key a1b2c3d The API key associated with the account that made the original request.
application_id aaaaaaaa-bbbb-cccc-dddd-0123456789ab (Optional) The id of the application that made the original request if an application was used.