Webhook new authentication

bbl
Member

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
11 Replies 11
lscanlan
HubSpot Alumni

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

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

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

bbl
Member

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

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

lscanlan
HubSpot Alumni

@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

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

Hello,

 

I have tried your solution. 

I have again two sha256 different..

 

I use Symfony.

 

I have no idea..

 

Bastien.

0 Upvotes
MichaelJC91
Member

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
viath
Participant

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
Mc8
Member

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;
    }