Skip to content

Security: alinawazcode/passli

Security

SECURITY.md

Security Policy & Cryptographic Architecture

Passli is engineered with an ephemeral, privacy-first access control architecture designed for secure, time-gated document sharing without master credential exposure or plaintext data retention.

⚠️ Security Model & Limitations

Passli operates with complete architectural transparency:

  • Not Zero-Knowledge: Passli is not zero-knowledge. Files are encrypted at rest server-side using AES-256-GCM with a master key held by the server. Because the server holds the key, the server can read documents.
  • Server-Side Enforcement: Expiry, access control, and revocation are enforced by the server; there is no hardware-based key destruction.
  • No Independent Audit: Passli has not been independently audited or certified by SOC 2 or any external security agency.

🛡️ Supported Versions

We actively support and release security updates for the following versions of Passli:

Version Supported Security Maintenance Cryptographic Engine
1.3.x (Current Main) ✅ Active Security Patches & Threat Mitigation AES-256-GCM + PBKDF2-HMAC-SHA256
1.2.x ✅ Bugfixes & Maintenance AES-256-GCM Migration Compatible
< 1.2.0 ❌ Deprecated Legacy

🔒 Security Specifications & Threat Model

1. Authenticated Encryption at Rest (AES-256-GCM)

  • Engine: Industry-standard AES-256-GCM (Galois/Counter Mode) with Authenticated Encryption with Associated Data (AEAD) via Python's cryptography library.
  • Storage Wire Format: [12-byte Nonce][Ciphertext + 16-byte GCM Authentication Tag]. Total cryptographic overhead is exactly 28 bytes per stored file.
  • Nonce Uniqueness: Each document encryption operation generates a unique, non-repeating 12-byte CSPRNG nonce (os.urandom(12)), preventing replay attacks and cryptographic pattern leakage across identical files.
  • Tamper Resistance: Any modification, bit-flip, or byte truncation of stored files triggers an immediate VaultDecryptionError upon tag verification failure, preventing corrupted or tampered payloads from ever reaching application memory.
  • Key Management: Primary vault key is supplied via VAULT_MASTER_KEY (32-byte hexadecimal key). In local/development environments without explicit configuration, a deterministic 256-bit key is derived using HKDF-SHA256 from Django's SECRET_KEY.

2. In-Memory Decrypted Streaming (Zero Plaintext Disk Writes)

  • Owner previews, owner downloads, and verified recipient views decrypt ciphertext strictly in-memory using io.BytesIO streams.
  • No plaintext files are ever written to the physical file system or temp directories during inspection or streaming.
  • Responses enforce defensive security headers:
    X-Content-Type-Options: nosniff
    Cache-Control: no-store, no-cache, must-revalidate, private
    Pragma: no-cache

3. Ephemeral Key Access Control & Hashed Storage

  • Plaintext 8-character Share Keys (XXXX-XXXX) are generated dynamically using cryptographically secure random sources (secrets.choice from a 31-character unambiguous alphabet excluding lookalike characters 0/O, 1/I/L).
  • Plaintext keys are never stored in the database or written to server logs; only salted PBKDF2-HMAC-SHA256 hashes are retained (django.contrib.auth.hashers.make_password).
  • The server verifies submitted keys against the PBKDF2 hash using constant-time comparison to establish ephemeral, session-bound recipient access.

4. Binary Digest Integrity (SHA-256)

  • Every uploaded vault document is processed through SHA-256 checksum hashing on the original plaintext on ingestion.
  • The binary digest is preserved in the database for client and recipient verification against source documents, even while stored files are encrypted at rest.

5. Multi-Tenant IDOR Protection

  • All vault access handlers strictly filter by user=request.user or authenticated recipient session ID before serving document contents.
  • Direct object reference tampering across tenant boundaries is caught at the view boundary with HTTP 404/403 responses.

6. Brute-Force & Rate-Limiting Defenses

  • Failed Share Key attempts trigger structured security logs and increment attempt counters.
  • Five consecutive failed key attempts enforce immediate lockout and trigger an automated security notification email to the pass owner.

7. Cryptographically Secure Email Auth & Alerts

  • Password reset workflows utilize time-sensitive, single-use cryptographic tokens generated by Django's PasswordResetTokenGenerator.
  • Email verification codes utilize time-gated 6-digit numeric OTPs stored with expiration timestamps.
  • Critical security events (password resets, failed passcode attempts, onboarding) trigger automated transactional emails via SMTP/TLS.

8. Structured Audit Logging & PII Sanitization

  • Access events are recorded in an append-only relational audit table (ShareAccessLog), capturing client IP, user agent, event type, and outcome timestamp for owner visibility.
  • Sensitive values (passcode characters, authorization tokens, full email addresses) are sanitized and masked before log persistence.

🚨 Reporting a Vulnerability

If you discover a potential security vulnerability within Passli, please do not open a public issue. Instead, follow our responsible disclosure procedure:

  1. Send an email with full reproduction steps to: alinawaz.code@gmail.com
  2. Include the following details in your report:
    • Description of the vulnerability and its potential impact.
    • Step-by-step instructions or proof-of-concept (PoC) code.
    • Affected URLs, endpoints, or parameters.
    • Suggestions for mitigation or fix (optional).

Response Timeline

  • Initial Acknowledgment: Within 24 hours.
  • Vulnerability Assessment: Within 48 hours.
  • Patch & Release: Within 7 business days for critical vulnerabilities.

We credit all responsible security researchers in our release notes and Hall of Fame.

There aren't any published security advisories