Threat model
A KYC system faces two very different adversaries: fraudsters trying to get a fake identity approved, and data thieves trying to get real identities out. The design treats them separately.
Adversary 1 — the fraudster (getting a fake approved)
Section titled “Adversary 1 — the fraudster (getting a fake approved)”| Attack | Countermeasure | Residual risk |
|---|---|---|
| Counterfeit document | AI tamper inspection (fonts, layout, photo edges) + MRZ check-digit maths — forgers who alter printed data rarely recompute ICAO checksums | Sophisticated forgeries with valid MRZ → relies on AI inspection; route low-confidence to review |
| Stolen real document | Biometric face match against the document portrait (ArcFace, threshold 0.40) | Look-alikes; raise threshold for high-stakes use |
| Photo of a photo / screen replay | Active pose challenge — static images can’t follow instructions; screen re-capture flagged by AI inspection | — |
| Pre-recorded victim video | Random pose sequence unlikely to match; identity-consistency across frames | Targeted attacker with victim video library — see roadmap |
| Face swap mid-challenge | Embedding consistency check across frames (threshold 0.55) rejects person-swaps | — |
| Deepfake live puppeteering | Partially mitigated by pose verification + AI screen-artifact detection | The hard frontier for the whole industry; passive anti-spoof planned |
| Expired / invalid documents | Deterministic expiry rejection; unparseable dates → review | — |
| Brute-forcing the flow (retry until lucky) | One challenge per verification, no frame re-uploads, short-lived tokens, rate limits, full audit trail for velocity analysis | Monitor repeat external_user_ref attempts — see Monitoring |
Adversary 2 — the data thief (getting identities out)
Section titled “Adversary 2 — the data thief (getting identities out)”| Attack | Countermeasure |
|---|---|
| Database dump (SQLi, backup theft, insider) | DB holds no images/doc numbers/biometrics — only statuses, hashes, and wrapped keys, useless without the master key |
| Vault storage theft | Every blob individually AES-256-GCM encrypted; keys are elsewhere |
| Stolen offline archive media | Same — archives hold ciphertext only; erasure works even after media leaves the building |
| Master key compromise | The one catastrophic asset: keep it in KMS/hardware, never in the repo, rotate on any suspicion (re-wrap: decrypt wrapped keys with old, re-wrap with new — blobs untouched) |
| Handoff token interception | Token can upload but never read; 30-minute TTL; revocable |
| Webhook eavesdropping/forgery | Payloads carry no PII; HMAC + timestamp verification |
| Malicious/compromised reviewer | Mandatory justification notes, every action chained in the audit log; PII exposure limited to review-relevant fields |
| Log/backup leakage | PII is kept out of logs by construction; API keys logged only as fingerprints |
What we explicitly do NOT defend against
Section titled “What we explicitly do NOT defend against”- A fully compromised host with access to the master key at runtime — that is root-level compromise; defend with platform hardening, least privilege, and key externalization.
- Coercion of the legitimate user (“verify this account for me”) — process/UX problem; mitigate with contextual warnings in your product.
- Compromise of your backend and its API key — rotate keys, scope one key per product, monitor the audit log for anomalous usage.
If you find a gap not listed here, that’s exactly what the hardening roadmap process is for — file it.