KYC Document Security
Document ID: SEC-KYC-001 Version: 1.0 Classification: Internal Effective Date: January 2026 Next Review: July 2026 Owner: Keshless Security Team
1. Overview
All KYC documents are classified as highly sensitive PII and are protected with multiple security layers in compliance with FSRA requirements and international data protection standards.
1.1 Document Types
| Document Type | Storage Location | Classification | Retention |
|---|---|---|---|
| ID Card (Front) | gs://keshless-documents/kyc/ | Restricted | 7 years |
| ID Card (Back) | gs://keshless-documents/kyc/ | Restricted | 7 years |
| Selfie (Liveness) | gs://keshless-documents/selfies/ | Restricted | 7 years |
| Business Registration | gs://keshless-documents/vendor-kyc/ | Confidential | 7 years |
| Director ID | gs://keshless-documents/vendor-kyc/ | Restricted | 7 years |
2. Storage Security
2.1 Private Cloud Storage
All KYC documents are stored in GCP Cloud Storage (europe-west1 region) with strict access controls:
# Bucket configuration
gcloud storage buckets create gs://keshless-documents \
--location=europe-west1 \
--default-storage-class=STANDARD \
--uniform-bucket-level-access \
--public-access-preventionSecurity Features:
- Public Access Prevention: No anonymous or public access possible
- Uniform Bucket-Level Access: No per-object ACLs (simpler, more secure)
- Region Restriction: Data never leaves
europe-west1 - Only Cloud Run Service Account: Only the API's service account has access
2.2 Encryption
| Layer | Algorithm | Key Management |
|---|---|---|
| At Rest | AES-256 | Google-managed keys |
| In Transit | TLS 1.3 | Auto-renewed certificates |
| Backup (Secrets) | AES-256-GCM | Customer-managed key |
3. Access Control
3.1 Signed URLs
All document access uses time-limited signed URLs. No permanent URLs are ever generated or stored.
// Generating a signed URL for KYC document
const signedUrl = await GCSService.getSignedUrl(documentKey, 15); // 15 minutesURL Expiration Settings:
| Context | Expiry | Rationale |
|---|---|---|
| KYC Viewing (Dashboard) | 15 minutes | Minimal exposure for review |
| OCR Verification | 60 minutes | Allow time for AI processing |
| Document Upload Response | 15 minutes | Immediate viewing after upload |
Key Principles:
- URLs cannot be shared or bookmarked (expire quickly)
- Each API request generates a fresh URL
- Database stores only the GCS key, never the URL
- Expired URLs return 403 Forbidden
3.2 Authentication & Authorization
| Layer | Requirement |
|---|---|
| API Authentication | Valid JWT token required |
| User Access | Users can only access their own documents |
| Admin Access | ADMIN or SUPER_ADMIN role required |
| Vendor Admin | Can access sub-user documents |
// Authorization check example
if (req.user.role !== 'ADMIN' && req.user.id !== document.userId) {
throw new UnauthorizedError('Access denied');
}4. Audit Trail
4.1 Access Logging
Every document access is logged to the AuditLog table:
| Field | Description |
|---|---|
eventType | DOCUMENT_VIEWED, DOCUMENT_UPLOADED, DOCUMENT_DELETED |
actorId | User/Admin who accessed |
targetId | Document key/User ID |
ipAddress | Client IP address |
createdAt | Timestamp (UTC) |
metadata | Additional context (user agent, request ID) |
4.2 Hash Chain Verification
Audit logs are hash-chained for tamper detection:
Entry 1: hash(data1)
Entry 2: hash(data2 + hash(entry1))
Entry 3: hash(data3 + hash(entry2))Verification:
- Daily integrity checks via Cloud Scheduler
- Admin can manually verify via
/admin/audit-logs/verifyendpoint - Any tampering breaks the chain immediately
4.3 Sample Audit Log Entry
{
"eventType": "DOCUMENT_VIEWED",
"description": "Admin viewed KYC document for user verification",
"actorId": "admin-123",
"actorType": "admin",
"targetType": "kyc_document",
"targetId": "kyc/user-456/id-front.jpg",
"ipAddress": "102.215.xxx.xxx",
"hashPrevious": "a3f5c2...",
"hashCurrent": "b7d4e1...",
"createdAt": "2026-01-24T10:30:00.000Z"
}5. Data Retention
5.1 Retention Periods
| Document Type | Active Retention | Post-Closure Retention | Total |
|---|---|---|---|
| User KYC | Account lifetime | 5 years | Up to 7+ years |
| Vendor KYC | Business lifetime | 5 years | Up to 7+ years |
| Audit Logs | Permanent | Permanent | Indefinite |
5.2 Automatic Deletion
// Documents eligible for deletion:
// - Account closed + 5 years elapsed
// - No regulatory hold
// - No pending investigations
async function scheduleDocumentDeletion(userId: string) {
const fiveYearsFromNow = new Date();
fiveYearsFromNow.setFullYear(fiveYearsFromNow.getFullYear() + 5);
await ScheduledDeletion.create({
userId,
scheduledFor: fiveYearsFromNow,
status: 'pending'
});
}5.3 Deletion Logging
All deletions are logged and cannot be undone:
{
"eventType": "DOCUMENT_DELETED",
"description": "KYC document deleted after retention period",
"targetId": "kyc/user-123/id-front.jpg",
"metadata": {
"reason": "RETENTION_PERIOD_EXPIRED",
"retentionDays": 2555,
"accountClosedAt": "2021-01-15T00:00:00.000Z"
}
}6. OCR Verification Flow
6.1 Document Processing
When documents are uploaded for KYC verification:
- Upload: Document uploaded to GCS, key stored in DB
- URL Generation: Fresh 60-minute signed URL generated
- OCR Processing: Gemini AI extracts text from signed URL
- Storage: Extracted data stored in PostgreSQL (encrypted)
- URL Expiry: Signed URL expires after 60 minutes
// OCR verification flow
const documentKey = user.idFrontImageKey; // From database
const signedUrl = await GCSService.getSignedUrl(documentKey, 60);
const ocrResult = await geminiService.extractIdData(signedUrl);6.2 Manual Review
For documents flagged for manual review:
- Admin navigates to verification dashboard
- API generates fresh 15-minute signed URL
- Admin reviews document within time limit
- If more time needed, refresh generates new URL
- All views logged to audit trail
7. Incident Response
7.1 Data Breach Procedures
See Incident Response Plan for full procedures.
Immediate Actions:
- Revoke all service account credentials
- Rotate bucket access keys
- Notify affected users within 72 hours
- Report to FSRA within 72 hours
- Preserve audit logs for investigation
7.2 Unauthorized Access
If unauthorized document access is detected:
- Block the actor's account immediately
- Log incident details with full audit trail
- Generate report of all documents accessed
- Notify affected users
- Review and strengthen access controls
8. Compliance Checklist
8.1 FSRA Requirements
- [x] Documents stored in approved jurisdiction (europe-west1)
- [x] Encryption at rest (AES-256)
- [x] Encryption in transit (TLS 1.3)
- [x] Access logging and audit trails
- [x] 7-year retention capability
- [x] Role-based access control
- [x] Regular security assessments
8.2 Technical Controls
- [x] Private buckets (no public access)
- [x] Signed URLs with time expiration
- [x] Hash-chained audit logs
- [x] Service account authentication only
- [x] No permanent document URLs
- [x] Automated backup to separate bucket
9. Related Documentation
- Data Privacy Policy - Overall data handling
- Information Security Policy - Security controls
- Incident Response Plan - Breach procedures
- Backup System - Document backup procedures