APIs & Integrations

bbl
Member

Webhook new authentication

Hello,

 

I have a problem with the new authentication method of a webhook.

 

I have implemented the new authentication in PHP.

I made :

$method = $request->getMethod();
$uri = $request->getUri();
$body = json_encode($request->request->all());
$sha256Key = hash('sha256',$apiKey.$method.$uri.$body);

The sha256 in header is not equal to the sha256 calculate.

I don't understand the difference.

 

Can you help me ?

0 Upvotes
13 Replies 13
gonzblanco
Participant

Webhook new authentication

I notice something very weird with all of this. If the request body is all set in UTF-8 characters, the signature validation with these form:

 

client_secret + http_method + http_uri + request_body

 

Is going to be ok. But if in the request body are present Latin characters like "ñ" o wherever words with accent marks, the signature validation hash in all my tests, is incorrect. Is somebody aware of this same behavior or some HubSpot Support friend that can enlighten me about this?

0 Upvotes
gonzblanco
Participant

Webhook new authentication

I have the same issue with python. I already the app installed in my client, my webhook set with the app id, but the hashes are not the same. This is my code:

 

 

import hashlib

client_secret = 'YYYY-YYY...'
http_method = 'POST'
http_uri = 'https://webhook.site/b2ca9e...'
request_body = '{"vid":355246551,...'

source_string = client_secret + http_method + http_uri + request_body
print('source_string: {}'.format(source_string))

hash_result = hashlib.sha256(source_string.encode('utf-8')).hexdigest()
print('hash_result: {}'.format(hash_result))

 

0 Upvotes
Mc8
Member

Webhook new authentication

For me the issue was the request uri was being seen as the http internal request from nginx rather than https. In my case a simple string replace resolved the issue.

Here is some Symfony code. It checks both possible versions of the signature.

    /**
     * @param Request $request
     * @return bool
     */
    public function validateHubspotRequest(Request $request)
    {
        $hubspotRequestSignature = $request->headers->get('x-hubspot-signature');
        $digest = hash('sha256', $_ENV['HUBSPOT_APP_CLIENT_SECRET'] . $request->getContent());
        $digest2 = hash('sha256', $_ENV['HUBSPOT_APP_CLIENT_SECRET'] . $request->getMethod() . str_replace("http", "https", $request->getUri()) . $request->getContent());
        if($hubspotRequestSignature == $digest || $hubspotRequestSignature == $digest2) {
            return true;
        }
        return false;
    }

 

lscanlan
HubSpot Alumni
HubSpot Alumni

Webhook new authentication

Hi @bbl,

 

To be honest I'm not extremely familiar with PHP, but I should be able to follow along. Are you following the instructions documented here: https://knowledge.hubspot.com/articles/kcs_article/workflows/how-do-i-use-webhooks-with-hubspot-work... ? I just want to make sure you're concatenating the right values. Are you also adding the app secret in your concatenation? It looks like you've got the method, URI, and request body, but I'm not sure I'm seeing an app secret being concatenated as well, which could account for the discrepancy.

 

Let me know if you have questions about that.

 

 - Leland

Leland Scanlan

HubSpot Developer Support
0 Upvotes
bbl
Member

Webhook new authentication

Hello,

 

Thank you for your response.

I use this authentication https://knowledge.hubspot.com/articles/kcs_article/workflows/how-do-i-use-webhooks-with-hubspot-work...

My app secret is $apiKey, it's my client_secret of my application.

$method = 'POST'.

$uri = 'http://url/attr/attr2'

$body = Hubspot data.

 

But the sha256 is different between me and Hubspot. I take the sha256 in  X-HubSpot-Signature head.

 

Bastien.

0 Upvotes
balabanov
Contributor

Webhook new authentication

Bastien, hey

 

Maybe this snippet will be helpful to you, it does match the HW webhooks header

<?php

$raw_json = file_get_contents('php://input');
$user = json_decode($raw_json, true);

