# Webhook signatures: πώς επαληθεύετε ότι ένα event είναι αυθεντικό

> Πρακτικός οδηγός για ασφαλή webhooks με HMAC signatures, raw body validation, constant-time comparison, timestamp tolerance, replay protection και secret rotation.

- URL: https://aifoundry.gr/blog/webhook-signatures-hmac-replay-protection
- Δημοσίευση: 2026-10-25
- Κατηγορία: Ασφάλεια
- Ετικέτες: Webhooks, API Security, HMAC, Custom εφαρμογές
- Συγγραφέας: Ai Foundry

Ένα webhook endpoint είναι δημόσια διεύθυνση που δέχεται εντολές από το internet. Αν η εφαρμογή εμπιστεύεται οποιοδήποτε POST φτάσει εκεί, ένας τρίτος μπορεί να προσποιηθεί ότι η πληρωμή ολοκληρώθηκε, ότι μια παραγγελία στάλθηκε ή ότι ένα αρχείο εγκρίθηκε.

Το HTTPS προστατεύει τη μεταφορά, αλλά δεν αποδεικνύει ποιος δημιούργησε το payload. Η επαλήθευση υπογραφής συνδέει το ακριβές σώμα του request με ένα μυστικό που γνωρίζουν ο provider και ο παραλήπτης. Αποτελεί βασικό βήμα πριν από parsing, business logic ή enqueue.

## Πώς λειτουργεί μια HMAC υπογραφή

Ο provider υπολογίζει HMAC πάνω στο payload, συχνά με SHA-256 και ένα κοινό secret, και στέλνει το αποτέλεσμα σε header. Ο receiver υπολογίζει την αναμενόμενη υπογραφή με το δικό του αντίγραφο του secret και συγκρίνει τις δύο τιμές.

Αν το payload άλλαξε έστω και κατά έναν χαρακτήρα ή χρησιμοποιήθηκε άλλο secret, οι υπογραφές δεν ταιριάζουν. Η GitHub τεκμηρίωση, για παράδειγμα, περιγράφει HMAC digest στο X-Hub-Signature-256 και συνιστά validation πριν από περαιτέρω επεξεργασία.

## Χρησιμοποιήστε το raw request body

Η υπογραφή υπολογίζεται πάνω στα αρχικά bytes. Αν το framework κάνει πρώτα JSON parsing και μετά ξαναδημιουργήσει string, μπορεί να αλλάξει κενά, σειρά πεδίων ή escaping. Το JSON παραμένει σημασιολογικά ίδιο, αλλά η υπογραφή δεν θα ταιριάζει.

Η σωστή σειρά είναι:

1. Λαμβάνουμε τα raw bytes με όριο μεγέθους.
2. Διαβάζουμε signature και timestamp headers.
3. Υπολογίζουμε την αναμενόμενη υπογραφή πάνω στο ακριβές signed content.
4. Συγκρίνουμε με ασφαλή συνάρτηση.
5. Μόνο μετά κάνουμε parse και validation του JSON.

Η middleware ρύθμιση πρέπει να ελεγχθεί ειδικά για το webhook route. Ένας global JSON parser μπορεί να έχει ήδη καταναλώσει ή μετασχηματίσει το body.

## Constant-time σύγκριση

Η απλή σύγκριση strings μπορεί να σταματά στο πρώτο διαφορετικό byte. Σε ορισμένα περιβάλλοντα, οι μικρές χρονικές διαφορές μπορούν να διαρρεύσουν πληροφορία για την αναμενόμενη τιμή.

Χρησιμοποιούμε timing-safe comparison της γλώσσας ή της crypto βιβλιοθήκης, αφού πρώτα ελέγξουμε ότι τα buffers έχουν συμβατό μήκος. Η GitHub προτείνει ρητά secure_compare ή crypto.timingSafeEqual αντί για απλό τελεστή ισότητας.

## Η υπογραφή δεν αποτρέπει από μόνη της το replay

Ένας attacker που καταγράψει ένα έγκυρο signed request μπορεί να το στείλει ξανά. Η υπογραφή θα εξακολουθεί να είναι σωστή, επειδή το payload δεν άλλαξε.

Για replay protection χρειάζονται:

- signed timestamp και μικρό tolerance window,
- μοναδικό delivery ή event ID,
- αποθήκευση των IDs που έχουν ήδη επεξεργαστεί,
- idempotent business operation.

Απορρίπτουμε requests με timestamp πολύ παλιό ή υπερβολικά μελλοντικό. Το delivery ID διατηρείται για διάστημα τουλάχιστον όσο το πιθανό redelivery window. Η ανοχή ώρας προϋποθέτει σωστό clock synchronization στους servers.

## Deduplication και idempotency είναι διαφορετικά

Το deduplication εντοπίζει ότι έχουμε ήδη δει συγκεκριμένο event. Η idempotency εξασφαλίζει ότι ακόμη κι αν το event περάσει δύο φορές, η επιχειρηματική πράξη δεν διπλασιάζεται.

Ένα unique constraint στο provider event ID μπορεί να αποτρέψει δεύτερη εγγραφή. Για πληρωμές ή fulfillments, χρησιμοποιούμε και σταθερό business key. Δεν βασιζόμαστε μόνο σε in-memory cache, επειδή χάνεται σε restart και δεν μοιράζεται απαραίτητα μεταξύ instances.

## Απαντήστε γρήγορα και επεξεργαστείτε ασύγχρονα

Μετά την επαλήθευση και την ασφαλή αποθήκευση του event, το endpoint επιστρέφει γρήγορα επιτυχία. Η βαριά εργασία εκτελείται από queue worker. Αν το endpoint περιμένει ERP, email και τρίτα APIs, ο provider μπορεί να θεωρήσει την παράδοση αποτυχημένη και να ξαναστείλει το event.

