Building automated identity verification engines for international compliance requires robust encryption and sandboxed processing. Here is how we built an enterprise-grade document validation engine.
By Rehan Idrisi · · Updated · 5 min read
Part of: Automation
Handling identity documents such as passports, drivers licenses or business registry files is a high-risk engineering task. You have to safeguard personal data from leaks while keeping upload and verification fast. The individual checks are largely solved problems available off the shelf from third-party verification partners: OCR extraction, face matching, document authenticity scoring. The hard engineering is everything around those checks. Ingestion, storage, retries, auditability, and what happens when a result comes back ambiguous instead of a clean pass or fail.
We designed and implemented an enterprise-grade eKYC/KYB identity verification platform that validates identity documents for European and Chinese-market clients. This is how we secured the platform architecture.
An uploaded PDF can carry an embedded shell script. So every file upload is quarantined in a sandboxed directory before anything else touches it.
We implemented strict MIME-type checks and file signature verification, reading the actual magic bytes of the file and never trusting the extension it arrived with. Non-compliant files are rejected immediately and logged.
function validateFileUpload($tmpName, $clientName) {
$allowedMimeTypes = ['image/jpeg', 'image/png', 'application/pdf'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $tmpName);
finfo_close($finfo);
if (!in_array($mime, $allowedMimeTypes)) {
throw new Exception('Invalid file signature detected.');
}
// Sanitize file name to prevent directory traversal
$safeName = preg_replace('/[^a-zA-Z0-9-.]/', '_', $clientName);
return $safeName;
}Once files are validated they are encrypted at rest using AES-256, before being written to our storage server. The decryption keys are managed separately, so compromised storage media still yields nothing readable.
This encryption layer complies with GDPR and international data protection standards, protecting sensitive identity data from unauthorized access.
Upstream verification checks, running OCR or a facial comparison API, take anywhere from 10 seconds to 2 minutes. Blocking the user's HTTP connection for that long causes timeouts, so it is not an option.
The interface gets an immediate '202 Accepted' instead. Background queue workers then process the documents, send them to our verification partner, and listen for the webhooks that update the account status once a result lands.
Serving European and Chinese-market clients from a single platform means the same document type falls under two regimes that don't fully overlap: GDPR's rules on transferring personal data out of the EU, and China's data-protection requirements, which in many cases call for the personal information of Chinese residents to be stored within mainland China. We designed storage as region-scoped from the start instead of as one global bucket. A client's documents are written and processed within their applicable region, and the pipeline's queue and worker layer is deployed per region, because one central location cannot always serve every client legally even where it could serve them technically.
The safest identity document is the one you no longer have. Once a verification decision is made and the compliance retention window required for that client's jurisdiction has passed, documents are purged automatically instead of being kept indefinitely just in case. A manual, someone-remembers-to-do-it deletion process does not hold up under an audit. We log the deletion itself, recording what was removed, when, and under which retention rule, separately from the document content, so compliance can be demonstrated without keeping the sensitive file around as its own evidence.
Encryption at rest protects against a storage breach. It says nothing about who looked at a given passport scan, which is an access-control and audit-logging problem instead of an encryption one. Every decryption of a stored document is tied to a specific authenticated user and a stated reason, a compliance review or a fraud investigation, and written to an append-only audit log kept separate from the application's normal logs. A compliance officer's dashboard reads from that log. Trusting that access only ever happened through the expected code path is not the same as knowing it did.
OCR and facial-comparison checks don't return a clean pass or fail for every document. Glare on a laminated ID, a slightly cropped edge, a selfie taken in bad lighting: each produces a result the automated check cannot confidently score either way. Treating every non-pass as a hard rejection loses you legitimate customers over a scanning problem that was never a fraud problem. So anything the verification partner scores as inconclusive, plus anything it flags outright as suspicious, is routed into a manual review queue for a compliance reviewer. The automated path stays scoped to the cases it can decide well.
You can't develop or load-test this pipeline against real users' passports without creating the exact exposure the whole architecture exists to prevent. We test against synthetic and consented sample documents, in an isolated staging environment with no network path to the production verification partner or production storage. A bug in a test run therefore can't trigger a real verification call, and can't write test data into the same encrypted store as real customer documents.
When building identity verification systems, treat security not as a feature, but as the core architectural constraint. Verify everything.
Uploaded documents are quarantined in a private, non-public directory and validated by file signature, not file extension, before any processing happens. Once verified, files are encrypted at rest with AES-256, with decryption keys held in a separate key-management service, so a storage breach alone never exposes readable identity documents.