Tredici regole per lo sviluppo di applicazioni Java sicure

La sicurezza è uno degli aspetti più complessi, ampi e importanti dello sviluppo del software. Anche la sicurezza del software viene spesso trascurata o semplificata al massimo a pochi piccoli aggiustamenti alla fine del ciclo di sviluppo. Possiamo vedere i risultati nell'elenco annuale delle principali violazioni della sicurezza dei dati, che nel 2019 ammontava a oltre 3 miliardi di record esposti. Se può succedere a Capital One, può succedere a te.

La buona notizia è che Java è una piattaforma di sviluppo di lunga data con molte funzionalità di sicurezza integrate. Il pacchetto Java Security è stato sottoposto a test intensivi di battaglia e viene spesso aggiornato per nuove vulnerabilità di sicurezza. La più recente API di sicurezza Java EE, rilasciata a settembre 2017, risolve le vulnerabilità nelle architetture cloud e di microservizi. L'ecosistema Java include anche un'ampia gamma di strumenti per la profilazione e la segnalazione di problemi di sicurezza. 

Ma anche con una solida piattaforma di sviluppo, è importante rimanere vigili. Lo sviluppo di applicazioni è un'impresa complessa e le vulnerabilità possono nascondersi nel rumore di fondo. Dovresti pensare alla sicurezza in ogni fase dello sviluppo dell'applicazione, dalle funzionalità del linguaggio a livello di classe all'autorizzazione dell'endpoint API.

Le seguenti regole di base offrono una buona base per la creazione di applicazioni Java più sicure.

Regola di sicurezza Java n. 1: scrivi codice Java pulito e potente

Le vulnerabilità amano nascondersi nella complessità, quindi mantieni il tuo codice il più semplice possibile senza sacrificare la funzionalità. L'utilizzo di principi di progettazione comprovati come DRY (non ripetere te stesso) ti aiuterà a scrivere codice che è più facile da esaminare per problemi.  

Esponi sempre il minor numero di informazioni possibile nel tuo codice. Nascondere i dettagli di implementazione supporta il codice che è sia gestibile che sicuro. Questi tre suggerimenti faranno molto per scrivere codice Java sicuro:

  • Fai buon uso dei modificatori di accesso di Java . Sapere come dichiarare diversi livelli di accesso per classi, metodi e loro attributi contribuirà notevolmente a proteggere il tuo codice. Tutto ciò che può essere reso privato, dovrebbe essere privato. 
  • Evita la riflessione e l'introspezione . Ci sono alcuni casi in cui queste tecniche avanzate sono meritevoli, ma per la maggior parte dovresti evitarle. L'uso della riflessione elimina la digitazione forte, che può introdurre punti deboli e instabilità nel codice. Il confronto dei nomi delle classi come stringhe è soggetto a errori e può facilmente portare a conflitti nello spazio dei nomi.
  • Definire sempre l'API e le superfici dell'interfaccia più piccole possibili . Disaccoppia i componenti e falli interagire nella più piccola area possibile. Anche se un'area della tua applicazione viene infettata da una violazione, altre saranno al sicuro. 

Regola di sicurezza Java n. 2: evitare la serializzazione

Questo è un altro suggerimento per la codifica, ma è abbastanza importante da essere una regola a sé stante. La serializzazione prende un input remoto e lo trasforma in un oggetto completamente dotato. Elimina i costruttori e i modificatori di accesso e consente a un flusso di dati sconosciuti di diventare codice in esecuzione nella JVM. Di conseguenza, la serializzazione Java è profondamente e intrinsecamente insicura.

La fine della serializzazione Java

Se non hai sentito, Oracle ha piani a lungo termine per rimuovere la serializzazione da Java. Mark Reinhold, chief architect del gruppo di piattaforme Java presso Oracle, ha affermato di ritenere che un terzo o più di tutte le vulnerabilità di Java implichino la serializzazione.

