Gift Moments: Privacy dei Dati via SQLite Crittografato
Mentre lo stato principale dell'applicazione risiede in soluzioni RDBMS standard, Gift Moments adotta un approccio specializzato per i repository di dati specifici dell'utente.
Il Vault Crittografato
Per i dati sensibili degli utenti, ho implementato un'architettura basata su SQLite Crittografato.
- Sicurezza a Riposo: Il file del database stesso è crittografato utilizzando SQLCipher, garantendo che anche se si accede al file fisico, i dati rimangano illeggibili senza le chiavi specifiche.
- Portabilità: L'uso di SQLite consente ai vault degli utenti di essere portabili e facilmente sottoposti a backup, comportandosi come documenti sicuri piuttosto che come semplici righe di database.
Dettagli Implementativi
La logica di crittografia è gestita in modo trasparente dal gestore delle connessioni. Quando viene richiesta una connessione, il sistema applica automaticamente la chiave di crittografia definita nella configurazione dell'ambiente.
// tc-be/src/common/services/sqlite/sqlite-connection-manager.ts
// Configure encryption if key is available
if (this.encryptionKey) {
console.log(`Setting up encryption for database: ${dbPath}`);
// Set to use standard SQLCipher encryption (compatible with DB Browser)
db.pragma(`cipher='sqlcipher'`);
db.pragma(`legacy=4`);
// Check if this is a new database (doesn't exist or is empty)
const isNewDb = !fs.existsSync(dbPath) || fs.statSync(dbPath).size === 0;
if (isNewDb) {
// For new database, set the key
db.pragma(`key='${this.escapePragmaValue(this.encryptionKey)}'`);
} else {
// For existing database, try to open with key
try {
db.pragma(`key='${this.escapePragmaValue(this.encryptionKey)}'`);
// Verify the database is readable (will throw if key is wrong)
db.prepare('SELECT 1').get();
} catch (error) {
throw new Error(`Cannot decrypt database with provided key: ${dbPath}`);
}
}
}
Isolamento Utente
A ogni utente viene assegnato un file di database SQLite dedicato. Questa decisione architettonica garantisce un rigoroso isolamento dei dati; i dati di un utente sono fisicamente separati dagli altri, non solo logicamente filtrati da una clausola WHERE.
// tc-be/src/common/services/sqlite/sqlite.service.ts
// * Constructs the path to the user's database file
private getUserDbPath(userId: number): string {
const userIdStr = String(userId).padStart(8, '0');
// Each user gets their own isolated directory and database file
return path.join(this.dbPath, `${userIdStr}/user_${userIdStr}.db`);
}
Questo approccio isola i contesti utente e aggiunge un livello critico di difesa per le informazioni personali, separato dalla logica principale dell'applicazione.