- common/crypto/crypto.util.ts: encrypt/decrypt (GCM, random 96-bit IV), format v1:base64(iv|tag|ct) - EncryptedTransformer applied to users.name + drivers.vehicle_plate (auto, no service change) - blindIndex (HMAC) helper for future searchable-field encryption (phone) - tolerant decrypt for legacy plaintext → no migration needed (varchar holds base64) - ENCRYPTION_KEY env + boot warning if unset; docs/16-encryption.md - fixes Siro's documented CBC+fixed-IV flaw Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
34 lines
2.7 KiB
Markdown
34 lines
2.7 KiB
Markdown
# 16 — التشفير (معيار at-rest)
|
|
|
|
> تصحيح لخطأ سيرو الموثّق (CBC بـ IV ثابت). المعيار عندنا: **AES-256-GCM + IV عشوائي لكل قيمة**.
|
|
|
|
## المعيار
|
|
- **الخوارزمية:** AES-256-GCM (تشفير موثَّق authenticated — يكشف أي عبث).
|
|
- **IV:** عشوائي **96-بت لكل عملية** (`randomBytes(12)`) — **لا IV ثابت إطلاقاً**.
|
|
- **صيغة التخزين:** `v1:base64(iv | authTag | ciphertext)` — الإصدار في المقدمة للترقية المستقبلية.
|
|
- **المفتاح:** `ENCRYPTION_KEY` (32 بايت hex؛ `openssl rand -hex 32`). إلزامي للإنتاج؛ بدونه مفتاح تطوير غير آمن + تحذير عند الإقلاع.
|
|
- **الكود:** `common/crypto/crypto.util.ts` — `encrypt` / `decrypt` / `blindIndex` / `EncryptedTransformer`.
|
|
|
|
## كيف يُطبَّق (بلا تعقيد)
|
|
عبر **TypeORM transformer** على العمود: يشفّر عند الكتابة ويفكّ عند القراءة **تلقائياً**. لا يتغير منطق الخدمات ولا استجابات الـ API (تظهر مفكوكة).
|
|
```ts
|
|
@Column({ type: 'varchar', nullable: true, transformer: EncryptedTransformer })
|
|
name: string;
|
|
```
|
|
|
|
## المطبّق حالياً
|
|
- `users.name` · `drivers.vehicle_plate`.
|
|
|
|
## الحقول القابلة للبحث (مثل الهاتف) — الحل الصحيح
|
|
تشفير GCM عشوائي **يمنع المطابقة بالتساوي** (كل تشفير مختلف). للبحث نستخدم **فهرس أعمى (blind index)**: عمود إضافي = `HMAC-SHA256(القيمة)` حتمي للبحث + العمود المشفّر للتخزين. مخطّط عند بناء بحث الهاتف/الهوية.
|
|
|
|
## الميغريشن (صحيح ومتوافق)
|
|
- **لا تغيير مخطّط:** الأعمدة `varchar` تتّسع للنص المشفّر (base64). لا هجرة لازمة لتحويل النوع.
|
|
- **تسامح انتقالي:** `decrypt` يُرجع النص القديم غير المشفّر كما هو (يبدأ بلا `v1:`) — فلا تنكسر القراءة على بيانات قديمة؛ والكتابة الجديدة تُشفّر.
|
|
- **عند نقل بيانات حقيقية من سيرو:** تُقرأ مفكوكةً من سيرو ثم تُكتب فتُشفَّر عندنا تلقائياً.
|
|
|
|
## ما يُشفَّر مستقبلاً
|
|
وثائق السائق وأرقام الهوية/الرخصة (وحدة الوثائق)، ووجهات السحب الحساسة، وأي PII يُضاف — بنفس المحوّل.
|
|
|
|
← يُقرأ مع: [14-server-conventions](14-server-conventions.md) · [08-data-model](08-data-model.md)
|