Skip to main content
Answered

Secure your webhooks (PHP)


Forum|alt.badge.img+1
  • Sharing wisdom
  • 10 replies

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                        )                )        ))

 

Best answer by Pako69

Here is the solution in pure PHP :-)

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

$typeformSignature  = $_SERVER['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;

     }  
  

View original

20 replies

Liz
Community Team
Forum|alt.badge.img+5
  • Tech Community Advocate
  • 14521 replies
  • April 14, 2022

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


picsoung
Typeform
Forum|alt.badge.img+5
  • Developer Advocate @ Typeform
  • 384 replies
  • April 14, 2022

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.


Forum|alt.badge.img+1
  • Author
  • Sharing wisdom
  • 10 replies
  • April 15, 2022

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

 


Forum|alt.badge.img+1
  • Author
  • Sharing wisdom
  • 10 replies
  • April 15, 2022

I tried this:

 

$typeform_signature = $_SERVER['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=

 


Forum|alt.badge.img+1
  • Author
  • Sharing wisdom
  • 10 replies
  • April 19, 2022

Is there anyone who can help me with this?

Thanks


john.desborough
Forum|alt.badge.img+6
  • Certified Partner & Champion
  • 5182 replies
  • April 19, 2022

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

 

if not use this link


Forum|alt.badge.img+1
  • Author
  • Sharing wisdom
  • 10 replies
  • April 19, 2022
john.desborough wrote:

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

 

if not use this link

I will...


Forum|alt.badge.img+1
  • Author
  • Sharing wisdom
  • 10 replies
  • Answer
  • April 20, 2022

Here is the solution in pure PHP :-)

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

$typeformSignature  = $_SERVER['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;

     }  
  


Gabi Amaral
Ex–Typefomer
Forum|alt.badge.img+5
  • Ex–Typefomer
  • 1779 replies
  • April 20, 2022

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


Forum|alt.badge.img+1
  • Author
  • Sharing wisdom
  • 10 replies
  • April 21, 2022
Gabi Amaral wrote:

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

 


Gabi Amaral
Ex–Typefomer
Forum|alt.badge.img+5
  • Ex–Typefomer
  • 1779 replies
  • April 22, 2022

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


Forum|alt.badge.img+1
  • Author
  • Sharing wisdom
  • 10 replies
  • April 22, 2022
Gabi Amaral wrote:

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

Yes


Gabi Amaral
Ex–Typefomer
Forum|alt.badge.img+5
  • Ex–Typefomer
  • 1779 replies
  • April 25, 2022

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! 😉


  • Navigating the Land
  • 1 reply
  • August 30, 2022

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!


Liz
Community Team
Forum|alt.badge.img+5
  • Tech Community Advocate
  • 14521 replies
  • August 31, 2022

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


  • Navigating the Land
  • 1 reply
  • October 5, 2022

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');

 


Liz
Community Team
Forum|alt.badge.img+5
  • Tech Community Advocate
  • 14521 replies
  • October 5, 2022

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


mathio-tf
Typeform
Forum|alt.badge.img+5
  • Typeform
  • 888 replies
  • October 6, 2022

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 = $headers['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.


  • Navigating the Land
  • 1 reply
  • October 22, 2024

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.', ['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:', ['payload' => $payload]);
            return false;
        }


        return true;
}

 


Liz
Community Team
Forum|alt.badge.img+5
  • Tech Community Advocate
  • 14521 replies
  • October 25, 2024

Thanks so much, @Greenwood !!


Reply