Prompt and setting
The provider signs a timestamp and raw body. Requests may be delayed, duplicated, malformed, or altered by JSON parsing. The implementation must reject tampering and replay without leaking comparison timing.
What the interviewer tests
- Preserving exact bytes and parsing a structured signature header safely.
- Computing HMAC with the specified algorithm and comparing in constant time.
- Enforcing timestamp tolerance and clear failure behavior.
Clarifying questions before answering
- What exact signed string, digest algorithm, encoding, and header format does the provider specify?
- Is the body available as raw bytes before JSON parsing?
- What clock-skew tolerance and replay identifier are required?
- Should malformed input return a generic rejection without revealing which check failed?
30-second answer framework
I would read the raw bytes, parse the timestamp and signature without trusting their types, reject timestamps outside the allowed window, and compute HMAC over the provider-defined canonical string. I would compare decoded MAC bytes with a constant-time primitive, then deduplicate the event ID before enqueueing. Errors return one generic rejection while internal metrics classify the failure.
Step-by-step deep dive
1. Keep the raw body
Do not stringify parsed JSON for verification. Whitespace, key order, and Unicode escaping can change bytes. Read the body once, enforce a size limit, and pass the same bytes to verification and later parsing.
2. Parse and validate the header
Split the header into timestamp and versioned signatures, reject duplicates or invalid encodings, and bound timestamp length. Convert the timestamp to an integer and reject values outside the configured skew window before expensive work.
3. Compute and compare
Build the exact signed message, calculate HMAC with the configured digest, decode the supplied signature, and compare equal-length byte arrays with a constant-time function. Never compare hexadecimal strings with ordinary equality when an attacker can measure responses.
4. Prevent replay
After signature verification, require a stable event ID and atomically record it with a retention period at least as long as the accepted timestamp window. A duplicate valid request can return success without repeating the business effect.
5. Test boundaries
Test altered bytes, wrong secret, wrong algorithm, malformed header, old and future timestamps, duplicate signatures, empty bodies, oversized bodies, and repeated event IDs. Keep logs free of secrets and complete payloads.
High-quality sample answer
“I would preserve the raw body, parse and bound the timestamp, reject outside the replay window, and build the provider’s exact signed string. I would compute HMAC with the required digest, decode the signature, and use a constant-time byte comparison. After verification, I would atomically deduplicate the event ID before enqueueing. Malformed input gets one generic rejection; internal counters distinguish parsing, time, MAC, and replay failures.”
Common mistakes
- Verify parsed JSON → serialization can change signed bytes → verify the raw body.
- Use ordinary string equality → timing may reveal partial matches → use constant-time byte comparison.
- Skip timestamp checks → valid signatures can be replayed → enforce a bounded window and event deduplication.
- Log headers and payloads → secrets or personal data leak → log safe IDs and failure classes only.
Follow-up questions and responses
Why verify before parsing?
Parsing first can normalize bytes and may consume a one-shot request stream. Verification must cover exactly what the provider signed; parse only after acceptance.
Can two signatures be accepted during rotation?
Yes, if the provider documents an overlap. Try only the active and retiring versions within a bounded window, record which version passed, and expire the old secret.
Is timestamp validation enough for replay protection?
No. A valid request can be replayed repeatedly within the window. Store a stable event ID or digest atomically and make the business operation idempotent.