Creo applicazioni web moderne e strumenti digitali personalizzati per aiutare le attività a crescere attraverso l'innovazione tecnologica. La mia passione è unire informatica ed economia per generare valore reale.
La mia passione per l'informatica è nata tra i banchi dell'Istituto Tecnico Commerciale di Maglie, dove ho scoperto il potere della programmazione e il fascino di creare soluzioni digitali. Fin da subito, ho capito che l'informatica non era solo codice, ma uno strumento straordinario per trasformare idee in realtà.
Durante gli studi superiori in Sistemi Informativi Aziendali, ho iniziato a intrecciare informatica ed economia, comprendendo come la tecnologia possa essere il motore della crescita per qualsiasi attività. Questa visione mi ha accompagnato all'Università degli Studi di Bari, dove ho conseguito la Laurea in Informatica, approfondendo le mie competenze tecniche e la mia passione per lo sviluppo software.
Oggi metto questa esperienza al servizio di imprese, professionisti e startup, creando soluzioni digitali su misura che automatizzano processi, ottimizzano risorse e aprono nuove opportunità di business. Perché la vera innovazione inizia quando la tecnologia incontra le esigenze reali delle persone.
Le Mie Competenze
Analisi Dati & Modelli Previsionali
Trasformo i dati in insights strategici con analisi approfondite e modelli predittivi per decisioni informate
Automazione Processi
Creo strumenti personalizzati che automatizzano operazioni ripetitive e liberano tempo per attività a valore aggiunto
Sistemi Custom
Sviluppo sistemi software su misura, dalle integrazioni tra piattaforme alle dashboard personalizzate
Credo fermamente che l'informatica sia lo strumento più potente per trasformare le idee in realtà e migliorare la vita delle persone.
Democratizzare la Tecnologia
La mia missione è rendere l'informatica accessibile a tutti: dalle piccole imprese locali alle startup innovative, fino ai professionisti che vogliono digitalizzare la propria attività. Ogni realtà merita di sfruttare le potenzialità del digitale.
Unire Informatica ed Economia
Non è solo questione di scrivere codice: è capire come la tecnologia possa generare valore reale. Intrecciando competenze informatiche e visione economica, aiuto le attività a crescere, ottimizzare processi e raggiungere nuovi traguardi di efficienza e redditività.
Creare Soluzioni su Misura
Ogni attività è unica, e così devono esserlo le soluzioni. Sviluppo strumenti personalizzati che rispondono alle esigenze specifiche di ciascun cliente, automatizzando processi ripetitivi e liberando tempo per ciò che conta davvero: far crescere il business.
Trasforma la Tua Attività con la Tecnologia
Che tu gestisca un negozio, uno studio professionale o un'azienda, posso aiutarti a sfruttare le potenzialità dell'informatica per lavorare meglio, più velocemente e in modo più intelligente.
Il mio percorso accademico e le tecnologie che padroneggio
Certificazioni Professionali
8 certificazioni conseguite
Nuovo
Visualizza
Reinvention With Agentic AI Learning Program
Anthropic
Dicembre 2024
Nuovo
Visualizza
Agentic AI Fluency
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency for Students
Anthropic
Dicembre 2024
Nuovo
Visualizza
AI Fluency: Framework and Foundations
Anthropic
Dicembre 2024
Nuovo
Visualizza
Claude with the Anthropic API
Anthropic
Dicembre 2024
Visualizza
Master SQL
RoadMap.sh
Novembre 2024
Visualizza
Oracle Certified Foundations Associate
Oracle
Ottobre 2024
Visualizza
People Leadership Credential
Connect
Settembre 2024
Linguaggi & Tecnologie
Java
Python
JavaScript
Angular
React
TypeScript
SQL
PHP
CSS/SCSS
Node.js
Docker
Git
💼
12/2024 - Presente
Custom Software Engineering Analyst
Accenture
Bari, Puglia, Italia · Ibrida
Analisi e sviluppo di sistemi informatici attraverso l'utilizzo di Java e Quarkus in Health and Public Sector. Formazione continua su tecnologie moderne per la creazione di soluzioni software personalizzate ed efficienti e sugli agenti.
💼
06/2022 - 12/2024
Analista software e Back End Developer Associate Consultant
Links Management and Technology SpA
Esperienza nell'analisi di sistemi software as-is e flussi ETL utilizzando PowerCenter. Formazione completata su Spring Boot per lo sviluppo di applicazioni backend moderne e scalabili. Sviluppatore Backend specializzato in Spring Boot, con esperienza in progettazione di database, analisi, sviluppo e testing dei task assegnati.
💼
02/2021 - 10/2021
Programmatore software
Adesso.it (prima era WebScience srl)
Esperienza nell'analisi AS-IS e TO-BE, evoluzioni SEO ed evoluzioni website per migliorare le performance e l'engagement degli utenti.
🎓
2018 - 2025
Laurea in Informatica
Università degli Studi di Bari Aldo Moro
Bachelor's degree in Computer Science, focusing on software engineering, algorithms, and modern development practices.
📚
2013 - 2018
Diploma - Sistemi Informativi Aziendali
Istituto Tecnico Commerciale di Maglie
Technical diploma specializing in Business Information Systems, combining IT knowledge with business management.
Contattami
Hai un progetto in mente? Parliamone! Compila il form qui sotto e ti risponderò al più presto.
* Campi obbligatori. I tuoi dati saranno utilizzati solo per rispondere alla tua richiesta.
07 - Crittografia Pratica per Developer: AES-256-GCM, Hashing e TLS 1.3
La crittografia e il fondamento invisibile della sicurezza informatica moderna. Ogni password salvata
in un database, ogni dato sensibile trasmesso via HTTPS, ogni firma digitale che autentica un
software update: tutto dipende da primitive crittografiche implementate correttamente. Eppure
la crittografia e anche uno degli ambiti in cui i developer commettono gli errori più gravi,
spesso senza rendersene conto.
Secondo OWASP Top 10:2025, le Cryptographic Failures (A02) rimangono tra le prime
tre vulnerabilità più critiche nelle applicazioni web. Non si tratta di attacchi esotici che
richiedono quantum computer: la maggior parte dei breach crittografici avviene per errori banali.
MD5 o SHA-1 usati per hash di password. AES in modalità ECB che produce pattern visibili nel
ciphertext. IV (Initialization Vector) riutilizzati. Chiavi hardcoded nel codice sorgente.
Errori che nessun pentest avanzato e necessario per sfruttare.
Questo articolo affronta la crittografia dal punto di vista del developer: non teoria matematica,
ma pattern pratici in Node.js e Web Crypto API. Imparerai quando usare crittografia simmetrica
vs asimmetrica, come implementare correttamente AES-256-GCM, quale algoritmo di hashing scegliere
per le password, come configurare TLS 1.3 e come prepararti alla crittografia post-quantum. Ogni
sezione include code examples pronti per la produzione con le insidie da evitare.
Cosa Imparerai
Crittografia simmetrica vs asimmetrica: quando usare quale approccio
AES-256-GCM: implementazione corretta con IV casuale e authentication tag
RSA vs ECC (ECDSA, Ed25519): confronto di performance e sicurezza nel 2025
Password hashing con Argon2id e bcrypt: parametri OWASP raccomandati
SHA-256 per integrità dei dati vs hash di password: usi corretti
TLS 1.3: configurazione sicura in Node.js con cipher suite moderne
Key management: evitare chiavi hardcoded, usare variabili d'ambiente e KMS
Web Crypto API: crittografia nel browser senza dipendenze esterne
Crittografia post-quantum: NIST FIPS 203 (ML-KEM) e preparazione al 2030
Checklist Angular: pattern sicuri per gestire dati sensibili nel frontend
Crittografia Simmetrica vs Asimmetrica
La scelta tra crittografia simmetrica e asimmetrica non e una questione di preferenza: e una
decisione architetturale che dipende dal problema da risolvere. Confondere i due approcci porta
sistematicamente a vulnerabilità o a performance inutilmente degradate.
Crittografia simmetrica usa la stessa chiave per cifrare e decifrare. E rapida
(AES-256-GCM cifra gigabyte al secondo su hardware moderno) ed e adatta per cifrare grandi
quantità di dati. Il problema e la distribuzione della chiave: come la condividi in modo sicuro
con chi deve decifrare i dati?
Crittografia asimmetrica usa una coppia di chiavi matematicamente correlate:
una pubblica (che puoi distribuire liberamente) e una privata (che tieni segreta). Risolve il
problema della distribuzione delle chiavi ma e ordini di grandezza più lenta della simmetrica.
RSA-2048 cifra circa 250 byte al secondo per operazione, mentre AES-256-GCM arriva a multi-GB/s.
Il pattern professionale combina i due approcci in un sistema ibrido, esattamente come fa TLS:
la crittografia asimmetrica viene usata solo per scambiare in modo sicuro una chiave di sessione
simmetrica; poi tutti i dati vengono cifrati con quella chiave AES. Questo e il fondamento di
HTTPS, SSH, e di qualsiasi protocollo sicuro moderno.
Principio Fondamentale
Simmetrica (AES-256-GCM): per cifrare dati at-rest e in-transit in bulk
Asimmetrica (RSA/ECC): per scambiare chiavi, autenticare identità, firme digitali
Ibrida: per quasi tutti i casi d'uso reali (TLS, PGP, Signal Protocol)
Hashing (Argon2, SHA-256): funzione unidirezionale, non reversibile
AES-256-GCM: Implementazione Corretta
AES-256-GCM (Advanced Encryption Standard con chiave 256-bit in modalità Galois/Counter Mode)
e lo standard de facto per la crittografia simmetrica autenticata. La modalità GCM ha due
proprietà critiche che la rendono superiore ad alternative come AES-CBC o AES-ECB:
Confidenzialita: i dati sono cifrati e illeggibili senza la chiave
Autenticazione (AEAD): produce un authentication tag che garantisce l'integrita
dei dati. Se qualcuno modifica il ciphertext, la decifrazione fallisce con un errore esplicito.
AES-ECB e AES-CBC non hanno questa proprietà: un attaccante può modificare il ciphertext senza
che la decifrazione se ne accorga (bit-flipping attack).
Errori Critici con AES da Evitare
Mai riutilizzare l'IV con la stessa chiave: in AES-GCM, riusare IV+chiave
compromette completamente la confidenzialita. Genera sempre un IV casuale di 12 byte per ogni
operazione di cifratura.
Mai usare AES-ECB: la modalità ECB produce lo stesso output per lo stesso
input (il classico "ECB penguin"), rendendo visibili i pattern nei dati.
Mai hardcodare la chiave: la chiave deve venire da variabili d'ambiente,
KMS (Key Management Service) o HSM, mai dal codice sorgente.
Verifica sempre il tag di autenticazione prima di usare i dati decifrati.
Ecco un'implementazione completa e corretta di AES-256-GCM in Node.js con il modulo
crypto built-in:
// crypto-utils.ts - Implementazione AES-256-GCM per Node.js
import { randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
// ============================================================
// COSTANTI - non hardcodate, vengono da env/KMS in produzione
// ============================================================
const ALGORITHM = 'aes-256-gcm';
const IV_LENGTH = 12; // 96 bit - raccomandato per GCM
const AUTH_TAG_LENGTH = 16; // 128 bit - lunghezza massima del tag
const KEY_LENGTH = 32; // 256 bit
// Interfaccia per il risultato della cifratura
interface EncryptedData {
iv: string; // base64
ciphertext: string; // base64
authTag: string; // base64
}
// ============================================================
// DERIVAZIONE CHIAVE da password (per chiavi derivate da secret)
// In produzione preferisci chiavi generate da KMS/HSM
// ============================================================
function deriveKey(password: string, salt: Buffer): Buffer {
// scrypt e memory-hard: resistente a brute-force su GPU
return scryptSync(password, salt, KEY_LENGTH, {
N: 32768, // CPU/memory cost (2^15)
r: 8,
p: 1,
});
}
// ============================================================
// CIFRATURA
// ============================================================
export function encrypt(plaintext: string, keyHex: string): EncryptedData {
// Chiave da hex string (32 byte = 64 caratteri hex)
const key = Buffer.from(keyHex, 'hex');
if (key.length !== KEY_LENGTH) {
throw new Error(`Chiave AES-256 richiede 32 byte, ricevuti
#123;key.length}`);
}
// IV casuale e unico per ogni operazione - CRITICO
const iv = randomBytes(IV_LENGTH);
// Crea cipher GCM
const cipher = createCipheriv(ALGORITHM, key, iv, {
authTagLength: AUTH_TAG_LENGTH,
});
// Cifra i dati
const encryptedBuffer = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final(),
]);
// Estrai il tag di autenticazione DOPO cipher.final()
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('base64'),
ciphertext: encryptedBuffer.toString('base64'),
authTag: authTag.toString('base64'),
};
}
// ============================================================
// DECIFRATURA
// ============================================================
export function decrypt(encrypted: EncryptedData, keyHex: string): string {
const key = Buffer.from(keyHex, 'hex');
const iv = Buffer.from(encrypted.iv, 'base64');
const ciphertext = Buffer.from(encrypted.ciphertext, 'base64');
const authTag = Buffer.from(encrypted.authTag, 'base64');
const decipher = createDecipheriv(ALGORITHM, key, iv, {
authTagLength: AUTH_TAG_LENGTH,
});
// Imposta il tag per la verifica dell'integrita
decipher.setAuthTag(authTag);
try {
// Se il tag non corrisponde, decipher.final() lancia un errore
const decryptedBuffer = Buffer.concat([
decipher.update(ciphertext),
decipher.final(), // Lancia Error se authTag non valido
]);
return decryptedBuffer.toString('utf8');
} catch (err) {
// Dati manomessi o chiave errata
throw new Error('Decifrazione fallita: dati corrotti o chiave non valida');
}
}
// ============================================================
// USO ESEMPIO
// ============================================================
// La chiave viene da una variabile d'ambiente (mai hardcodata)
const AES_KEY = process.env['AES_256_KEY']!; // 64 caratteri hex = 32 byte
const datiSensibili = JSON.stringify({
carta: '4111111111111111',
scadenza: '12/26',
cvv: '123',
});
const cifrati = encrypt(datiSensibili, AES_KEY);
console.log('Cifrato:', cifrati);
// Output: { iv: 'abc...', ciphertext: 'xyz...', authTag: 'def...' }
const decifrati = decrypt(cifrati, AES_KEY);
console.log('Decifrato:', decifrati);
// Output: {"carta":"4111111111111111","scadenza":"12/26","cvv":"123"}
RSA e ECC: Firme Digitali e Crittografia Asimmetrica
Nel 2025, la scelta tra RSA e ECC (Elliptic Curve Cryptography) e chiara: per nuovi sistemi,
preferisci ECC. A parita di livello di sicurezza, le chiavi ECC sono drasticamente più piccole
e le operazioni sono significativamente più veloci. Una chiave ECDSA P-256 da 256 bit offre la
stessa sicurezza di una chiave RSA da 3072 bit, con operazioni di firma 10-15x più veloci.
Algoritmo
Dimensione chiave
Livello sicurezza
Performance relativa
Raccomandato 2025
RSA-2048
2048 bit
112 bit
Baseline (1x)
Solo per compatibilità legacy
RSA-3072
3072 bit
128 bit
0.3x
Accettabile, ma preferisci ECC
ECDSA P-256
256 bit
128 bit
10x più veloce di RSA-3072
Si, per TLS e firme
Ed25519
256 bit
128 bit
15x più veloce di RSA-3072
Si, preferito per firme digitali
Ed25519 (Edwards-curve Digital Signature Algorithm) e la scelta moderna per
le firme digitali: e più veloce di ECDSA, non soffre di problemi di implementazione legati alla
generazione del nonce, ed e supportato da Node.js, OpenSSL 3.x, e tutti i browser moderni. SSH,
Signal e molte applicazioni di alta sicurezza lo usano come default.
// digital-signatures.ts - Ed25519 e RSA con Node.js crypto
import {
generateKeyPairSync,
createSign,
createVerify,
KeyObject,
} from 'crypto';
// ============================================================
// GENERAZIONE COPPIA DI CHIAVI Ed25519
// ============================================================
export function generateEd25519KeyPair(): { publicKey: string; privateKey: string } {
const { publicKey, privateKey } = generateKeyPairSync('ed25519', {
publicKeyEncoding: {
type: 'spki',
format: 'pem',
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
// In produzione: cifra la chiave privata con una passphrase
// cipher: 'aes-256-cbc',
// passphrase: process.env['KEY_PASSPHRASE'],
},
});
return { publicKey, privateKey };
}
// ============================================================
// FIRMA DIGITALE con Ed25519
// ============================================================
export function signData(data: string, privateKeyPem: string): string {
const signer = createSign('SHA512'); // Ed25519 usa SHA-512 internamente
signer.update(data, 'utf8');
signer.end();
const signature = signer.sign(privateKeyPem);
return signature.toString('base64');
}
// ============================================================
// VERIFICA FIRMA
// ============================================================
export function verifySignature(
data: string,
signature: string,
publicKeyPem: string
): boolean {
const verifier = createVerify('SHA512');
verifier.update(data, 'utf8');
verifier.end();
try {
return verifier.verify(publicKeyPem, Buffer.from(signature, 'base64'));
} catch {
return false;
}
}
// ============================================================
// GENERAZIONE COPPIA RSA-4096 (per sistemi legacy o interoperabilità)
// ============================================================
export function generateRSAKeyPair(): { publicKey: string; privateKey: string } {
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 4096, // 4096 bit nel 2025 per nuovi sistemi RSA
publicExponent: 0x10001, // 65537 - valore standard e sicuro
publicKeyEncoding: {
type: 'spki',
format: 'pem',
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
},
});
return { publicKey, privateKey };
}
// ============================================================
// USO COMPLETO
// ============================================================
const { publicKey, privateKey } = generateEd25519KeyPair();
const payload = JSON.stringify({
userId: 'usr_123',
action: 'transfer',
amount: 1500,
timestamp: Date.now(),
});
const firma = signData(payload, privateKey);
console.log('Firma base64:', firma);
const isValid = verifySignature(payload, firma, publicKey);
console.log('Firma valida:', isValid); // true
// Prova con dati manomessi
const payloadManomesso = payload.replace('1500', '15000');
const isValidTampered = verifySignature(payloadManomesso, firma, publicKey);
console.log('Firma valida su dati manomessi:', isValidTampered); // false
Hashing delle Password: Argon2id, bcrypt e SHA-256
L'hashing delle password e uno degli aspetti più fraintesi della sicurezza applicativa. Il
principio fondamentale e semplice: non devi mai poter recuperare una password dal database.
Se il database viene compromesso, l'attaccante non deve poter risalire alle password originali.
Per questo l'hashing delle password richiede algoritmi specifici, radicalmente diversi da
quelli usati per l'integrita dei dati.
SHA-256 Non e per le Password
SHA-256 e velocissimo (miliardi di operazioni al secondo su GPU), il che e ottimo per
l'integrita dei dati ma catastrofico per le password. Un attaccante con una RTX 4090 può
testare 10+ miliardi di SHA-256/secondo, rendendo un intero dizionario di password comuni
craccabile in pochi secondi. Per le password servono algoritmi
memory-hard e computazionalmente costosi.
SHA-256: per checksums, HMAC, derivazione di token. MAI per password.
MD5, SHA-1: deprecati anche per checksums. Non usarli in nessun nuovo codice.
bcrypt: sicuro, battle-tested, usalo se già integrato nel tuo sistema.
Argon2id: gold standard OWASP 2025 per nuove applicazioni.
OWASP raccomanda Argon2id come prima scelta per le nuove applicazioni. E il
vincitore del Password Hashing Competition (2015) ed e progettato per essere resistente agli
attacchi GPU e ASIC grazie alla sua natura memory-hard: richiede una quantità significativa
di RAM per calcolare il hash, rendendo gli attacchi paralleli molto più costosi.
// password-hashing.ts - Argon2id e bcrypt per Node.js
import argon2 from 'argon2';
import bcrypt from 'bcrypt';
import { createHmac, timingSafeEqual } from 'crypto';
// ============================================================
// ARGON2ID - Raccomandato OWASP 2025 per nuove applicazioni
// ============================================================
// Parametri OWASP minimi per Argon2id
const ARGON2_OPTIONS: argon2.Options = {
type: argon2.argon2id, // id = combinazione di i (data-independent) e d (data-dependent)
memoryCost: 19456, // 19 MiB - minimo OWASP
timeCost: 2, // 2 iterazioni - minimo OWASP
parallelism: 1, // 1 thread
// Per sistemi ad alta sicurezza (es. admin, banche):
// memoryCost: 65536, // 64 MiB
// timeCost: 3,
};
export async function hashPasswordArgon2(password: string): Promise<string> {
// argon2 gestisce automaticamente il salt casuale
return argon2.hash(password, ARGON2_OPTIONS);
}
export async function verifyPasswordArgon2(
hashedPassword: string,
candidatePassword: string
): Promise<boolean> {
try {
return await argon2.verify(hashedPassword, candidatePassword);
} catch {
return false;
}
}
// Controlla se il hash necessità di essere aggiornato (rehashing)
export async function needsRehash(hash: string): Promise<boolean> {
return argon2.needsRehash(hash, ARGON2_OPTIONS);
}
// ============================================================
// BCRYPT - Per sistemi esistenti (cost factor >= 12)
// ============================================================
const BCRYPT_ROUNDS = 12; // Minimo OWASP; 14 per sistemi critici
export async function hashPasswordBcrypt(password: string): Promise<string> {
// bcrypt tronca a 72 byte - usare pre-hashing per password lunghe
if (password.length > 72) {
// Pre-hash con SHA-256 per password lunghe (prevenire DoS)
const preHashed = createHmac('sha256', process.env['BCRYPT_PEPPER']!)
.update(password)
.digest('hex');
return bcrypt.hash(preHashed, BCRYPT_ROUNDS);
}
return bcrypt.hash(password, BCRYPT_ROUNDS);
}
export async function verifyPasswordBcrypt(
hashedPassword: string,
candidatePassword: string
): Promise<boolean> {
try {
// bcrypt.compare usa timing-safe comparison internamente
return await bcrypt.compare(candidatePassword, hashedPassword);
} catch {
return false;
}
}
// ============================================================
// SHA-256 per HMAC e verifiche di integrita (NON password)
// ============================================================
export function computeHMAC(data: string, secret: string): string {
return createHmac('sha256', secret).update(data, 'utf8').digest('hex');
}
export function verifyHMAC(data: string, secret: string, expected: string): boolean {
const computed = createHmac('sha256', secret).update(data, 'utf8').digest('hex');
// timingSafeEqual previene timing attacks
const computedBuffer = Buffer.from(computed, 'hex');
const expectedBuffer = Buffer.from(expected, 'hex');
if (computedBuffer.length !== expectedBuffer.length) return false;
return timingSafeEqual(computedBuffer, expectedBuffer);
}
// ============================================================
// PATTERN DI AUTENTICAZIONE COMPLETO
// ============================================================
async function authenticationFlow() {
// Registrazione
const password = 'MyS3cur3P4ss!';
const hash = await hashPasswordArgon2(password);
// Salva hash nel DB: utente.passwordHash = hash
// Login
const isValid = await verifyPasswordArgon2(hash, password);
console.log('Login valido:', isValid); // true
// Rehashing automatico (aggiorna parametri senza invalidare le sessioni)
if (await needsRehash(hash)) {
const newHash = await hashPasswordArgon2(password);
// Aggiorna il DB con newHash
console.log('Password rehashed con parametri aggiornati');
}
// Timing-safe per ID utente (previene user enumeration timing attacks)
const userId1 = Buffer.from('user_abc123');
const userId2 = Buffer.from('user_abc123');
console.log('IDs uguali (safe):', timingSafeEqual(userId1, userId2)); // true
}
TLS 1.3: Configurazione Sicura in Node.js
TLS 1.3 (RFC 8446, 2018) e il protocollo di trasporto sicuro attuale. Rispetto a TLS 1.2
introduce miglioramenti significativi: handshake più veloce (1-RTT invece di 2-RTT, con
supporto 0-RTT per sessioni riprese), cipher suite semplificate e più sicure (elimina
tutti gli algoritmi deboli inclusi RC4, DES, MD5 per MAC, RSA key exchange), e Perfect
Forward Secrecy obbligatoria tramite ECDHE.
Le cipher suite di TLS 1.3 sono solo 5 (vs decine in TLS 1.2) e tutte usano AEAD
(Authenticated Encryption with Associated Data):
TLS_AES_256_GCM_SHA384 - Raccomandato
TLS_AES_128_GCM_SHA256 - Accettabile
TLS_CHACHA20_POLY1305_SHA256 - Preferibile su dispositivi senza AES hardware
// tls-config.ts - Configurazione TLS 1.3 sicura per Node.js HTTPS
import https from 'https';
import fs from 'fs';
import { TLSSocket } from 'tls';
// ============================================================
// CONFIGURAZIONE HTTPS SERVER CON TLS 1.3
// ============================================================
const tlsOptions: https.ServerOptions = {
// Certificato e chiave privata (da file o KMS)
cert: fs.readFileSync('/etc/ssl/certs/server.crt'),
key: fs.readFileSync('/etc/ssl/private/server.key'),
// Forza TLS 1.3 (disabilita versioni precedenti)
minVersion: 'TLSv1.3',
maxVersion: 'TLSv1.3',
// Cipher suite TLS 1.3 (ordine indica preferenza)
ciphers: [
'TLS_AES_256_GCM_SHA384',
'TLS_CHACHA20_POLY1305_SHA256',
'TLS_AES_128_GCM_SHA256',
].join(':'),
// HSTS header viene aggiunto dall'app, non da TLS
// (vedi Express middleware sotto)
// Session tickets: disabilita per PFS rigorosa
// (TLS 1.3 ha PFS by default, ma session tickets possono ridurla)
sessionTimeout: 300, // 5 minuti max
// Mutual TLS (mTLS) - opzionale per API interne
// requestCert: true,
// rejectUnauthorized: true,
// ca: fs.readFileSync('/etc/ssl/ca.crt'),
};
// ============================================================
// EXPRESS + HTTPS + SECURITY HEADERS
// ============================================================
import express from 'express';
import helmet from 'helmet';
const app = express();
// Helmet per security headers (include HSTS, CSP, etc.)
app.use(helmet({
hsts: {
maxAge: 31536000, // 1 anno
includeSubDomains: true,
preload: true, // Includi in HSTS preload list
},
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
upgradeInsecureRequests: [], // Forza HTTPS per risorse HTTP
},
},
}));
const server = https.createServer(tlsOptions, app);
server.listen(443, () => console.log('HTTPS con TLS 1.3 attivo'));
// ============================================================
// VERIFICA VERSIONE TLS PER OGNI RICHIESTA
// ============================================================
app.use((req, res, next) => {
const socket = req.socket as TLSSocket;
const tlsVersion = socket.getProtocol?.();
if (tlsVersion !== 'TLSv1.3') {
// Log per audit (non esporre info al client)
console.warn(`Connessione con #123;tlsVersion} da #123;req.ip} - rifiutata`);
return res.status(426).json({
error: 'TLS 1.3 richiesto',
});
}
next();
});
// ============================================================
// CERTIFICATI: rotazione automatica con Let's Encrypt + Certbot
// ============================================================
// Comando certbot per rinnovo automatico:
// certbot certonly --webroot -w /var/www/html -d example.com
// crontab: 0 2 * * 1 certbot renew --quiet --post-hook "systemctl reload nginx"
Key Management: il Punto Debole Più Comune
Il sistema crittografico più robusto del mondo e inutile se le chiavi sono gestite male.
Il vettore di attacco più frequente non e "rompere AES-256" ma trovare la chiave AES-256
nell'environment file committato su GitHub, nel codice sorgente, o nei log di sistema.
Nel 2024, GitHub Secret Scanning ha rilevato oltre 39 milioni di segreti esposti in repository
pubblici. La maggior parte erano chiavi API, ma una quota significativa erano chiavi crittografiche.
Il danno derivante dall'esposizione di una chiave simmetrica e catastrofico: tutti i dati
cifrati con quella chiave sono compromessi.
Gerarchia di Sicurezza per la Gestione delle Chiavi
Livello 1 - Minimo accettabile: variabili d'ambiente (non in .env
committato su git). Usa .env.local e aggiungi *.env* al
.gitignore.
Livello 2 - Produzione piccole app: servizi secrets manager come
HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager.
Livello 3 - Alta sicurezza: Hardware Security Module (HSM) o
servizi KMS (Key Management Service) che eseguono le operazioni crittografiche
dentro hardware tamper-proof senza esporre mai la chiave in chiaro.
// key-management.ts - Pattern sicuri per gestire chiavi crittografiche
// ============================================================
// CARICAMENTO SICURO DELLE CHIAVI (da ambiente, mai hardcoded)
// ============================================================
function loadCryptoKeys(): { aesKey: string; hmacSecret: string } {
const aesKey = process.env['AES_256_KEY'];
const hmacSecret = process.env['HMAC_SECRET'];
if (!aesKey || aesKey.length !== 64) {
throw new Error('AES_256_KEY mancante o non valida (deve essere 64 hex chars = 32 byte)');
}
if (!hmacSecret || hmacSecret.length < 32) {
throw new Error('HMAC_SECRET mancante o troppo corta (minimo 32 caratteri)');
}
// Verifica che la chiave sia effettivamente hex valida
if (!/^[0-9a-fA-F]{64}$/.test(aesKey)) {
throw new Error('AES_256_KEY non e una stringa hex valida');
}
return { aesKey, hmacSecret };
}
// ============================================================
// GENERAZIONE CHIAVE SICURA (per setup iniziale)
// ============================================================
import { randomBytes } from 'crypto';
export function generateSecureKey(lengthBytes: number = 32): string {
return randomBytes(lengthBytes).toString('hex');
}
// Per generare una nuova chiave AES-256:
// node -e "const {randomBytes}=require('crypto'); console.log(randomBytes(32).toString('hex'))"
// Output: e6f7a8b9c0d1e2f3... (64 char hex)
// Salva il valore in: AWS Secrets Manager / Azure Key Vault / .env.local
// ============================================================
// KEY ROTATION PATTERN
// ============================================================
interface KeyVersion {
version: number;
key: string;
activeFrom: Date;
activeTo?: Date; // undefined = chiave attiva
}
class KeyRotationManager {
private keys: Map<number, KeyVersion> = new Map();
private currentVersion: number;
constructor(keys: KeyVersion[]) {
keys.forEach(k => this.keys.set(k.version, k));
// La versione più recente con activeTo undefined e quella attiva
this.currentVersion = Math.max(...keys.map(k => k.version));
}
// Cifra sempre con la chiave più recente
encrypt(data: string): EncryptedPayload {
const currentKey = this.keys.get(this.currentVersion)!;
const encrypted = encrypt(data, currentKey.key);
return {
...encrypted,
keyVersion: this.currentVersion, // Includi versione nel payload
};
}
// Decifra usando la versione specificata nel payload (supporta old keys)
decrypt(payload: EncryptedPayload): string {
const key = this.keys.get(payload.keyVersion);
if (!key) {
throw new Error(`Versione chiave #123;payload.keyVersion} non trovata`);
}
return decrypt(payload, key.key);
}
}
interface EncryptedPayload {
iv: string;
ciphertext: string;
authTag: string;
keyVersion: number;
}
// ============================================================
// INTEGRAZIONE CON AWS SECRETS MANAGER (esempio)
// ============================================================
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
async function getKeyFromAWS(secretName: string): Promise<string> {
const client = new SecretsManagerClient({ region: 'eu-west-1' });
const response = await client.send(
new GetSecretValueCommand({ SecretId: secretName })
);
if (!response.SecretString) {
throw new Error(`Secret #123;secretName} non trovato`);
}
return response.SecretString;
}
Web Crypto API: Crittografia nel Browser
La Web Crypto API (disponibile in tutti i browser moderni e in Node.js 15+) porta la
crittografia direttamente nel browser senza dipendenze da librerie JavaScript esterne.
E basata su operazioni asincrone, usa memoria non esposta a JavaScript (le chiavi
CryptoKey non sono estraibili per default), ed e implementata a livello
nativo dal browser per performance ottimali.
Un caso d'uso tipico e la cifratura end-to-end nel browser: i dati vengono cifrati
prima di essere inviati al server, che non ha mai accesso ai dati in chiaro. Questo
pattern e usato da applicazioni come password manager, note sicure, e client di
messaggistica crittografata.
// web-crypto.service.ts - Angular service per cifratura nel browser
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class WebCryptoService {
private readonly subtle = window.crypto.subtle;
// ============================================================
// GENERAZIONE CHIAVE AES-256-GCM nel browser
// ============================================================
async generateAESKey(extractable = false): Promise<CryptoKey> {
return this.subtle.generateKey(
{
name: 'AES-GCM',
length: 256,
},
extractable, // false = chiave non estraibile (più sicuro)
['encrypt', 'decrypt']
);
}
// ============================================================
// CIFRATURA AES-256-GCM nel browser
// ============================================================
async encrypt(
data: string,
key: CryptoKey
): Promise<{ iv: string; ciphertext: string }> {
const encoder = new TextEncoder();
const dataBuffer = encoder.encode(data);
// IV casuale di 12 byte (96 bit) - CRITICO: unico per ogni cifratura
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const cipherBuffer = await this.subtle.encrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
key,
dataBuffer
);
return {
iv: this.bufferToBase64(iv.buffer),
ciphertext: this.bufferToBase64(cipherBuffer),
// In AES-GCM del browser, il tag e concatenato al ciphertext
};
}
// ============================================================
// DECIFRATURA AES-256-GCM nel browser
// ============================================================
async decrypt(
encryptedData: { iv: string; ciphertext: string },
key: CryptoKey
): Promise<string> {
const iv = this.base64ToBuffer(encryptedData.iv);
const cipherBuffer = this.base64ToBuffer(encryptedData.ciphertext);
const decryptedBuffer = await this.subtle.decrypt(
{ name: 'AES-GCM', iv, tagLength: 128 },
key,
cipherBuffer
);
const decoder = new TextDecoder();
return decoder.decode(decryptedBuffer);
}
// ============================================================
// DERIVAZIONE CHIAVE DA PASSWORD (PBKDF2)
// Per chiavi derivate da password utente
// ============================================================
async deriveKeyFromPassword(
password: string,
salt: Uint8Array,
iterations = 310000 // OWASP minimo 2025 per PBKDF2-SHA-256
): Promise<CryptoKey> {
const encoder = new TextEncoder();
// Importa la password come material grezzo
const keyMaterial = await this.subtle.importKey(
'raw',
encoder.encode(password),
'PBKDF2',
false,
['deriveKey']
);
// Deriva la chiave AES
return this.subtle.deriveKey(
{
name: 'PBKDF2',
salt,
iterations,
hash: 'SHA-256',
},
keyMaterial,
{ name: 'AES-GCM', length: 256 },
false, // Non estraibile
['encrypt', 'decrypt']
);
}
// ============================================================
// FIRMA DIGITALE con ECDSA P-256 nel browser
// ============================================================
async generateSigningKeyPair(): Promise<CryptoKeyPair> {
return this.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-256',
},
false,
['sign', 'verify']
);
}
async sign(data: string, privateKey: CryptoKey): Promise<string> {
const encoder = new TextEncoder();
const signature = await this.subtle.sign(
{ name: 'ECDSA', hash: { name: 'SHA-256' } },
privateKey,
encoder.encode(data)
);
return this.bufferToBase64(signature);
}
// ============================================================
// UTILS
// ============================================================
private bufferToBase64(buffer: ArrayBuffer): string {
const bytes = new Uint8Array(buffer);
return btoa(String.fromCharCode(...bytes));
}
private base64ToBuffer(base64: string): ArrayBuffer {
const binaryStr = atob(base64);
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
return bytes.buffer;
}
}
Crittografia Post-Quantum: Prepararsi al 2030
I computer quantistici non sono ancora una minaccia pratica per la crittografia odierna,
ma il NIST ha pubblicato nel 2024 i primi standard post-quantum definitivi (FIPS 203, 204, 205)
proprio perchè i tempi di migrazione sono lunghi e il rischio "harvest now, decrypt later"
e reale. Un attore malevolo che raccoglie oggi comunicazioni cifrate con RSA potrebbe
decifrarle in futuro con un computer quantistico sufficientemente potente.
Standard NIST Post-Quantum (Agosto 2024)
FIPS 203 - ML-KEM (Kyber): Key Encapsulation Mechanism per scambio
di chiavi. Sostituisce ECDH e RSA key exchange. Raccomandato per TLS 1.3 ibrido
(X25519+MLKEM-768).
FIPS 204 - ML-DSA (Dilithium): Firma digitale post-quantum. Sostituisce
ECDSA e RSA-PSS. Chiavi più grandi ma sicure contro algoritmi Shor.
FIPS 205 - SLH-DSA (SPHINCS+): Firma digitale basata su hash, più
conservativa. Utile come backup algoritmico.
Strategia raccomandata 2025-2030: approccio ibrido. Usa X25519 +
ML-KEM-768 per TLS (sicurezza classica + post-quantum simultaneamente).
Per i developer, la preparazione alla post-quantum cryptography oggi significa
principalmente:
Crypto agility: progettare i sistemi per poter cambiare algoritmo
senza riscrivere tutto. Non hardcodare "AES-256" o "RSA" nei database; usa un campo
algorithm_version.
Inventario crittografico: sapere dove e come vengono usati RSA/ECC
nella tua applicazione. Il primo passo e la visibilità.
TLS ibrido: abilitare X25519+MLKEM-768 su server TLS che supportano
OpenSSL 3.5+ o BoringSSL (Chrome lo usa già dal 2023).
Nessuna migrazione urgente per dati at-rest con chiavi AES-256 simmetriche:
AES-256 e già resistente ai computer quantistici (Grover riduce la sicurezza effettiva
a 128 bit, ancora molto sicuro).
// crypto-agility.ts - Pattern per crypto agility
// Permette di aggiornare gli algoritmi senza riscrivere l'intera app
type AlgorithmVersion = 'v1' | 'v2' | 'v3';
interface CryptoStrategy {
version: AlgorithmVersion;
encrypt: (data: string, key: string) => Promise<string>;
decrypt: (data: string, key: string) => Promise<string>;
description: string;
}
// Registro degli algoritmi supportati
const cryptoStrategies: Record<AlgorithmVersion, CryptoStrategy> = {
v1: {
version: 'v1',
description: 'AES-256-CBC (legacy, no AEAD)',
encrypt: async (data, key) => encryptAESCBC(data, key), // vecchio
decrypt: async (data, key) => decryptAESCBC(data, key),
},
v2: {
version: 'v2',
description: 'AES-256-GCM (current standard)',
encrypt: async (data, key) => {
const result = encrypt(data, key);
return JSON.stringify(result);
},
decrypt: async (data, key) => decrypt(JSON.parse(data), key),
},
v3: {
version: 'v3',
description: 'AES-256-GCM + ML-KEM key encapsulation (post-quantum ready)',
encrypt: async (data, key) => encryptWithPQC(data, key),
decrypt: async (data, key) => decryptWithPQC(data, key),
},
};
const CURRENT_VERSION: AlgorithmVersion = 'v2';
// Struttura dati nel DB include sempre la versione dell'algoritmo
interface StoredSecret {
data: string;
algorithmVersion: AlgorithmVersion;
encryptedAt: string; // ISO date
}
async function storeEncrypted(plaintext: string, key: string): Promise<StoredSecret> {
const strategy = cryptoStrategies[CURRENT_VERSION];
const encryptedData = await strategy.encrypt(plaintext, key);
return {
data: encryptedData,
algorithmVersion: CURRENT_VERSION,
encryptedAt: new Date().toISOString(),
};
}
// La decifratura usa sempre la versione registrata nel record
async function retrieveDecrypted(stored: StoredSecret, key: string): Promise<string> {
const strategy = cryptoStrategies[stored.algorithmVersion];
if (!strategy) {
throw new Error(`Versione algoritmo '#123;stored.algorithmVersion}' non supportata`);
}
return strategy.decrypt(stored.data, key);
}
// Migrazione automatica: aggiorna al formato corrente al prossimo accesso
async function migrateIfNeeded(
stored: StoredSecret,
key: string
): Promise<StoredSecret | null> {
if (stored.algorithmVersion === CURRENT_VERSION) return null; // Già aggiornato
// Decifra con vecchio algoritmo, ri-cifra con il nuovo
const plaintext = await retrieveDecrypted(stored, key);
const updated = await storeEncrypted(plaintext, key);
console.log(`Migrato da #123;stored.algorithmVersion} a #123;CURRENT_VERSION}`);
return updated;
}
Checklist Crittografia per Applicazioni Angular
Le applicazioni Angular hanno requisiti crittografici specifici. Il browser e un ambiente
non fidato: qualsiasi segreto nel codice JavaScript e accessibile a chiunque apra gli
strumenti di sviluppo. Questo cambia radicalmente cosa ha senso fare lato client.
Regole d'Oro per la Crittografia in Angular
Mai chiavi crittografiche hardcoded nel codice TypeScript/JavaScript.
Le chiavi simmetriche devono rimanere sul server.
Usa HTTPS sempre - configura Content-Security-Policy con
upgrade-insecure-requests e HSTS. Non gestire mai dati sensibili
su HTTP anche in sviluppo.
Per la cifratura E2E nel browser: usa Web Crypto API con chiavi
derivate dalla password dell'utente (PBKDF2/Argon2 wasm). La chiave non lascia
mai il browser.
Token JWT: verifica firma, scadenza (exp), issuer
(iss) e audience (aud). Non decodificare senza verificare.
localStorage vs sessionStorage: entrambi accessibili da JS. Non
salvare dati ultra-sensibili (chiavi private, token di lunga durata) in storage
accessibili via XSS. Preferisci cookie HttpOnly per i token di sessione.
Randomness: usa sempre window.crypto.getRandomValues()
per dati casuali crittograficamente sicuri, mai Math.random().
// crypto-checklist.angular.ts - Pattern sicuri specifici per Angular
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
// ============================================================
// 1. TOKEN JWT: VERIFICA COMPLETA (lato client = solo decodifica)
// ============================================================
interface JWTPayload {
sub: string;
iss: string;
aud: string | string[];
exp: number;
iat: number;
[key: string]: unknown;
}
function decodeJWT(token: string): JWTPayload {
// Decodifica il payload (non verifica la firma - questa avviene sul server)
const parts = token.split('.');
if (parts.length !== 3) throw new Error('Token JWT non valido');
try {
const payload = JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
return payload as JWTPayload;
} catch {
throw new Error('Payload JWT non decodificabile');
}
}
function isTokenExpired(token: string): boolean {
try {
const payload = decodeJWT(token);
const nowSeconds = Math.floor(Date.now() / 1000);
return payload.exp <= nowSeconds;
} catch {
return true; // Se non decodificabile, trattalo come scaduto
}
}
// ============================================================
// 2. RANDOMNESS SICURA per CSRF token, nonce, ID temporanei
// ============================================================
function generateSecureToken(lengthBytes = 32): string {
const array = new Uint8Array(lengthBytes);
window.crypto.getRandomValues(array); // Crittograficamente sicuro
return Array.from(array, b => b.toString(16).padStart(2, '0')).join('');
}
// SBAGLIATO: Math.random() non e crittograficamente sicuro
// const insecureToken = Math.random().toString(36); // MAI usare per token di sicurezza
// ============================================================
// 3. HASHING SHA-256 nel browser per integrità (non password)
// ============================================================
async function sha256Browser(data: string): Promise<string> {
const encoder = new TextEncoder();
const hashBuffer = await window.crypto.subtle.digest('SHA-256', encoder.encode(data));
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// Uso: verifica integrita file scaricati, PKCE code challenge
async function computePKCEChallenge(verifier: string): Promise<string> {
const hash = await window.crypto.subtle.digest(
'SHA-256',
new TextEncoder().encode(verifier)
);
// Base64URL encoding (senza padding)
return btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
// ============================================================
// 4. HTTPCLIENT: intercettore per headers di sicurezza
// ============================================================
@Injectable()
export class SecurityHeadersInterceptor {
intercept(req: any, next: any) {
// Aggiungi header di sicurezza a tutte le richieste autenticate
const secureReq = req.clone({
headers: req.headers
.set('X-Requested-With', 'XMLHttpRequest') // Mitiga alcuni CSRF
.set('X-Content-Type-Options', 'nosniff'), // Lato client hint
});
return next.handle(secureReq);
}
}
// ============================================================
// 5. CIFRATURA LOCALE dati sensibili prima di sessionStorage
// ============================================================
@Injectable({ providedIn: 'root' })
export class SecureStorageService {
private sessionKey: CryptoKey | null = null;
// Inizializza con chiave di sessione in memoria (non in storage)
async initialize(): Promise<void> {
this.sessionKey = await window.crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
false, // Non estraibile
['encrypt', 'decrypt']
);
// La chiave e in memoria: sparisce al refresh della pagina
}
async setItem(key: string, value: string): Promise<void> {
if (!this.sessionKey) await this.initialize();
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encrypted = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
this.sessionKey!,
new TextEncoder().encode(value)
);
const stored = JSON.stringify({
iv: Array.from(iv),
data: Array.from(new Uint8Array(encrypted)),
});
sessionStorage.setItem(key, stored);
}
}
I 10 Errori Crittografici Più Comuni
La crittografia e un campo dove le meta-conoscenze sono più importanti dei dettagli tecnici.
Sapere cosa NON fare spesso vale più di sapere come usare ogni API. Ecco gli errori che
compaiono sistematicamente nei code review di sicurezza:
Errore
Impatto
Soluzione
MD5/SHA-1 per hash di password
Critico - cracckabile in secondi
Argon2id o bcrypt con cost >= 12
AES-ECB (no IV)
Alto - pattern visibili nel ciphertext
AES-256-GCM con IV casuale
IV fisso o incrementale
Critico - annulla la sicurezza GCM
randomBytes(12) per ogni cifratura
Chiavi hardcoded nel codice
Critico - espone tutti i dati cifrati
AWS/Azure KMS, variabili d'ambiente
RSA-PKCS1v1.5 per cifratura
Alto - vulnerabile a Bleichenbacher
RSA-OAEP o ECDH per key exchange
JWT senza verifica firma
Critico - privilege escalation
Verifica sempre la firma sul server
Confronto di hash con ==
Medio - timing attack
timingSafeEqual() di Node.js crypto
Math.random() per token
Alto - token prevedibili
crypto.randomBytes() o getRandomValues()
TLS 1.0/1.1 abilitati
Alto - BEAST, POODLE, CRIME attack
minVersion: TLSv1.3
Nessuna validazione certificato
Critico - MITM attack
Mai NODE_TLS_REJECT_UNAUTHORIZED=0 in prod
Conclusioni
La crittografia pratica per developer non richiede di essere crittografi: richiede di conoscere
i pattern giusti, evitare gli anti-pattern noti, e scegliere gli strumenti appropriati per ogni
caso d'uso. I principi fondamentali si riassumono in pochi punti:
AES-256-GCM per cifratura simmetrica autenticata: IV casuale di 12 byte,
authentication tag di 128 bit, chiave mai nel codice.
Ed25519 o ECDSA P-256 per firme digitali: preferisci ECC su RSA per
nuovi sistemi, RSA-4096 solo per compatibilità legacy.
Argon2id per password hashing (parametri OWASP: 19 MiB, 2 iterazioni),
bcrypt con cost factor 12+ per sistemi esistenti. Mai SHA-256 o MD5 per le password.
TLS 1.3 come minimo per tutti i servizi in produzione, con HSTS e
cipher suite moderne. Disabilita TLS 1.0/1.1/1.2 se possibile.
Key management tramite KMS o secret manager: la chiave più robusta
diventa inutile se e esposta nel repository.
Crypto agility nei nuovi sistemi: progetta per poter migrare algoritmi
senza riscrivere tutto, in preparazione alla transizione post-quantum entro il 2030.
La crittografia non e una funzionalità che si aggiunge alla fine: e una decisione
architetturale che va presa all'inizio. Ogni campo sensibile nel database, ogni API
che trasmette dati personali, ogni token di autenticazione richiede una scelta
deliberata sull'algoritmo, la gestione delle chiavi e la rotazione.
Continua con la Serie: Sicurezza Web per Developer
Articolo precedente: Supply Chain Security: npm audit e SBOM - come proteggere la catena di dipendenze
Prossimo articolo: DevSecOps per Developer: SAST, DAST nel CI/CD - integrare la sicurezza nella pipeline
Collegato a: API Security: OAuth 2.1, JWT e Rate Limiting - JWT best practices in dettaglio
Vedi anche: la serie DevOps Frontend per la configurazione del deployment sicuro