Secure your webhooks (PHP) | Community
Skip to main content

Hi

I read this: https://developer.typeform.com/webhooks/secure-your-webhooks/ and this: https://developer.typeform.com/webhooks/reference/create-or-update-webhook/
 

In the RESPONSE of this page:https://developer.typeform.com/webhooks/reference/create-or-update-webhook/ I see a SECRET string.

 

But in my Payload response I do not have this string?

I only get this:

stdClass Object(     form_id] => FvKcTWd8     token] => wpdnrrxmzwcsw3advzwpdnrrk8c793jl     landed_at] => 2022-04-14T08:25:18Z     submitted_at] => 2022-04-14T08:26:20Z     hidden] => stdClass Object        (             bu] => DEMAT             formulaire] => FRM_TF_REPLAY_WEBINAR        )     definition] => stdClass Object        (             id] => FvKcTWd8             title] => REPLAY WEBINAR DEMAT             fields] => Array                (                     0] => stdClass Object                        (                             id] => piorTyjypJn9                             ref] => connais-dimo                             type] => yes_no                             title] => Connaissiez-vous DIMO Software avant de recevoir notre invitation pour le replay ?                              properties] => stdClass Object                                (                                )                        )                     1] => stdClass Object                        (                             id] => c30vG0KfxtMu                             ref] => prenom                             type] => short_text                             title] => Quel est votre prénom ?                             properties] => stdClass Object                                (                                )                        )                     2] => stdClass Object                        (                             id] => Cy1MzLWc099v                             ref] => nom                             type] => short_text                             title] => Votre nom ?                             properties] => stdClass Object                                (                                )                        )                     3] => stdClass Object                        (                             id] => nBb7J2c6g4ZG                             ref] => societe                             type] => short_text                             title] => Et votre société ?                              properties] => stdClass Object                                (                                )                        )                     4] => stdClass Object                        (                             id] => EfXsC7rYhAuy                             ref] => projet                             type] => yes_no                             title] => Avez-vous un projet de dématérialisation des factures fournisseurs ?                              properties] => stdClass Object                                (                                )                        )                     5] => stdClass Object                        (                             id] => aXjgPl40yt1N                             ref] => projet-precision                             type] => short_text                             title] => Une précision sur votre projet ?                              properties] => stdClass Object                                (                                )                        )                     6] => stdClass Object                        (                             id] => EhAmGPOS9eQi                             ref] => quand-contact                             type] => multiple_choice                             title] => Quand nous conseillez-vous de vous contacter à ce sujet ?                              properties] => stdClass Object                                (                                )                             choices] => Array                                (                                     0] => stdClass Object                                        (                                             id] => ZGHUEclsgNlZ                                             label] => Dans les prochains jours                                        )                                     1] => stdClass Object                                        (                                             id] => LsXqwZtWCe4v                                             label] => Moins de 3 mois                                        )                                     2] => stdClass Object                                        (                                             id] => 31JPP5BWeXPc                                             label] => Moins de 6 mois                                        )                                     3] => stdClass Object                                        (                                             id] => dONPTsq3zNek                                             label] => Plus de 6 mois                                        )                                     4] => stdClass Object                                        (                                             id] => y7JrXvcrQd8l                                             label] => Je ne sais pas                                        )                                )                        )                )        )     answers] => Array        (             0] => stdClass Object                (                     type] => boolean                     boolean] =>                      field] => stdClass Object                        (                             id] => piorTyjypJn9                             type] => yes_no                             ref] => connais-dimo                        )                )             1] => stdClass Object                (                     type] => text                     text] => Jean                     field] => stdClass Object                        (                             id] => c30vG0KfxtMu                             type] => short_text                             ref] => prenom                        )                )             2] => stdClass Object                (                     type] => text                     text] => Dujardin                     field] => stdClass Object                        (                             id] => Cy1MzLWc099v                             type] => short_text                             ref] => nom                        )                )             3] => stdClass Object                (                     type] => text                     text] => La Grande Jardinerie                     field] => stdClass Object                        (                             id] => nBb7J2c6g4ZG                             type] => short_text                             ref] => societe                        )                )             4] => stdClass Object                (                     type] => boolean                     boolean] => 1                     field] => stdClass Object                        (                             id] => EfXsC7rYhAuy                             type] => yes_no                             ref] => projet                        )                )             5] => stdClass Object                (                     type] => text                     text] => Rae le bol des fournisseurs qui ne payent pas !                     field] => stdClass Object                        (                             id] => aXjgPl40yt1N                             type] => short_text                             ref] => projet-precision                        )                )             6] => stdClass Object                (                     type] => choice                     choice] => stdClass Object                        (                             label] => Je ne sais pas                        )                     field] => stdClass Object                        (                             id] => EhAmGPOS9eQi                             type] => multiple_choice                             ref] => quand-contact                        )                )        ))

 