Per quanto possibile, evitare la serializzazione / deserializzazione nel codice Java. Considera invece l'utilizzo di un formato di serializzazione come JSON o YAML. Mai e poi mai esporre un endpoint di rete non protetto che riceve e agisce su un flusso di serializzazione. Questo non è altro che un tappeto di benvenuto per il caos.

Regola di sicurezza Java n. 3: non esporre mai credenziali non crittografate o PII

È difficile da credere, ma questo errore evitabile provoca dolore anno dopo anno.

Quando un utente inserisce una password nel browser, viene inviata come testo in chiaro al tuo server. Quella dovrebbe essere l'ultima volta che vede la luce del giorno. È necessario crittografare la password tramite una crittografia unidirezionale prima di renderla persistente nel database, quindi ripetere la procedura ogni volta che si confronta con quel valore.

Le regole per le password si applicano a tutte le informazioni di identificazione personale (PII): carte di credito, numeri di previdenza sociale, ecc. Qualsiasi informazione personale affidata alla tua applicazione deve essere trattata con il massimo livello di attenzione.

Le credenziali non crittografate o le PII in un database sono una falla nella sicurezza, in attesa che un utente malintenzionato lo scopra. Allo stesso modo, non scrivere mai credenziali non elaborate in un registro o trasmetterle in altro modo a file o rete. Invece, crea un hash salato per le tue password. Assicurati di fare la tua ricerca e di utilizzare un algoritmo di hashing consigliato.

Saltando alla Regola # 4: usa sempre una libreria per la crittografia; non rotolare il tuo.

Regola di sicurezza Java n. 4: utilizza librerie note e testate

Rifatevi gli occhi con questa domanda e risposta sul rollio del vostro algoritmo di sicurezza. La lezione di tl; dr è: usa librerie e framework conosciuti e affidabili quando possibile. Questo si applica a tutto lo spettro, dall'hashing della password all'autorizzazione dell'API REST.

Fortunatamente, Java e il suo ecosistema ti danno le spalle qui. Per la sicurezza delle applicazioni, Spring Security è lo standard de facto. Offre una vasta gamma di opzioni e la flessibilità per adattarsi a qualsiasi architettura di app e incorpora una serie di approcci alla sicurezza.

Il tuo primo istinto nell'affrontare la sicurezza dovrebbe essere quello di fare la tua ricerca. Cerca le migliori pratiche e poi cerca quale libreria implementerà quelle pratiche per te. Ad esempio, se stai cercando di utilizzare i token Web JSON per gestire l'autenticazione e l'autorizzazione, guarda la libreria Java che incapsula JWT e poi impara come integrarlo in Spring Security.

Anche utilizzando uno strumento affidabile, è abbastanza facile confondere l'autorizzazione e l'autenticazione. Assicurati di muoverti lentamente e ricontrolla tutto ciò che fai.

Regola di sicurezza Java n. 5: essere paranoici sull'input esterno

Indipendentemente dal fatto che provenga da un utente che digita in un modulo, un datastore o un'API remota, non fidarti mai dell'input esterno.

SQL injection e cross-site scripting (XSS) sono solo gli attacchi più comunemente noti che possono derivare da una cattiva gestione dell'input esterno. Un esempio meno noto, uno dei tanti, è l '"attacco da un miliardo di risate", in base al quale l'espansione di entità XML può causare un attacco Denial of Service.

Ogni volta che si riceve un input, è necessario verificarne l'integrità e disinfettarlo. Ciò è particolarmente vero per tutto ciò che potrebbe essere presentato a un altro strumento o sistema per l'elaborazione. Ad esempio, se qualcosa potesse finire come argomento per una riga di comando del sistema operativo: attenzione!

Un'istanza speciale e ben nota è SQL injection, trattata nella regola successiva.

Regola di sicurezza Java n. 6: utilizzare sempre istruzioni preparate per gestire i parametri SQL

Ogni volta che si crea un'istruzione SQL, si rischia di interpolare un frammento di codice eseguibile.

