Sample code for 30+ languages & platforms
AutoIt Requires Chilkat v11.5.0+

Box: Verify a Captured V2 Webhook Signature

See more Box Examples

Verify raw payload bytes and captured headers using the configured primary and secondary signing keys. The example rejects stale/future timestamps and compares randomized HMACs of the signatures to avoid exposing a signature-prefix timing comparison. It does not host an HTTP listener. The receiver must persist/deduplicate the verified event and acknowledge promptly.

See the Box API guide for setup and the complete workflow.

Chilkat AutoIt Downloads

AutoIt
Local $bSuccess = False

;  Requires Chilkat 11.5.0 or later and a previously unlocked Chilkat bundle.
;  See Global Unlock Sample.
;  Guide: Box API Guide.

$bSuccess = False

;  Save the exact HTTP body bytes to this file, without JSON reserialization.
$oRawBody = ObjCreate("Chilkat.BinData")
$bSuccess = $oRawBody.LoadFile("qa_data/box_webhook_body.bin")
If ($bSuccess <> True) Then
    ConsoleWrite($oRawBody.LastErrorText & @CRLF)
    Exit
EndIf

;  Read these fields case-insensitively from the incoming request headers.
;  For this offline example, enter the captured header values.
Local $sTimestamp = "BOX_DELIVERY_TIMESTAMP"
Local $sPrimarySignature = "BOX_SIGNATURE_PRIMARY"
Local $secondarySignature = "BOX_SIGNATURE_SECONDARY"
Local $signatureVersion = "1"
Local $signatureAlgorithm = "HmacSHA256"
;  Retrieve configured keys from protected application configuration.
Local $sPrimaryKey = "BOX_PRIMARY_SIGNATURE_KEY"
Local $secondaryKey = "BOX_SECONDARY_SIGNATURE_KEY"
If (($signatureVersion <> "1") Or ($signatureAlgorithm <> "HmacSHA256")) Then
    ConsoleWrite("Unsupported signature version or algorithm." & @CRLF)
    Exit
EndIf

$oDelivered = ObjCreate("Chilkat.CkDateTime")
$bSuccess = $oDelivered.SetFromTimestamp($sTimestamp)
If ($bSuccess <> True) Then
    ConsoleWrite($oDelivered.LastErrorText & @CRLF)
    Exit
EndIf

$oNow = ObjCreate("Chilkat.CkDateTime")
$bSuccess = $oNow.SetFromCurrentSystemTime()
If ($bSuccess <> True) Then
    ConsoleWrite($oNow.LastErrorText & @CRLF)
    Exit
EndIf

Local $iAge = Int($oNow.GetAsUnixTime(False)) - Int($oDelivered.GetAsUnixTime(False))
;  Box recommends rejecting deliveries older than ten minutes.
;  Allow at most 60 seconds of future clock skew; keep the server clock synchronized.
If (($iAge > 600) Or ($iAge < -60)) Then
    ConsoleWrite("Delivery timestamp is outside the accepted window." & @CRLF)
    Exit
EndIf

$oSignedBytes = ObjCreate("Chilkat.BinData")
$bSuccess = $oSignedBytes.AppendBd($oRawBody)
If ($bSuccess <> True) Then
    ConsoleWrite($oSignedBytes.LastErrorText & @CRLF)
    Exit
EndIf

$bSuccess = $oSignedBytes.AppendString($sTimestamp,"utf-8")
If ($bSuccess <> True) Then
    ConsoleWrite($oSignedBytes.LastErrorText & @CRLF)
    Exit
EndIf

$oMac = ObjCreate("Chilkat.Crypt2")
$oMac.MacAlgorithm = "hmac"
$oMac.HashAlgorithm = "sha256"
$oMac.EncodingMode = "base64"
$bSuccess = $oMac.SetMacKeyString($sPrimaryKey)
If ($bSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

Local $sExpectedPrimary = $oMac.MacBdENC($oSignedBytes)
If ($oMac.LastMethodSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

$bSuccess = $oMac.SetMacKeyString($secondaryKey)
If ($bSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

Local $sExpectedSecondary = $oMac.MacBdENC($oSignedBytes)
If ($oMac.LastMethodSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

;  Blind both sides with a fresh secret random key before equality comparison.
;  Never compare the original HMAC strings using a prefix-short-circuit comparison.
;  A host-language receiver can instead use its standard constant-time byte comparison.
$oRandom = ObjCreate("Chilkat.Prng")
Local $sComparisonKey = $oRandom.GenRandom(32,"base64")
If ($oRandom.LastMethodSuccess <> True) Then
    ConsoleWrite($oRandom.LastErrorText & @CRLF)
    Exit
EndIf

$bSuccess = $oMac.SetMacKeyEncoded($sComparisonKey,"base64")
If ($bSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

Local $sPExpected = $oMac.MacStringENC($sExpectedPrimary)
If ($oMac.LastMethodSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

Local $sPReceived = $oMac.MacStringENC($sPrimarySignature)
If ($oMac.LastMethodSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

Local $sExpected = $oMac.MacStringENC($sExpectedSecondary)
If ($oMac.LastMethodSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

Local $sReceived = $oMac.MacStringENC($secondarySignature)
If ($oMac.LastMethodSuccess <> True) Then
    ConsoleWrite($oMac.LastErrorText & @CRLF)
    Exit
EndIf

Local $bValidPrimary = ($sPrimaryKey <> "") And ($sPrimarySignature <> "") And ($sPExpected = $sPReceived)
Local $bValidSecondary = ($secondaryKey <> "") And ($secondarySignature <> "") And ($sExpected = $sReceived)
If (($bValidPrimary = False) And ($bValidSecondary = False)) Then
    ConsoleWrite("Signature verification failed." & @CRLF)
    Exit
EndIf

;  Parse JSON only after signature verification.
$oWebhookEvent = ObjCreate("Chilkat.JsonObject")
$bSuccess = $oWebhookEvent.Load($oRawBody.GetString("utf-8"))
If ($bSuccess <> True) Then
    ConsoleWrite($oWebhookEvent.LastErrorText & @CRLF)
    Exit
EndIf

ConsoleWrite("Verified event ID: " & $oWebhookEvent.StringOf("id") & @CRLF)
ConsoleWrite("Trigger: " & $oWebhookEvent.StringOf("trigger") & @CRLF)
;  Deduplicate using event.id, not BOX-DELIVERY-ID, which changes on retries.
;  Verify the target belongs to your configured webhook before dispatching work.