Skip to content

Secure web hooks

Geoff Baskwill edited this page Apr 3, 2019 · 1 revision

Deep Security Smart Check gives you an easy way to ensure that the events that your web server is receiving are coming from Deep Security Smart Check and have not been modified in transit.

When Deep Security Smart Check calls back to your server, it will compute a signature (HMAC-SHA-256) for the event using the secret as the key and put the signature value in the X-Scan-Event-Signature HTTP header.

The request that your server will get will look like this:

POST https://server.example.com/ HTTP/1.1
X-Scan-Event-Signature: {signature}
Content-Type: application/json

{
    "event": "scan-completed",
    "source": "/api/webhooks/7a2f1d8c-7780-41d2-821b-7230005d4be8",
    "timestamp": "2018-05-01T00:00:00Z",
    "scan": {
        ...
    }
}

Create the web hook

When you create the web hook, set the secret attribute to a value that only Deep Security Smart Check and your server will know. In this example, we're using correct horse battery staple as our shared secret, but you should use a long string of random characters.

POST https://dssc.example.com/api/webhooks HTTP/1.1
Authorization: Bearer {token}
Content-Type: application/json

{
  "name": "secure webhook",
  "hookUrl": "https://server.example.com/",
  "active": true,
  "events": [ "*" ],
  "secret": "correct horse battery staple",
}

Calculate and check the signature

To check the signature, compute the HMAC-SHA-256 signature of the payload using the secret as the key and compare it with the value in the X-Scan-Event-Signature HTTP header. In this example, the secret is stored in the HMAC_SECRET environment variable.

export HMAC_SECRET="correct horse battery staple"
# Get the secret from the environment -- don't store it in code!
secret = os.environ.get('HMAC_SECRET')

# Get the size of the payload
content_length = int(self.headers.get('Content-Length', 0))
if content_length == 0 or content_length > MAX_PAYLOAD_SIZE:
    raise InvalidPayloadSizeException()

# Read the full event payload from the request
payload = self.rfile.read(content_length)

# Calculate the HMAC-SHA-256 signature of the payload using the secret as the HMAC key
actual = hmac.new(bytes(secret, 'utf-8'),
            msg=bytes(payload),
            digestmod=hashlib.sha256).hexdigest()

# Get the expected HMAC-SHA-256 signature value from the request header
expected = self.headers.get('X-Scan-Event-Signature', '')

# Use compare_digest to reduce vulnerability to timing attacks
if not hmac.compare_digest(actual, expected):
    # the event has been tampered with or is not from the right sender

(full working example)

Send a test event

You can use the web hook ping API endpoint to send a test event to your web hook:

POST https://dssc.example.com/api/webhooks/7a2f1d8c-7780-41d2-821b-7230005d4be8/ping HTTP/1.1
Authorization: Bearer {token}

Deep Security Smart Check will send a sample event that looks similar to this:

POST https://server.example.com/ HTTP/1.1
X-Scan-Event-Signature: {signature}
Content-Type: application/json

{
    "event": "ping",
    "source": "/api/webhooks/7a2f1d8c-7780-41d2-821b-7230005d4be8",
    "timestamp": "2018-05-01T00:00:00Z",
}

and you can confirm that your validation code works as expected.

You can’t perform that action at this time.