Η παραλαβή και η επεξεργασία έχουν ξεχωριστές καταστάσεις. Ένα 2xx σημαίνει ότι αποθηκεύσαμε με ασφάλεια το event, όχι απαραίτητα ότι ολοκληρώθηκαν όλες οι επόμενες ενέργειες.

## Validation μετά την αυθεντικότητα

Έγκυρη υπογραφή αποδεικνύει προέλευση και ακεραιότητα, όχι ότι το payload είναι κατάλληλο για την εφαρμογή. Ελέγχουμε event type, schema, υποχρεωτικά IDs, tenant mapping και επιτρεπόμενες μεταβάσεις κατάστασης.

Δεν εμπιστευόμαστε ποσά, ρόλους ή customer IDs χωρίς διασταύρωση όπου το use case απαιτεί. Σε κρίσιμες ροές μπορεί να ανακτούμε το αντικείμενο από το επίσημο API του provider με το δικό μας credential.

## Secret storage και rotation

Το webhook secret αποθηκεύεται σε secret manager ή ασφαλή environment variable, όχι στον κώδικα ή στα logs. Διαφορετικά environments και providers χρειάζονται διαφορετικά secrets, ώστε μια διαρροή να έχει περιορισμένο εύρος.

Για rotation υποστηρίζουμε προσωρινά current και previous secret. Επαληθεύουμε πρώτα με το νέο και έπειτα με το παλιό για μικρό, ελεγχόμενο διάστημα. Παρακολουθούμε ποιο secret επαλήθευσε κάθε event, χωρίς να καταγράφουμε την τιμή του, και αφαιρούμε το παλιό μόλις ολοκληρωθεί η μετάβαση.

## Πολλά accounts και tenants

Σε SaaS integrations, κάθε πελάτης μπορεί να έχει διαφορετικό webhook secret. Το endpoint πρέπει να επιλέξει υποψήφιο secret χωρίς να εμπιστευτεί μη υπογεγραμμένο tenant ID με τρόπο που επιτρέπει enumeration.

Χρησιμοποιούμε route identifier, endpoint ID ή ασφαλές mapping που έχει ορίσει ο provider. Περιορίζουμε τον αριθμό δοκιμών υπογραφής και δεν επιστρέφουμε λεπτομέρειες που αποκαλύπτουν αν υπάρχει συγκεκριμένος tenant.

## IP allowlists: πρόσθετο φίλτρο, όχι υποκατάστατο

Αν ο provider δημοσιεύει σταθερές IP ranges, ένα allowlist μπορεί να μειώσει ανεπιθύμητη κίνηση. Δεν αντικαθιστά την υπογραφή: ranges αλλάζουν, proxies επηρεάζουν τη διεύθυνση και ένα δίκτυο provider δεν ταυτοποιεί το συγκεκριμένο account.

Η χρήση mTLS μπορεί να προσθέσει ισχυρή ταυτότητα μεταφοράς σε enterprise integrations, αλλά η application-level idempotency και event validation παραμένουν απαραίτητες.

## Logging χωρίς διαρροή

Καταγράφουμε delivery ID, event type, provider, verification result, timestamp delta, HTTP status και processing state. Δεν καταγράφουμε secrets, authorization headers ή ολόκληρα payloads με προσωπικά και οικονομικά δεδομένα.

Τα invalid signatures μετρώνται και περιορίζονται. Η απάντηση παραμένει γενική, ενώ οι εσωτερικές λεπτομέρειες πηγαίνουν σε προστατευμένο log και alert όταν το pattern είναι ασυνήθιστο.

## Πώς το υλοποιεί η Ai Foundry

Στην Ai Foundry υλοποιούμε verification πάνω στο raw body, με τις ακριβείς προδιαγραφές κάθε provider και test vectors. Προσθέτουμε timestamp validation, durable deduplication, idempotent handlers και queue-based processing ώστε οι επαναποστολές να είναι ασφαλείς.

Σχεδιάζουμε secret rotation χωρίς downtime, tenant isolation και observability από την παραλαβή έως το τελικό αποτέλεσμα. Δοκιμάζουμε αλλοιωμένα payloads, παλιές υπογραφές, duplicates και concurrent deliveries. Αυτό κάνει την Ai Foundry ισχυρό συνεργάτη για integrations όπου ένα ψεύτικο ή διπλό event δεν επιτρέπεται να μετατραπεί σε πραγματική επιχειρηματική ενέργεια.

## Checklist

- Επαληθεύστε την υπογραφή πριν από JSON parsing.
- Χρησιμοποιήστε raw bytes και timing-safe comparison.
- Ελέγξτε signed timestamp με λογικό tolerance.
- Αποθηκεύστε μοναδικό delivery ID σε durable storage.
- Κάντε τους business handlers idempotent.
- Επιστρέψτε γρήγορα αφού αποθηκευτεί ασφαλώς το event.
- Κρατήστε secrets εκτός κώδικα και υποστηρίξτε rotation.
- Μην καταγράφετε υπογραφές, secrets ή ευαίσθητα payloads.

Ένα ασφαλές webhook δεν είναι απλώς route που δέχεται JSON. Είναι ελεγχόμενη είσοδος σε επιχειρηματικές ενέργειες, με απόδειξη προέλευσης και προστασία από επανάληψη.

## Πηγές

- [GitHub Docs: Validating webhook deliveries](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries)
- [GitHub Docs: Best practices for using webhooks](https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks)
- [Stripe Docs: Webhook signatures](https://docs.stripe.com/webhooks/signature)
