My Apps

    Verify Webhook Signatures

    Verify Webhook Signatures

    To protect your application against man-in-the-middle and replay attacks it is essential to verify webhook signatures. Verification ensures that the webhook payloads were actually sent by Box and that the contents of the payloads have not been changed in transport.

    Verify with SDK

    Although it is possible to verify SDKs manually using your own code, convenience methods are provided in our SDKs.

    .NET
    using Box.V2.Managers;
    
    var body = "{\"type\":\"webhook_event\",\"webhook\":{\"id\":\"1234567890\"},\"trigger\":\"FILE.UPLOADED\",\"source\":{\"id\":\"1234567890\",\"type\":\"file\",\"name\":\"Test.txt\"}}";
    var headers = new Dictionary<string, string>()
    {
        { "box-delivery-id", "f96bb54b-ee16-4fc5-aa65-8c2d9e5b546f" },
        { "box-delivery-timestamp", "2020-01-01T00:00:00-07:00" },
        { "box-signature-algorithm", "HmacSHA256" } ,
        { "box-signature-primary", "6TfeAW3A1PASkgboxxA5yqHNKOwFyMWuEXny/FPD5hI=" },
        { "box-signature-secondary", "v+1CD1Jdo3muIcbpv5lxxgPglOqMfsNHPV899xWYydo=" },
        { "box-signature-version", "1" }
    };
    var primaryKey = "Fd28OJrZ8oNxkgmS7TbjXNgrG8v";
    var secondaryKey = "KWkROAOiof4zhYUHbAmiVn63cMj"
    
    bool isValid = BoxWebhooksManager.VerifyWebhook(
        deliveryTimestamp: headers["box-delivery-timestamp"],
        signaturePrimary: headers["box-signature-primary"],
        signatureSecondary: headers["box-signature-secondary"],
        payload: body,
        primaryWebhookKey: primaryKey,
        secondaryWebhookKey: secondaryKey
    );
    Java
    // Webhook message contents are shown for demonstration purposes
    // Normally these would come from your HTTP handler
    
    // Webhook message HTTP body
    String messagePayload = "{"
        + "\"type\":\"webhook_event","
        + "\"webhook\":{"
        +   "\"id\":\"1234567890\""
        + "},"
        + "\"trigger\":\"FILE.UPLOADED\","
        + "\"source\":{"
        +   "\"id\":\"1234567890\","
        +   "\"type\":\"file\","
        +   "\"name\":\"Test.txt\""
        + "}}";
    
    // Webhook message HTTP headers
    Map<String, String> messageHeaders = new HashMap<String, String>();
    headers.put("BOX-DELIVERY-ID", "f96bb54b-ee16-4fc5-aa65-8c2d9e5b546f");
    headers.put("BOX-DELIVERY-TIMESTAMP", "2020-01-01T00:00:00-07:00");
    headers.put("BOX-SIGNATURE-ALGORITHM", "HmacSHA256");
    headers.put("BOX-SIGNATURE-PRIMARY", "6TfeAW3A1PASkgboxxA5yqHNKOwFyMWuEXny/FPD5hI=");
    headers.put("BOX-SIGNATURE-SECONDARY", "v+1CD1Jdo3muIcbpv5lxxgPglOqMfsNHPV899xWYydo=");
    headers.put("BOX-SIGNATURE-VERSION", "1");
    
    // Your application's webhook keys, obtained from the Box Developer Console
    String primaryKey = "4py2I9eSFb0ezXH5iPeQRcFK1LRLCdip";
    String secondaryKey = "Aq5EEEjAu4ssbz8n9UMu7EerI0LKj2TL";
    
    BoxWebHookSignatureVerifier verifier = new BoxWebHookSignatureVerifier(primaryKey, secondaryKey);
    boolean isValidMessage = verifier.verify(
        headers.get("BOX-SIGNATURE-VERSION"),
        headers.get("BOX-SIGNATURE-ALGORITHM"),
        headers.get("BOX-SIGNATURE-PRIMARY"),
        headers.get("BOX-SIGNATURE-SECONDARY"),
        messagePayload,
        headers.get("BOX-DELIVERY-TIMESTAMP")
    );
    
    if (isValidMessage) {
        // Message is valid, handle it
    } else {
        // Message is invalid, reject it
    }
    Python
    body = b'{"webhook":{"id":"1234567890"},"trigger":"FILE.UPLOADED","source":{"id":"1234567890","type":"file","name":"Test.txt"}}'
    headers = {
        'box-delivery-id': 'f96bb54b-ee16-4fc5-aa65-8c2d9e5b546f',
        'box-delivery-timestamp': '2020-01-01T00:00:00-07:00',
        'box-signature-algorithm': 'HmacSHA256',
        'box-signature-primary': '4KvFa5/unRL8aaqOlnbInTwkOmieZkn1ZVzsAJuRipE=',
        'box-signature-secondary': 'yxxwBNk7tFyQSy95/VNKAf1o+j8WMPJuo/KcFc7OS0Q=',
        'box-signature-version': '1',
    }
    is_validated = Webhook.validate_message(body, headers, primary_key, secondary_key)
    print('The webhook message is validated to: {0}'.format(is_validated))
    Node
    const BoxSDK = require('box-node-sdk');
    let body = '{"type":"webhook_event","webhook":{"id":"1234567890"},"trigger":"FILE.UPLOADED","source":{"id":"1234567890","type":"file","name":"Test.txt"}}',
    	headers = {
    		'box-delivery-id': 'f96bb54b-ee16-4fc5-aa65-8c2d9e5b546f',
    		'box-delivery-timestamp': '2020-01-01T00:00:00-07:00',
    		'box-signature-algorithm': 'HmacSHA256',
    		'box-signature-primary': '6TfeAW3A1PASkgboxxA5yqHNKOwFyMWuEXny/FPD5hI=',
    		'box-signature-secondary': 'v+1CD1Jdo3muIcbpv5lxxgPglOqMfsNHPV899xWYydo=',
    		'box-signature-version': '1'
    	},
    	primaryKey = 'SamplePrimaryKey',
    	secondaryKey = 'SampleSecondaryKey';
    
    let isValid = BoxSDK.validateWebhookMessage(body, headers, primaryKey, secondaryKey);
    if (isValid) {
    	// message is valid, accept
    } else {
    	// message is NOT valid, reject
    }

    Verify manually

    If using our SDKs is not an option the following steps describe the basics of how to verify a signature.

    1. Ensure valid timestamp

    Firstly, ensure that the timestamp in the BOX-DELIVERY-TIMESTAMP header of the payload is no older than ten minutes.

    Node
      var timestamp = headers['BOX-DELIVERY-TIMESTAMP'];
      var date = Date.parse(timestamp);
      var expired = Date.now() - date > 10*60*1000;
      ```
    
    </Tab>
    <Tab title='Python'>
    
    ```py
      import dateutil.parser
      import pytz
      import datetime
    
      timestamp = headers["BOX-DELIVERY-TIMESTAMP"]
      date = dateutil.parser.parse(timestamp).astimezone(pytz.utc)
    
      now = datetime.datetime.now(pytz.utc)
      delta = datetime.timedelta(minutes=10)
      expiry_date = now - deltaMinutes
    
      expired = date >= expiry_date
      ```
    
    </Tab>
    
    </Tabs>
    
    ### 2. Calculate HMAC signature
    
    Then, calculate the HMAC of the payload using either of the two signatures
    found in the application's configuration on the [developer console][console].
    
    Make sure to append the bytes of the body of the payload first, and then the
    bytes of the timestamp found in the `BOX-DELIVERY-TIMESTAMP` header.
    
    <Tabs>
    
    <Tab title='Node'>
    
      ```js
      var crypto = require('crypto');
    
      var primaryKey = '...';
      var secondaryKey = '...';
    
      var payload = '{"type":"webhook_event"...}';
    
      var hmac1 = crypto.createHmac('sha256', primaryKey);
      hmac1.update(payload);
      hmac1.update(timestamp);
    
      var hmac2 = crypto.createHmac('sha256', secondaryKey);
      hmac2.update(payload);
      hmac2.update(timestamp);
      ```
    
    </Tab>
    <Tab title='Python'>
    
      ```py
      import hmac
      import hashlib
    
      primary_key = '...'
      secondary_key = '...'
    
      payload = "{\"type\":\"webhook_event\"...}"
    
      bytes = bytes(payload, 'utf-8') + bytes(timestamp, 'utf-8')
      
      hmac1 = hmac.new(primary_key, bytes, hashlib.sha256).digest()
      hmac2 = hmac.new(secondary_key, bytes, hashlib.sha256).digest()
      ```
    
    </Tab>
    
    </Tabs>
    
    ### 4. Convert to Base64
    
    Make sure to convert the HMAC to a `Base64` encoded digest.
    
    <Tabs>
    
    <Tab title='Node'>
    
      ```js
      var digest1 = hmac1.digest('base64');
      var digest2 = hmac2.digest('base64');
      ```
    
    </Tab>
    <Tab title='Python'>
    
      ```py
      import base64
    
      digest1 = base64.b64encode(hmac1)
      digest2 = base64.b64encode(hmac2)
      ```
    
    </Tab>
    
    </Tabs>
    
    ### 5. Compare signatures
    
    Finally, compare the encoded digest with the value of the
    `BOX-SIGNATURE-PRIMARY` or `BOX-SIGNATURE-SECONDARY` headers.
    
    Make sure to compare the value of the `BOX-SIGNATURE-PRIMARY` header
    to the digest created with the primary key, and the value of the
    `BOX-SIGNATURE-SECONDARY` header to the digest created with the secondary key.
    
    <Tabs>
    
    <Tab title='Node'>
    
      ```js
      var signature1 = headers['BOX-SIGNATURE-SECONDARY'];
      var signature2 = headers['BOX-SIGNATURE-PRIMARY'];
    
      var primarySignatureValid = digest1 === signature1
      var secondarySignatureValid = digest2 === signature2
    
      var valid = !expired && (primarySignatureValid || secondarySignatureValid)
      ```
    
    </Tab>
    <Tab title='Python'>
    
      ```py
      signature1 = headers["BOX-SIGNATURE-SECONDARY"]
      signature2 = headers["BOX-SIGNATURE-PRIMARY"]
    
      primary_sig_valid = digest1 === signature1
      secondary_sig_valid = digest2 === signature2
    
      valid = !expired && (primary_sig_valid || secondary_sig_valid)
      ```
    
    </Tab>
    
    </Tabs>
    
    [console]: https://app.box.com/developers/console