Sapendo ciò, è buona norma utilizzare sempre la classe java.sql.PreparedStatement per creare SQL. Esistono strutture simili per gli archivi NoSQL come MongoDB. Se stai usando un livello ORM, l'implementazione userà PreparedStatements per te sotto il cofano.

Regola di sicurezza Java n. 7: non rivelare l'implementazione tramite messaggi di errore

I messaggi di errore in produzione possono essere una fertile fonte di informazioni per gli aggressori. Le tracce dello stack, in particolare, possono rivelare informazioni sulla tecnologia che stai utilizzando e su come la stai utilizzando. Evita di rivelare tracce dello stack agli utenti finali.

Anche gli avvisi di accesso non riuscito rientrano in questa categoria. È generalmente accettato che venga fornito un messaggio di errore come "Accesso non riuscito" rispetto a "Utente non trovato" o "Password errata". Offri il minor aiuto possibile agli utenti potenzialmente malvagi.

Idealmente, i messaggi di errore non dovrebbero rivelare lo stack tecnologico sottostante per la tua applicazione. Mantieni queste informazioni il più opache possibile.

Regola di sicurezza Java n. 8: mantenere aggiornate le versioni di sicurezza

A partire dal 2019, Oracle ha implementato un nuovo schema di licenze e un nuovo programma di rilascio per Java. Sfortunatamente per gli sviluppatori, la nuova cadenza di rilascio non semplifica le cose. Tuttavia, è tua responsabilità controllare frequentemente gli aggiornamenti di sicurezza e applicarli al tuo JRE e JDK.

Assicurati di sapere quali patch critiche sono disponibili controllando regolarmente la home page di Oracle per gli avvisi di sicurezza. Ogni trimestre, Oracle fornisce un aggiornamento automatico delle patch per l'attuale versione LTS (supporto a lungo termine) di Java. Il problema è che quella patch è disponibile solo se stai pagando una licenza di supporto Java.

Se la tua organizzazione sta pagando per tale licenza, segui il percorso di aggiornamento automatico. In caso contrario, probabilmente stai utilizzando OpenJDK e dovrai eseguire le patch da solo. In questo caso, potresti applicare la patch binaria, oppure potresti semplicemente sostituire l'installazione di OpenJDK esistente con l'ultima versione. In alternativa, puoi utilizzare un OpenJDK supportato commercialmente come Zulu Enterprise di Azul.

Hai bisogno di ogni patch di sicurezza?

Se osservi attentamente gli avvisi di sicurezza, potresti scoprire che non hai bisogno di un determinato set di aggiornamenti. Ad esempio, la versione di gennaio 2020 sembra essere un aggiornamento critico di Java; tuttavia, una lettura attenta mostra che l'aggiornamento risolve solo i buchi nella sicurezza delle applet Java e non influisce sui server Java.

Regola di sicurezza Java n. 9: cerca le vulnerabilità delle dipendenze

Sono disponibili molti strumenti per eseguire la scansione automatica della base di codice e delle dipendenze alla ricerca di vulnerabilità. Tutto quello che devi fare è usarli.

OWASP, Open Web Application Security Project, è un'organizzazione dedicata al miglioramento della sicurezza del codice. L'elenco di OWASP di strumenti di scansione automatica del codice affidabili e di alta qualità include diversi strumenti orientati a Java.

Controlla regolarmente la tua base di codice, ma tieni anche d'occhio le dipendenze di terze parti. Gli aggressori prendono di mira sia le librerie open source che quelle closed-source. Controlla gli aggiornamenti alle tue dipendenze e aggiorna il tuo sistema non appena vengono rilasciate nuove correzioni per la sicurezza.

Regola di sicurezza Java n. 10: monitora e registra l'attività degli utenti

Anche un semplice attacco di forza bruta può avere successo se non stai monitorando attivamente la tua applicazione. Usa strumenti di monitoraggio e registrazione per tenere d'occhio lo stato di salute dell'app.

