◇ OpenCyber Academy
Technologist · Chapter 14 · English

Chapter 14 — Cryptography and trust services: methods I

Open, free learning for anyone who wants to protect people and technology. No account is required to read this text or its worked answers.

Chapter 14 — Cryptography and trust services: methods I

Lesson map

Lesson 14 published. Map, example, EX040.

flowchart TD
  N0["Learning outcomes"]
  N1["14.1 Signed webhooks"]
  N2["Lab — Webhook verification"]
  N3["14.2 Reading the webhook run"]
  N4["14.3 Combining checks"]
  N5["Synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5
  N5 --> EX["Worked example"]
  EX --> Q["Exercise EX040"]

Logical drawing

flowchart LR
  N0["Learning outcomes"]
  N1["14.1 Signed webhooks"]
  N2["Lab — Webhook verification"]
  N3["14.2 Reading the webhook run"]
  N4["14.3 Combining checks"]
  N5["Synthesis"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX040"]
[Learning outcomes]
    |
    v
[14.1 Signed webhooks]
    |
    v
[Lab — Webhook verification]
    |
    v
[14.2 Reading the webhook run]
    |
    v
[14.3 Combining checks]
    |
    v
[Synthesis]
    |
    v
[Worked example] --> [Exercise EX040]

Learning outcomes

You will (1) verify signed webhooks with HMAC; (2) limit replay with timestamps; (3) combine checks correctly in code.

14.1 Signed webhooks

The payment provider notifies the council when a permit is paid by sending an HTTP request containing a JSON body, a timestamp header and a signature header. The signature is an HMAC-SHA256, as in RFC 2104, computed over the timestamp and body with a secret shared between the provider and the council. The council recomputes the HMAC and compares. Rosa learns that the comparison must use a constant-time function, hmac.compare_digest in Python, so attackers cannot learn the correct signature byte by byte from timing.

Lab — Webhook verification

"""Verify a signed webhook with HMAC-SHA256 plus a 300-second timestamp window (synthetic payment notice)."""
import hmac, hashlib
SECRET = b"demo-webhook-secret-not-for-production"
WINDOW = 300

def sign(ts, body): return hmac.new(SECRET, f"{ts}.".encode() + body, hashlib.sha256).hexdigest()

def verify(ts, body, sig, now):
    return abs(now - ts) <= WINDOW and hmac.compare_digest(sig, sign(ts, body))

def main():
    body, ts = b'{"permit":"PK-2291","paid":45.00}', 1767225600
    sig = sign(ts, body)
    print("fresh, intact:", verify(ts, body, sig, now=ts + 20))
    print("amount edited:", verify(ts, body.replace(b"45.00", b"4.50"), sig, now=ts + 20))
    print("replayed after 1 hour:", verify(ts, body, sig, now=ts + 3600))

if __name__ == "__main__":
    main()

Run with python3 lab_webhook.py. Verified output:

fresh, intact: True
amount edited: False
replayed after 1 hour: False

14.2 Reading the webhook run

A fresh, intact notification verifies as True. When the paid amount is edited from 45.00 to 4.50, verification fails because the HMAC no longer matches. When the same valid notification is replayed an hour later, verification fails because the timestamp falls outside the 300-second window. Rosa notes that the window limits replay to five minutes but does not prevent it entirely; the council also records processed notification identifiers and rejects duplicates. The demonstration secret is fixed only for reproducible output; the real secret lives in the secret store.

14.3 Combining checks

Rosa writes the verification order: read the raw body exactly as received, before any JSON parsing that might change spacing; check the timestamp window; recompute and compare the HMAC; check the notification identifier has not been processed; and only then act on the content. She also logs failures with reasons for operations, but returns a generic error to the caller. Owen reviews the code and asks her to add tests for each failure path, mirroring the lab cases. Each test sends a crafted request and asserts both the response code and the log line, so silent acceptance cannot slip through.

Worked example

Rosa verifies webhooks with HMAC and a 300-second window, adds duplicate-identifier checks, and orders the checks so raw bytes are verified before parsing.

Synthetic case. The secret and notification are invented; RFC 2104 is real.

Errors and limits

Verifying after JSON parsing can fail on harmless formatting changes. A timestamp window alone does not stop all replays.

Exercises

Synthesis

Webhook security combines HMAC over raw bytes, constant-time comparison, a timestamp window and duplicate detection.

Continue studying — chapter 14 of 30

← Chapter 13 (draft) · Course chapter index · Chapter 15 (draft) →