Security & Privacy¶
Financial applications handle sensitive Personal Identifiable Information (PII), including Indian Permanent Account Numbers (PAN). folioman-client implements defense-in-depth security to protect sensitive investor data at rest and enforce logical multi-tenancy.
1. PAN Encryption at Rest¶
In accordance with data protection best practices, PAN numbers are never stored in plaintext within the database.
- Symmetric Encryption: PAN strings are encrypted using Fernet symmetric authenticated cryptography (
cryptography.fernet.Fernet), backed by 128-bit AES in CBC mode with PKCS7 padding and HMAC-SHA256 authentication. - Binary Storage: Ciphertext is stored in the
investors.pan_encryptedcolumn as raw bytes (LargeBinary).
sequenceDiagram
participant User as Caller / CAS PDF
participant Engine as Security Layer
participant DB as Selected Database
User->>Engine: Plaintext PAN ("ABCDE1234F")
Engine->>Engine: Normalize: uppercase + strip whitespace
Engine->>Engine: Fernet.encrypt(normalized_pan)
Engine->>Engine: hmac_sha256(fernet_key, normalized_pan)
Engine->>DB: INSERT pan_encrypted (bytes), pan_hash (hex)
2. Keyed HMAC-SHA256 Lookup (pan_hash)¶
Because statements frequently need to match existing investor records during re-imports, decrypting every investor row in the database to find a matching PAN would be inefficient and insecure.
- Why bare SHA-256 is insufficient: PANs follow a rigid 10-character alphanumeric syntax (
[A-Z]{5}[0-9]{4}[A-Z]{1}) and have low entropy, making unkeyed hashes vulnerable to precomputed rainbow table attacks. -
Keyed Hash (HMAC):
folioman-clienthashes the PAN using HMAC-SHA256, using the Fernet encryption key itself as the pepper secret: -
Security Guarantee: Without the encryption key, the
pan_hashvalue is useless and cannot be reverse-engineered or dictionary-attacked.
3. Encryption Key Resolution¶
The Fernet key is resolved using the following order of precedence:
FOLIOMAN_FERNET_KEYEnvironment Variable: Used in production, CI/CD, and containerized deployments (Docker, Kubernetes).- Key File (
~/.folioman/fernet.key): In local development or desktop environments, if no environment variable is provided,folioman-clientautomatically generates a secure 32-byte URL-safe base64 key and writes it with restricted owner-only permissions (0600).
4. Masked Display (pan_masked)¶
To prevent accidental visual leaks or logging of full PAN values:
- The single-investor detail model (
InvestorDetail) returns only a masked PAN string: - The roster list (
Investor) exposes only a boolean flaghas_pan: bool, avoiding decryption overhead entirely when listing all investors.
5. Multi-Tenancy Scoping (user_id)¶
For platforms serving multiple advisors or tenant organizations:
- All
FamilyandInvestorentities carry auser_idcolumn. - Queries are strictly scoped to the active client's
user_id: - Cross-tenant access is blocked at the database query level.