Se vuoi essere convinto del motivo per cui il monitoraggio è importante, siediti e guarda i pacchetti TCP sulla porta di ascolto delle tue applicazioni. Vedrai tutti i tipi di attività, ben oltre le semplici interazioni dell'utente. Alcune di queste attività saranno bot e malfattori che cercano vulnerabilità.

È necessario registrare e monitorare i tentativi di accesso non riusciti e implementare contromisure per impedire che i client remoti attacchino impunemente.

Il monitoraggio può avvisarti di picchi inspiegabili e la registrazione può aiutare a svelare cosa è andato storto a seguito di un attacco. L'ecosistema Java include una vasta gamma di soluzioni commerciali e open source per la registrazione e il monitoraggio.

Regola di sicurezza Java n. 11: attenzione agli attacchi Denial of Service (DoS)

Ogni volta che si elaborano risorse potenzialmente costose o si intraprendono operazioni potenzialmente costose, è necessario proteggersi dall'utilizzo incontrollato delle risorse.

Oracle mantiene un elenco di potenziali vettori per questo tipo di problema nelle sue linee guida sulla codifica sicura per il documento Java SE, sotto l'intestazione "Denial Of Service".

Fondamentalmente, ogni volta che si va a eseguire un'operazione costosa, come decomprimere un file compresso, è necessario monitorare l'utilizzo delle risorse esplosive. Non fidarti dei file manifest. Fidati solo del consumo effettivo su disco o in memoria, monitoralo e proteggiti da eccessi che mettono in ginocchio il server.

Allo stesso modo, in alcune elaborazioni è importante osservare i cicli continui inaspettati. Se un loop è sospetto, aggiungi una guardia che assicuri che il loop stia procedendo e mandalo in corto circuito se sembra essere diventato zombi.

Regola di sicurezza Java n. 12: prendere in considerazione l'utilizzo del gestore della sicurezza Java

Java ha un gestore della sicurezza che può essere utilizzato per limitare le risorse a cui ha accesso un processo in esecuzione. Può isolare il programma rispetto all'accesso al disco, alla memoria, alla rete e alla JVM. Restringere questi requisiti per la tua app riduce l'impronta di possibili danni da un attacco. Tale isolamento può anche essere scomodo, motivo per cui SecurityManagernon è abilitato per impostazione predefinita.

Dovrai decidere da solo se aggirare SecurityManagerle opinioni forti vale il livello aggiuntivo di protezione per le tue applicazioni. Consulta i documenti Oracle per ulteriori informazioni sulla sintassi e sulle funzionalità di Java Security Manager.

Regola di sicurezza Java n. 13: considera l'utilizzo di un servizio di autenticazione cloud esterno

Alcune applicazioni devono semplicemente possedere i propri dati utente; per il resto, un provider di servizi cloud potrebbe avere senso.

Cerca in giro e troverai una vasta gamma di provider di autenticazione cloud. Il vantaggio di un tale servizio è che il fornitore è responsabile della protezione dei dati sensibili degli utenti, non tu. D'altra parte, l'aggiunta di un servizio di autenticazione aumenta la complessità dell'architettura aziendale. Alcune soluzioni, come FireBase Authentication, includono SDK per l'integrazione in tutto lo stack.

Conclusione

Ho presentato 13 regole per lo sviluppo di applicazioni Java più sicure. Queste regole sono collaudate, ma la regola più grande di tutte è questa: sii sospettoso. Affronta sempre lo sviluppo del software con cautela e una visione orientata alla sicurezza. Cerca le vulnerabilità nel codice, sfrutta i vantaggi delle API e dei pacchetti di sicurezza Java e utilizza strumenti di terze parti per monitorare e registrare il codice per problemi di sicurezza.

Ecco tre buone risorse di alto livello per rimanere al passo con il panorama della sicurezza Java in continua evoluzione:

  • OWASP Top 10
  • CWE Top 25
  • Linee guida per il codice sicuro di Oracle

Questa storia, "Tredici regole per lo sviluppo di applicazioni Java sicure" è stata originariamente pubblicata da JavaWorld.