This article details an architectural strategy for implementing application-level envelope encryption to achieve robust data security and SOC 2 compliance, moving beyond basic RBAC and database encryption. It outlines a hybrid cryptographic solution using AES for content and RSA for key wrapping, and presents the data modeling and service contracts necessary for a Symfony application. The focus is on cryptographic isolation at the record level and secure handling of encryption keys.
Read original on Dev.to #architectureAchieving SOC 2 compliance in modern SaaS applications necessitates going beyond traditional Role-Based Access Control (RBAC) and basic encryption-at-rest. While standard database encryption protects against physical theft, it offers no defense if an attacker compromises the running application or database credentials. This article proposes an application-level encryption architecture to ensure data is cryptographically isolated at the record level, proving that only authorized users access specific data and protecting it even if the perimeter fails.
The core of this advanced security model is a hybrid cryptographic strategy known as Envelope Encryption, which combines the strengths of symmetric (AES) and asymmetric (RSA) encryption. This pattern addresses the dilemma of using fast symmetric encryption for large data while securely distributing keys.
For decryption, the user's RSA Private Key unwraps the DEK, which then decrypts the document. This ensures the document content is never encrypted with a shared, static password, enhancing security significantly. The use of AES-256-GCM provides integrity and authenticity, crucial for SOC 2, by generating an Authentication Tag to prevent 'Bit-Flipping' attacks.
The database schema is designed to support this hybrid model, ensuring cryptographic isolation. Key entities include:
interface EnvelopeCryptoServiceInterface {
public function generateDek(): string;
public function encryptContent(string $plainText, string $dek): string;
public function decryptContent(string $payload, string $dek): ?string;
public function encryptDekForUser(string $dek, string $publicKey): string;
public function decryptDekWithUserKey(string $wrappedDek, string $privateKey): string;
}