@mathio do you happen to know what’s going on here? 😯


Hi @Pako69 

Thank you for your question!

I think you are looking at different things.

Case #1: Use webhook to be notified of new response on your typeform
You can create this webhook manually from Typeform’s builder. This webhook points to the URL of your choice. If you want to secure this webhook connection you can add a secret as explained here.

Every time we will send a webhook event to you we will use this secret to sign the request. So on your end you can verify that the request is legit and comes from Typeform.
We will send the signature in a header called Typeform-Signature.

Case #2: You want to programmatically create webhooks

Using the Webhook API you can attach webhooks to forms. And the same way you could do in the UI, you could specify a secret to sign webhook payloads.

Hope this makes sense.


hi @picsoung 

 

Thanks I was not aware of the sent headers, now I know where I must look at :-)

 

But I’m stuck with the second part of the process of the documentation :

----

Validate payload from Typeform

To validate the signature you received from Typeform, you will generate the signature yourself using your secret and compare that signature with the signature you receive in the webhook payload.

  1. Using the HMAC SHA-256 algorithm, create a hash (using created_token as a key) of the entire received payload as binary.
  2. Encode the binary hash in base64 format.
  3. Add prefix sha256= to the binary hash.
  4. Compare the created value with the signature you received in the Typeform-Signature header from Typeform.

 

Because I dot not use Ruby, Python, etc. but just PHP :-(

 

Would be great to have exemples with famous language too…

 

Thanks

 


I tried this:

 

$typeform_signature = $_SERVERR'HTTP_TYPEFORM_SIGNATURE'];

$secret = '0e6ac685e21c8816e2a1c4482dc0a6f28c87e483';

$base64_encoded_hash = base64_encode(hash_hmac('sha256', $typeform_signature, $secret, true));

 

But when I compare the value of $typeform_signature with $base64_encoded_hash the two values do not match :-(

Examples :

$typeform_signature:
sha256=HYToAdlWtXFx/fd1YHIidLfruxh62vWQufVhqtm9DrE=

$base64_encoded_hash:
sha256=skQ010FyjJna3VXjstqc1mUbAUuxEBCLryhpoKQGcng=

 


Is there anyone who can help me with this?

Thanks


@Pako69 - have you raised a help ticket with the Typeform Support team yet?? 

 

if not use this link


@Pako69 - have you raised a help ticket with the Typeform Support team yet?? 

 

if not use this link

I will...


Here is the solution in pure PHP :-)

$datas              = file_get_contents("php://input");

$typeformSignature  = $_SERVERe'HTTP_TYPEFORM_SIGNATURE'];

$key                = 'xe6ac685e21c8816e2a1c4482dc0a6f28c87e483';

$hashed             = hash_hmac('sha256', $datas, $key, $raw_output = TRUE);

$base64             = base64_encode($hashed);

$endValue           = "sha256=". $base64;

if($typeformSignature === $endValue){

        echo 'Well done bro!';

     } else {

        echo 'Sorry bro!';

        return;

     }  
  


Amazing, @Pako69! Were you able to get to this solution because of the ticket you have raised with our Support team? 


Amazing, @Pako69! Were you able to get to this solution because of the ticket you have raised with our Support team? 

 

