Skip to main content

Webhooks

caution

Webhooks are currently only available in certain Hosted CTFd tiers and CTFd Enterprise

Overview

Webhooks are HTTP requests sent by CTFd to endpoints you choose when certain events occur within the application. CTFd webhooks go through an initial verification over GET requests and subsequent events are sent over POST requests.

Ownership

Webhook endpoints are first validated by CTFd to prove that you have ownership over the endpoint. To validate an endpoint, CTFd will send a GET request to the URL with an additional query parameter, "token". Your endpoint must then construct a JSON response containing the token hashed with HMAC-SHA256.

For example with an endpoint of http://example.com/, CTFd will send:

GET http://example.com/?token=2b00042f6

and your application should respond with

{"response": hmac_sha256(SHARED_SECRET, "2b00042f6")}

Events

Events types are specified by the CTFd-Webhook-Event header and the request body will contain the event body. The body will differ based on the event but in general will be the schema specified for the object in the REST API documentation

Security

It's important to ensure that your endpoint verifies that data is coming from CTFd. If not, malicious users could potentially replay requests to your endpoint or trick your endpoint into doing something it isn't supposed to.

CTFd provides the CTFd-Webhook-Signature header on every webhook sent out.

For example:

Ctfd-Webhook-Signature: t=1616995588,v1=9e8cdd253434b74119ff44a119bb2d93e3b78a3a6a1a52d71e1bf70c36c55234

The header is split up into two sections t and v1. t represents the unix time/epoch time when the webhook was sent out. The v1 value represents an HMAC-SHA256 of the the t value, a period, and the request body, with the shared secret as the HMAC key.

For example if your endpoint received a body of {'data': None, 'value': None} then the signature would be made up of hmac_sha256(SHARED_SECRET, "1616995588.{'data': None, 'value': None}")

For optimal security, your endpoint should validate the signature of every request before using any of the data contained within. In addition, your endpoint can also choose to ignore requests whose signature is too old based on the timestamp.

Should an attacker attempt to manipulate the timestamp, request body, or signature, validation should fail as long as your secret hasn't been leaked.

Debugging

We provide simple applications that implement the above security logic to debug/inspect webhook events:

Questions/Feedback

Questions, feedback, and feature requests can be directed to https://ctfd.io/contact/.