ConsentProof Verify a receipt · تحقق من إيصال Start free · ابدأ مجاناً

Security model النموذج الأمني

الخلاصة بالعربية طبقتان: حماية البيانات (تشفير الجوال والبريد عند السكون مع بصمات بحث لا تكشف النص، وعزل بنيوي بين المنشآت لا مجرد تصفية)، وحماية الدليل (أسرار توقيع لكل منشأة قابلة للتدوير دون يُتم الإيصالات القديمة). ورفضات تصميمية نفخر بها: لا بصمة أجهزة أبداً — احتمالية، وتتبع خفي من النوع الذي وُجد المنتج لضبطه؛ لا بيانات شخصية في الإيصالات؛ ولا علامة تاجر على صفحة التحقق — أداة الإثبات يجب أن تصدر بصرياً عن طبقة الدليل لا عن الطرف الذي تدافع عنه.

The security model in one page — what protects the data, what protects the evidence, and the design refusals that keep the product honest.

Data protection

LayerMechanism
PII at restPhone/email encrypted (Fernet); lookups via keyed HMAC hashes — the DB can find a subject by exact identifier without storing it in the clear, and fuzzy search is impossible by design
TenancyEvery row carries a real organization foreign key; every query is org-scoped; cross-tenant reads are structurally absent, not filtered out
Evidence integrityPer-tenant HMAC signing secrets, encrypted at rest, versioned by key_id, rotatable without orphaning history — see evidence
TransportTLS-only deployment posture (HSTS, secure cookies, proxy-TLS headers); webhook URLs must be HTTPS, payloads HMAC-signed with timestamp binding against replay
Abuse limitsPer-key API throttle (120/min), per-IP widget/subject throttle (30/min), plus the OTP-specific limits below

Credential model

Four credentials, each unable to do the others' job: secret keys (server, scoped, sha256-stored, shown once, revocable), publishable keys (browser, widget-only), subject tokens (one person, 15 minutes, opaque, Redis-backed — merchant keys are rejected on subject endpoints), and staff dashboard sessions (role-based: viewer roles cannot mutate). Every staff mutation lands in an immutable audit log.

Identity verification for rights access

The hosted rights page proves identity with one-time codes engineered against the two abuses that matter: enumeration (unknown identifiers get an indistinguishable flow that never verifies; the rate limiter counts unknowns too, so the throttle can't become the oracle) and amplification (codes are sent only to identifiers already on file). Codes are single-use, attempt-capped; sessions are 30 minutes, single-org. The anonymous-claim flow inverts the threat (the code must reach a new identifier) so it inverts the defense: hard caps per identifier and per browser record, and only for records that actually exist.

Design refusals

  • No device fingerprinting, ever. Considered for re-identifying anonymous subjects and rejected: it is probabilistic (a false match shows one person another person's record), it is itself covert tracking of the kind this product exists to regulate, and data-protection law is explicit that a controller need not collect more data merely to identify a subject. The consented alternative shipped instead: the person adds their own identifier and proves it.
  • No PII in receipts — that's what makes public verification safe.
  • No merchant branding on the verify page — the evidence artifact must visibly come from the evidence layer, not from the party it defends.
  • Preferences outside the evidence chain — mutable UX state must never dilute the append-only legal record.

Operations

Key inventory and rotation procedures (Django secret, Fernet PII key, lookup-hash key, tenant signing secrets, API keys, webhook secrets) live in the repo's docs/SECURITY-RUNBOOK.md — tenant signing rotation is one call and safe anytime; old secrets are retained because chain verification of history depends on them. Disclosed pre-launch posture: staff TOTP/2FA and the automated 72-hour SDAIA breach workflow are on the roadmap, not shipped; production KSA residency is a deployment decision pending.