I'm trying to set up an automatic deployment when a pull request is merged into the main branch using Bitbucket webhooks.
However, I'm facing an issue with validating the webhook signature using the secret key.
What I Have
My secret key: q3bn5JCdR1yqK46V. (Already changed before posting this)
The webhook triggers correctly and sends the following headers:
X-Hub-Signature: sha256=51ff4d1fe17b05bc6c649abf4bb09e0f3d8d90d45faa43abcf66822cb658ae57
X-Hub-Signature-256: sha256=51ff4d1fe17b05bc6c649abf4bb09e0f3d8d90d45faa43abcf66822cb658ae57
What I'm Doing
I assumed the X-Hub-Signature-256 is the HMAC-SHA256 hash of the payload using my secret key, so I'm trying to generate the same hash with:
$hash = 'sha256=' . hash_hmac('sha256', $payload, $secret);
However, the hash I generate does not match the one provided in the headers.
How can I validate the Bitbucket webhook secret key using the content sent in the request?
I'm trying to set up an automatic deployment when a pull request is merged into the main branch using Bitbucket webhooks.
However, I'm facing an issue with validating the webhook signature using the secret key.
What I Have
My secret key: q3bn5JCdR1yqK46V. (Already changed before posting this)
The webhook triggers correctly and sends the following headers:
X-Hub-Signature: sha256=51ff4d1fe17b05bc6c649abf4bb09e0f3d8d90d45faa43abcf66822cb658ae57
X-Hub-Signature-256: sha256=51ff4d1fe17b05bc6c649abf4bb09e0f3d8d90d45faa43abcf66822cb658ae57
What I'm Doing
I assumed the X-Hub-Signature-256 is the HMAC-SHA256 hash of the payload using my secret key, so I'm trying to generate the same hash with:
$hash = 'sha256=' . hash_hmac('sha256', $payload, $secret);
However, the hash I generate does not match the one provided in the headers.
How can I validate the Bitbucket webhook secret key using the content sent in the request?
Share Improve this question edited Feb 7 at 15:45 hakre 198k55 gold badges446 silver badges854 bronze badges Recognized by PHP Collective asked Feb 7 at 15:27 Gustavo SoaresGustavo Soares 355 bronze badges2 Answers
Reset to default 3This is what I am using for Github actions.
Note, to compare the hashes via hash_equals function!
Also note, the header keys are underscored in the $_SERVER globals array. The dashes are transformed by PHP.
In addition to the elements listed below, PHP will create additional elements with values from request headers. These entries will be named HTTP_ followed by the header name, capitalized and with underscores instead of hyphens. For example, the Accept-Language header would be available as $_SERVER['HTTP_ACCEPT_LANGUAGE'].
$body = file_get_contents(filename: 'php://input');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
$expected = 'sha256=' . hash_hmac(algo: 'sha256', data: $body, key: $secret);
if (hash_equals($signature, $expected)) {
return true;
}
This is an addition to Markus Zellers' answer, as it is relevant for Atlassian Bitbucket Webhooks and may be relevant for the individual use-case.
Marcus shows how to use hash_hmac() for the known X-Hub-Signature header value for the case of one signature only:
enum BitbucketSignature
{
case sha256;
}
This is fine right now, but the algorithm may change. (ref)
enum BitbucketSignature
{
case sha256;
case sha384;
case sha512;
}
PHP would handle it regardlessly, if supported via hash_hmac_algos():
in_array($algo?->name, hash_hmac_algos())
Originally, the enum was only descriptive; it would also allow putting the moving parts into it for convenience:
enum BitbucketSignature
{
case sha256;
case sha384;
case sha512;
public static function authContents(...) : ?string{...}
}
...
$contents = BitbucketSignature::authContents(
signature: @$_SERVER['HTTP_X_HUB_SIGNATURE'],
payload: file_get_contents(filename: 'php://input'),
secret: $secret,
);
The $contents is authenticated, null
otherwise.
Try: https://phpstan.org/r/5e1f4c87-78e5-42aa-9976-a650e3663fca
Demo: https://3v4l.org/kbSg5
Options:
Given
null
, respond with HTTP 2xx quickly to confirm the delivery (not the authenticity).Given authentic, you may want to check if the content matches your expectation of the payloads Content-Type.