> ## Documentation Index
> Fetch the complete documentation index at: https://developer.box.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Signature Verification

export const MultiRelatedLinks = ({sections = []}) => {
  if (!sections || sections.length === 0) {
    return null;
  }
  return <div className="space-y-8">
      {sections.map((section, index) => <RelatedLinks key={index} title={section.title} items={section.items} />)}
    </div>;
};

export const RelatedLinks = ({title, items = []}) => {
  const getBadgeClass = badge => {
    if (!badge) return "badge-default";
    const badgeType = badge.toLowerCase().replace(/\s+/g, "-");
    return `badge-${badge === "ガイド" ? "guide" : badgeType}`;
  };
  if (!items || items.length === 0) {
    return null;
  }
  return <div className="my-8">
      {}
      <h3 className="text-sm font-bold uppercase tracking-wider mb-4">{title}</h3>

      {}
      <div className="flex flex-col gap-3">
        {items.map((item, index) => <a key={index} href={item.href} className="py-2 px-3 rounded related_link hover:bg-[#f2f2f2] dark:hover:bg-[#111827] flex items-center gap-3 group no-underline hover:no-underline border-b-0">
            {}
            <span className={`px-2 py-1 rounded-full text-xs font-semibold uppercase tracking-wide flex-shrink-0 ${getBadgeClass(item.badge)}`}>
              {item.badge}
            </span>

            {}
            <span className="text-base">{item.label}</span>
          </a>)}
      </div>
    </div>;
};

export const Link = ({href, children, className, ...props}) => {
  const localizedHref = href;
  return <a href={localizedHref} className={className} {...props}>
      {children}
    </a>;
};

Webhook signatures help ensure that a webhook payload was sent by Box and was
not tampered with. Signatures greatly reduce the likelihood of
a successful man-in-the-middle or replay attacks.

When signatures are configured, Box generates a cryptographic digest of the
notification's body and attaches it to the header of the webhook payload. When
your application receives the payload, verify the signatures by calculating the
same digest and comparing it to the one received. If the digests do not match,
the payload should not be trusted.

You can achieve an extra level of protection by frequently changing the
signature keys. To enable a smooth transition between the old and new keys,
Box supports two simultaneous signature keys.

## Signature configuration

In order to attach signatures to an application's notifications, you must first
generate signature keys for the application.

To configure your application's keys follow the steps below.

1. Navigate to the application in the developer console.
2. Click on the **Webhooks** tab.
3. Click the **Manage signature keys** button.
4. Click the **Generate Key** button to configure your keys.

Once generating the primary or secondary key, copy the value. You will need
it to verify the webhook payloads. Every webhook will now include a
`BOX-SIGNATURE-PRIMARY` and `BOX-SIGNATURE-SECONDARY` header payload.

## Signature verification with SDKs

Although it is possible to verify signatures manually, methods are provided for
your convenience in the <Link href="/guides/tooling/sdks">official Box SDKs</Link>.

## Manual signature verification

The following steps describe the basics of how to verify a signature.

### Timestamp validation

Check if the timestamp in the `BOX-DELIVERY-TIMESTAMP` header of the payload is
not older than ten minutes.

<Tabs>
  <Tab title="Node">
    ```js theme={null}
    var timestamp = headers['BOX-DELIVERY-TIMESTAMP'];
    var date = Date.parse(timestamp);
    var expired = Date.now() - date > 10*60*1000;
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    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>

### Calculate HMAC signature

Calculate the HMAC of the payload using either one of the two configured
signatures for the application in the [Developer Console][console].

Ensure you append the bytes of the payload body first, and then the bytes
of the timestamp found in the `BOX-DELIVERY-TIMESTAMP` header.

<Tabs>
  <Tab title="Node">
    ```js theme={null}
    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">
    ```python theme={null}
    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>

### Base64 Conversion

Convert the HMAC to a `Base64` encoded digest.

<Tabs>
  <Tab title="Node">
    ```js theme={null}
    var digest1 = hmac1.digest('base64');
    var digest2 = hmac2.digest('base64');
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import base64

    digest1 = base64.b64encode(hmac1)
    digest2 = base64.b64encode(hmac2)
    ```
  </Tab>
</Tabs>

### Signature comparison

Compare the encoded digest with the value of the
`BOX-SIGNATURE-PRIMARY` or `BOX-SIGNATURE-SECONDARY` headers.

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.
Make sure to use a timing-safe comparison between signatures to prevent timing attacks.

<Tabs>
  <Tab title="Node">
    ```js theme={null}
    const crypto = require('crypto');

    function compareSignatures(expectedSignature, receivedSignature) {
        const expectedBuffer = Buffer.from(expectedSignature, 'base64');
        const receivedBuffer = Buffer.from(receivedSignature, 'base64');

        if (expectedBuffer.length !== receivedBuffer.length) {
            return false;
        }

        return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
    }

    const signature1 = headers['BOX-SIGNATURE-SECONDARY'];
    const signature2 = headers['BOX-SIGNATURE-PRIMARY'];

    const primarySignatureValid = compareSignatures(digest1, signature1)
    const secondarySignatureValid = compareSignatures(digest2, signature2)

    const valid = !expired && (primarySignatureValid || secondarySignatureValid)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import hmac

    def compare_signatures(expected_signature: Optional[str], received_signature: Optional[str]) -> bool:
        if not expected_signature or not received_signature:
            return False
        if len(expected_signature) != len(received_signature):
            return False
        return hmac.compare_digest(expected_signature, received_signature)

    signature1 = headers["BOX-SIGNATURE-SECONDARY"]
    signature2 = headers["BOX-SIGNATURE-PRIMARY"]

    primary_sig_valid = compare_signatures(digest1, signature1)
    secondary_sig_valid = compare_signatures(digest2, signature2)

    valid = not expired and (primary_sig_valid or secondary_sig_valid)
    ```
  </Tab>
</Tabs>

<Warning>
  HTTP header names are case insensitive. Your client should convert
  all header names to a standardized lowercase or uppercase format before trying
  to determine the value of a header.
</Warning>

## Rotate signatures

When enabled, Box sends two signatures with every webhook payload.
Your application can trust a payload as long as at least one of its signatures
is valid. When updating one signature key at a time your application will always
receive a payload with at least one valid signature.

### Rotation steps

These instructions assume that you have already created a primary and secondary
key in the [Developer Console][console] and you are ready to replace either of
them.

By following these steps you can configure your application with two new keys
without any conflicts.

1. Go to the **Webhooks** tab in the [Developer Console][console].
2. Click the **Manage signatures keys**.
3. Click the **Reset** button to change the primary key.
4. Update your application with the new primary key. Your application can still receive notifications with the old primary key, but your webhooks should be processed correctly since the secondary key is still valid.
5. Once you are confident that no webhooks with the old primary key are in-flight, you can update the secondary key using the same process.

[console]: https://app.box.com/developers/console

<RelatedLinks
  title="RELATED APIS"
  items={[
{ label: translate("Create webhook"), href: "/reference/post-webhooks", badge: "POST" }
]}
/>
