Usa query parametrizzate per ogni accesso al database
Invia al database separatamente la struttura della query e i valori forniti dall'utente così che l'input non possa mai cambiare il significato dell'SQL. È la difesa definitiva contro la SQL injection e funziona allo stesso modo tra driver e ORM che fanno binding dei parametri.
- Fai: usa un prepared statement come
SELECT * FROM users WHERE email = ?e fai binding del valore. - Non fare: costruire SQL per concatenazione come
"... WHERE email = '" + email + "'", anche con escaping manuale.
Codifica l'output in base al suo contesto, non solo in ingresso
Applica una codifica consapevole del contesto nel punto in cui i dati vengono resi - corpo HTML, attributo HTML, JavaScript, URL e CSS richiedono ciascuno un escaping diverso. Sanitizzare solo in ingresso è insufficiente perché lo stesso valore può essere sicuro in un contesto e pericoloso in un altro.
- Fai: affidati al binding automatico del tuo framework (per esempio
{{ value }}in Angular o il testo JSX) e codifica per contesto. - Non fare: assegnare dati non fidati a sink grezzi come
element.innerHTMLsenza prima sanitizzarli.
Imponi l'autorizzazione sul server per ogni richiesta
Riverifica sul server che il principal autenticato sia abilitato a eseguire l'azione specifica sull'oggetto specifico, a ogni endpoint protetto. I controlli lato client e gli elementi UI nascosti sono solo comodità e non offrono alcuna sicurezza.
- Fai: verifica la proprietà della risorsa lato server, ad esempio conferma
order.userId == currentUser.idprima di restituire/orders/{id}. - Non fare: fidarti di un ruolo, un tenant ID o un object ID forniti dal client, o assumere che "loggato" significhi "autorizzato".
Valida e metti in allow-list tutto l'input non fidato sul server
Tratta ogni input (body, query, header, file) come ostile e validalo contro un'allow-list rigorosa di tipi, range e formati attesi prima dell'uso. Mettere in allow-list ciò che è permesso è molto più robusto che tentare di bloccare pattern noti come dannosi.
- Fai: valida con uno schema e rifiuta tutto ciò che non corrisponde, ad esempio un enum di
["asc","desc"]per un parametro di ordinamento. - Non fare: affidarti alla sola validazione lato client, o usare deny-list che gli attaccanti eludono banalmente con codifiche nuove.
Conserva le password con un hash lento, con salt e memory-hard
Usa una funzione di hashing per password costruita allo scopo così che una violazione del database non produca credenziali utilizzabili. Il salt unico per password vanifica le rainbow table e un work factor elevato rende costoso il cracking offline.
- Fai: fai l'hash con
Argon2id(oppure bcrypt/scrypt) con un cost factor calibrato e un salt unico per password. - Non fare: conservare in chiaro, usare hash veloci come MD5 o SHA-256 da soli, o riutilizzare un unico salt globale.
Imposta Secure, HttpOnly e SameSite sui cookie di sessione
Rafforza il cookie di sessione così che non possa fuoriuscire su HTTP semplice, non possa essere letto da JavaScript e non venga allegato sulle richieste cross-site. Questi tre attributi insieme attenuano il furto di sessione via XSS e CSRF.
- Fai: emetti
Set-Cookie: sid=...; Secure; HttpOnly; SameSite=Lax; Path=/e rigenera l'ID al login. - Non fare: esporre l'ID di sessione a JavaScript, inviarlo su HTTP, o mantenere lo stesso ID attraverso un cambio di privilegio.
Imponi HTTPS ovunque e invia HSTS
Servi tutto il traffico su TLS 1.3 con certificati validi e aggiungi l'header Strict-Transport-Security così che i browser rifiutino il downgrade a HTTP. Questo previene intercettazione, manomissione e attacchi di SSL-stripping.
- Fai: imposta
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloade reindirizza HTTP a HTTPS. - Non fare: mescolare contenuti HTTP e HTTPS, o affidarti al solo redirect senza HSTS, lasciando la prima richiesta non protetta.
Distribuisci una Content Security Policy rigorosa
Usa uno script-src basato su nonce o hash così che gli script inline iniettati senza il nonce corretto non possano eseguire, rendendo inerti molti payload XSS. La CSP è una potente difesa in profondità sopra l'output encoding.
- Fai: invia
Content-Security-Policy: script-src 'self' 'nonce-.'; object-src 'none'; base-uri 'self' - Non fare: indebolirla con
'unsafe-inline'o un*generico, che di fatto disattiva la protezione.
Richiedi MFA resistente al phishing dove conta
Aggiungi un secondo fattore all'autenticazione e preferisci WebAuthn/FIDO2 (passkey, chiavi di sicurezza) per gli account ad alto valore, perché la credenziale è legata all'origine e non può essere sottratta via phishing o rigiocata. Questo blocca la maggior parte dei furti di account anche quando una password trapela.
- Fai: offri passkey/WebAuthn e ripiega su app authenticator TOTP dove necessario.
- Non fare: affidarti ai codici usa-e-getta via SMS come unico secondo fattore, dato che sono vulnerabili al SIM-swapping.
Applica il privilegio minimo a ogni account e token
Concedi a ogni utente, servizio, account di database e token API solo i permessi strettamente necessari, e rendi le credenziali a breve durata. Questo contiene il danno quando un singolo componente viene compromesso.
- Fai: dai all'app un utente DB limitato alle sue tabelle ed emetti token API con scope ristretto e in scadenza.
- Non fare: connetterti come superuser del database, o distribuire token ampi, a lunga durata e con tutti gli scope "per semplicità".
Applica rate limiting su autenticazione ed endpoint sensibili
Limita il numero di richieste per client e per account in una finestra temporale per attenuare forza bruta, credential stuffing e abuso. Combina il throttling con chiare risposte 429 Too Many Requests e backoff.
- Fai: applica un limite token-bucket per account e IP a
/login, e aggiungi lockout/backoff temporaneo dopo fallimenti ripetuti. - Non fare: lasciare illimitati gli endpoint di login, password-reset e token, permettendo tentativi infiniti.
Proteggi le richieste in uscita contro l'SSRF
Valida, metti in allow-list e vincola ogni richiesta lato server il cui URL sia influenzato dall'input dell'utente, e blocca i range di indirizzi interni. Questo impedisce agli attaccanti di raggiungere servizi interni o l'endpoint di metadati cloud.
- Fai: metti in allow-list host/schemi permessi, blocca i range link-local e privati e disabilita il follow dei redirect.
- Non fare: scaricare un URL arbitrario fornito dall'utente direttamente, esponendo
169.254.169.254e i servizi interni.
Mantieni le dipendenze fissate, scansionate e inventariate
Gestisci il codice di terze parti come parte della tua superficie d'attacco: fissa le versioni, scansiona per vulnerabilità note e mantieni un SBOM così da reagire in fretta alle nuove divulgazioni. Per gli script di terze parti caricati nel browser, verificane l'integrità.
- Fai: committa un lockfile, esegui la scansione delle dipendenze in CI e aggiungi
integrity="sha384-..."(SRI) ai tagesterni. - Non fare: lasciare versioni fluttuanti non fissate con
^/latest, o caricare script di terze parti da un CDN senza controllo di integrità.
Fallisci in modo sicuro e non rivelare dettagli sensibili negli errori
Progetta la gestione degli errori così che i fallimenti neghino l'accesso per default e la risposta non riveli nulla di utile a un attaccante. Logga il contesto dettagliato lato server, ma restituisci al client un messaggio generico.
- Fai: cattura le eccezioni, logga il dettaglio completo internamente e restituisci un generico
500o un preciso ma non rivelatore403. - Non fare: esporre stack trace, errori SQL, hostname interni o versioni di librerie nelle risposte HTTP.