$secret = "6201xxxx-xxxxx-xxxx-xxxx-xxxx6b766138";
$data = $secret . $_SERVER['REQUEST_METHOD'] . "https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . $raw_json;
$check = hash('sha256', $data);

foreach (getallheaders() as $name => $value) {
	if ($name == "X-Hubspot-Signature")
	{
		if ($value != $check)
		{
			die();
		}
	}
}

Maybe you're mixing up the AppID and the App Client Secret? HS docs aren't super-clear with the whole webhook signing thing, unfortunately

viath
Participant

Webhook new authentication

The parameters to hash depend on what version of webhooks you're receiving.  Adding the HTTP Method and URI are listed in v2 (https://legacydocs.hubspot.com/docs/faq/v2-request-validation) but not in v1 (https://legacydocs.hubspot.com/docs/faq/v1-request-validation) which is listed as just $secret.$raw_json (no URI parameters at all).

 

@balabanov, your looping of getallheaders()  to until you find 'X-Hubspot-Signature' would let a fake request without any 'X-Hubspot-Signature' header passed at all get sucessfully through your hash check.  Optionally in PHP, this value can be referenced as $_SERVER['HTTP_X_HUBSPOT_SIGNATURE'].  You can see what version of webhooks you're receiving by checking the value of $_SERVER['HTTP_X_HUBSPOT_SIGNATURE_VERSION'] ('v1' is what I receive)

0 Upvotes
bbl
Member

Webhook new authentication

Hello,

 

Thank you for your help.

 

I have tried your code. I don't find the same sha256.

 

However, I use the app client secret.

 

$secret = "1d24xxxx-xxxx-xxxx-xxxx-xxxxxxxd5fbb";
$method = $request->getMethod();
$uri = $request->getUri();
$body = $request->request->all();
$body = json_encode($body,true);
$data = $secret . $method . $uri . $body;
$xSignatureHubspot = $request->headers->get('X-Hubspot-Signature');
if($xSignatureHubspot == $data){
    dump('isOK');
}
exit;

 I don't understand this difference.

 

You think that can come from JSON encoding?

 

Yes, Hubspot does not present a concrete example.

 

Bastien.

 

 

0 Upvotes
balabanov
Contributor

Webhook new authentication

Assuming this is Laravel, can you try replacing the $request->request->all(); with the $request->getContent();

lscanlan
HubSpot Alumni
HubSpot Alumni

Webhook new authentication

@bbl: I just want to confirm one other thing here. You mentioned that you're using the authentication method documented here: https://knowledge.hubspot.com/articles/kcs_article/workflows/how-do-i-use-webhooks-with-hubspot-work.... You are in fact trying to authenticate the request signature from a workflow webhook, correct?

 

Because if you're actually trying to authenticate webhooks sent through our Webhooks API, you'll need to use a different valiation method, which is documented in our Webhooks API documentation here: https://developers.hubspot.com/docs/methods/webhooks/webhooks-overview#security. In that case you should be generating an SHA256 hash from a concatenation of your app secret + the request body.

 

I think you're authenticating request signatures for webhooks sent from workflows, in which case you are concatenating the correct values. But I just wanted to make sure, because if you're not, that would explain the discrepancy here.

 

Also, thank you @balabanov for helping out here. I will also pass along the feedback that we should have better examples of this.

Leland Scanlan

HubSpot Developer Support
bbl
Member

Webhook new authentication

Hello @lscanlan,

 

Yes, I am trying to authenticate the request signature from a workflow webhook.

 

Hello @balabanov,

 

Okay, I try your solution.

 

Thank you.

0 Upvotes
bbl
Member

Webhook new authentication

Hello,

 

I have tried your solution. 

I have again two sha256 different..

 

I use Symfony.

 

I have no idea..

 

Bastien.

0 Upvotes
MichaelJC91
Member

Webhook new authentication

I'm having the same issue in Node JS and it's making me go insane. I for the life of me can't figure out why the hashs are different.

0 Upvotes