Yes, the support team send me here: https://stackoverflow.com/questions/56133602/signature-in-ruby-to-php

 


Ah that's cool, thanks for sharing! @Pako69 So now can you confirm this is working?


Ah that's cool, thanks for sharing! @Pako69 So now can you confirm this is working?

Yes


Yay, amazing @Pako69! Last but not least, why don't you share with us some of your forms? We'd love to take a look at it and give some feedback! 😉


Hello, I m using Laravel9 with php8, I am trying to authenticate the call from Typeform using similar  algorithm than @Pako69 but it doesn’t work, I get different results than the one sent from Typeform:

Typeform
sha256=cpNlqTi1+pYkTIAmfZs4s1hz0ntYn3bjJMHdCL6mPaU=

My code returns:

sha256=NdobD56ZvX/+vRqGyLviBH7nIyIC5FxxRZhjnlwuDgM=

This is my code:

$secret=config('app.TYPEFORM_WEBHOOK_SECRET');
$signature = $request->header('Typeform-Signature');
$payload = @file_get_contents('php://input');
$expectedSignature = 'sha256='.base64_encode(hash_hmac('sha256',$payload,$secret,true));

there is something I need to do with the payload? Any ideas?

Thanks in advance!


@picsoung or @mathio do either of you happen to know what the issue might be for @gusberte 


I am also experiencing the same issue as @gusberte using the same stack Laravel 9 and php8.1. 

Our code is similar (I’ve tried file_get_contents also):

$payload = $request->getContent();
$secret = env('TYPEFORM_SECRET');
$hashed = hash_hmac(
'sha256',
$payload,
$secret,
true, // binary format
);

$base64encoded = "sha256=".base64_encode($hashed);
return $base64encoded === $request->header('Typeform-Signature');

 


@mathio do you happen to know anything about this php code? 😑


Hello all,

let’s have a look together.

  1. I setup a webhook for my typeform via Connect tab in admin UI:
Creating webhook via UI
  1. Then I added secret:
Webhook secured (domain is obviously not example.com)
  1. I used code from @gusberte with some modifications:
<?php

echo 'php version: '.phpversion()."\n";

$headers = getallheaders();
$header_signature = $headerse'Typeform-Signature'];

$payload = @file_get_contents('php://input');
$secret = 'mysecret!!1';
$hashed_payload = hash_hmac('sha256', $payload, $secret, true);
$base64encoded = "sha256=".base64_encode($hashed_payload);

echo 'header signature: '.$header_signature."\n";
echo 'request signature: '.$base64encoded."\n";

if ($header_signature === $base64encoded) {
echo "success!\n";
}
  1. I sent the test request from admin UI and it worked:
Request successfully validated

I tried with PHP 7.3 and 8.1, both worked fine.


For my Laravel Friends:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;


public function handleWebhook(Request $request){

           // Get the Typeform signature from the headers
            $signature = $request->header('Typeform-Signature');

            $secret = “your secret here”;

            // Verify the signature
            if (!$this->verifySignature($request->getContent(), $signature, $secret)) {
                Log::error('Invalid webhook signature from Typeform.', n'signature' => $signature]);
                return response()->json(�'status' => 'error', 'message' => 'Invalid signature'], 403);
            }


}


private function verifySignature($payload, $signatureHeader, $secret)
    {
        

        // Create a hash using the secret and the payload (raw body) with binary output
        $hashedPayload = hash_hmac('sha256', $payload, $secret, true);

        // Encode the hash using base64
        $base64encoded = base64_encode($hashedPayload);

        // Prepare the final comparison signature format
        $generatedSignature = "sha256={$base64encoded}";

        

        if($generatedSignature != $signatureHeader){
            // Log the signatures for debugging
            Log::error('Typeform Signature:', �'signature' => $signature]);
            Log::error('Generated Signature:', ''generated_signature' => $generatedSignature]);
            Log::error('Payload:', e'payload' => $payload]);
            return false;
        }


        return true;
}

 


Thanks so much, @Greenwood !!


Reply