Come posso garantire la distruzione di un object String in Java?

Un destinatario presso la mia azienda ha bisogno di modificare i dati da un database SQL Server attraverso un programma che ho creato. Inizialmente il programma utilizzava l’autenticazione di Windows e ho chiesto agli amministratori di database di fornire a questo specifico utente l’accesso in scrittura a tale database.

Non erano disposti a farlo, e invece hanno dato accesso in scrittura al mio account utente di Windows.

Dato che mi fido del ragazzo ma non abbastanza da consentirgli di lavorare per 90 minuti con la mia sessione aperta, aggiungerò un prompt di accesso al mio programma, chiedendo una combinazione di nome utente e password e accesso a SQL Server. Effettuerò il login e mi fido della mia applicazione per fargli fare solo ciò di cui ha bisogno.

Questo, tuttavia, solleva un piccolo rischio per la sicurezza. Il tutorial sui campi della password sul sito Sun Oracle afferma che le password devono conservare la quantità minima di tempo richiesta in memoria e, a tal fine, il metodo getPassword restituisce un array char[] che è ansible azzerare una volta getPassword .

Tuttavia, la class DriverManager di Java accetta solo oggetti String come password, quindi non potrò disporre della password non appena avrò finito con essa. E dal momento che la mia applicazione è incidentalmente piuttosto bassa su allocazioni e requisiti di memoria, chissà per quanto tempo sopravviverà in memoria? Il programma funzionerà per un tempo piuttosto lungo, come indicato sopra.

Naturalmente, non posso controllare qualunque cosa le classi JDBC di SQL Server facciano con la mia password, ma speravo di poter controllare quello che faccio con la mia password.

Esiste un modo sicuro per distruggere / azzerare un object String con Java? So che entrambi sono in qualche modo contrari al linguaggio (la distruzione dell’object non è deterministica e gli oggetti String sono immutabili) e System.gc() è anche imprevedibile, ma continuo; qualche idea?

Quindi, ecco le cattive notizie. sono sorpreso che nessuno l’abbia mai menzionato. con i moderni spazzini, anche l’intero concetto di char [] è rotto. indipendentemente dal fatto che tu usi una stringa o un carattere [], i dati possono finire per vivere in memoria per chissà quanto. perché? perché i jvms moderni usano i garbage collector generazionali che, in breve, copiano gli oggetti dappertutto . quindi, anche se si utilizza un carattere [], la memoria effettiva che utilizza potrebbe essere copiata in varie posizioni nell’heap, lasciando copie della password ovunque vada (e nessun gc performante sta per azzerare la vecchia memoria). così, quando azzeri l’istanza che hai alla fine, stai solo azzerando l’ultima versione in memoria.

lunga storia, breve, non esiste un modo a prova di proiettile per gestirlo. ti devi quasi fidare della persona.

Posso solo pensare a una soluzione usando la riflessione. È ansible utilizzare reflection per richiamare il costruttore privato che utilizza un array di caratteri condivisi:

  char[] chars = {'a', 'b', 'c'}; Constructor con = String.class.getDeclaredConstructor(int.class, int.class, char[].class); con.setAccessible(true); String password = con.newInstance(0, chars.length, chars); System.out.println(password); //erase it Arrays.fill(chars, '\0'); System.out.println(password); 

modificare

Per chiunque pensi che questa sia una misura di sicurezza o anche un’utile precauzione, ti incoraggio a leggere la risposta di jtahlborn per almeno un avvertimento.

se è assolutamente necessario, mantenere un WeakReference sulla stringa e continuare a inghiottire memoria finché non si impone la garbage collection della stringa che è ansible rilevare testando se il riferimento debole è diventato nullo. questo può ancora lasciare i byte nello spazio degli indirizzi del processo. potrebbe esserci un altro paio di bidoni del netturbino che ti darebbe conforto? quindi, dopo che il riferimento debole della stringa originale è stato annullato, crea un altro riferimento debole e abbandona fino a quando non viene azzerato, il che implicherebbe un ciclo completo di raccolta dei dati inutili.

in qualche modo, devo aggiungere LOL a questo anche se la mia risposta sopra è del tutto seria 🙂

È ansible modificare il valore del char[] interno char[] utilizzando la reflection.

È necessario fare attenzione a cambiarlo con un array della stessa lunghezza o ad aggiornare anche il campo count . Se si desidera poterlo utilizzare come una voce in un set o come un valore nella mappa, sarà necessario ricalcolare il codice hash e impostare il valore del campo hashCode .

Detto questo, il codice minimo per raggiungere questo è

  String password = "password"; Field valueField = String.class.getDeclaredField("value"); valueField.setAccessible(true); char[] chars = (char[]) valueField.get(password); chars[0] = Character.valueOf('h'); System.out.println(password); 

Non sono sicuro della class DriverManager .
In generale, hai ragione, la raccomandazione è di memorizzare la password in array di caratteri e di cancellare esplicitamente la memoria dopo l’uso.
L’esempio più comune:

 KeyStore store = KeyStore. getInstance(KeyStore, getDefaultType()) ; char[] password = new char[] {'s','e','c','r','e','t'}; store .load(is, password ); //After finished clear password!!! Arrays. fill(password, '\u0000' ) ; 

Nel JSSE e JCA il design aveva esattamente questo in mente. Questo è il motivo per cui le API si aspettano un char[] e non una stringa.
Poiché, come ben sai, le stringhe sono immutabili, la password è idonea per la futura garbage collection e non puoi ripristinarla in seguito. Ciò può causare rischi per la sicurezza da parte di programmi dannosi che snoopano aree di memoria.
Non penso che in questo caso tu stia guardando dentro c’è un lavoro in giro.
In realtà c’è una domanda simile qui:
Perché Driver Manager non usa array di caratteri?
ma non c’è una risposta chiara.
Sembra che il concetto sia che la password è già memorizzata in un file di proprietà (esiste già un costruttore di DriverManager che accetta le proprietà) e quindi il file stesso impone già un rischio maggiore rispetto al reale caricamento della password da un file a una stringa.
Oppure i progettisti dell’API avevano alcune ipotesi sulla sicurezza della macchina che accede al DB.
Penso che l’opzione più sicura sarebbe cercare di esaminare, se è ansible, su come DriverManager utilizza la password, ovvero mantiene un riferimento interno, ecc.

quindi non sarò in grado di disporre della password non appena ho finito con esso.

Perchè no?

Se stai ottenendo una connessione via

 Driver.getConnection 

devi solo passare la password e lasciare che sia gc.

 void loginScreen() { ... connect( new String( passwordField.getPassword() ) ); ... } ... void connect( String withPassword ) { connection = DriverManager.getConnection( url, user, withPassword ); ... }//<-- in this line there won't be any reference to your password anymore. 

Quando il controllo ritorna dal metodo di connect , non c'è più un riferimento per la tua password. Nessuno può più usarlo. Se è necessario creare una nuova sessione, è necessario richiamare "Connetti" di nuovo con una nuova password. Il riferimento all'object che contiene le tue informazioni viene perso.

Se la stringa non viene trattenuta da JDBC driver manager (un grande se), non mi preoccuperei di forzarne la distruzione. Una JVM moderna, esegue ancora la raccolta dei rifiuti in modo abbastanza rapido anche con molta memoria disponibile. Il problema è se la garbage collection è efficace “cancellazione sicura”. Dubito che lo sia. Immagino che dimentichi semplicemente il riferimento a quella locazione di memoria e non azzeri nulla.

Non esiste un modo regolare per forzare la garbage collection. È ansible tramite una chiamata nativa, ma non so se funzionerebbe con le stringhe, visto che sono raggruppate.

Forse un approccio alternativo ti sarà d’aiuto? Non ho molta dimestichezza con SQL Server, ma in Oracle lo studio consiste nel fare in modo che l’utente A possegga gli oggetti del database e una serie di stored procedure e l’utente B non possegga nulla se non l’authorization di esecuzione sulle procedure. In questo modo la password non è più un problema.

Nel tuo caso, sarà il tuo utente a possedere tutto l’object del database e le stored procedure e il tuo dipendente avrà bisogno di privilegi di esecuzione per i proc memorizzati, che gli amministratori di database saranno probabilmente meno riluttanti a dare.

Domanda interessante Alcuni googeling hanno rivelato questo: http://securesoftware.blogspot.com/2009/01/java-security-why-not-to-use-string.html . Secondo il commento, non farà la differenza.

Cosa succede se non si memorizza la stringa in una variabile ma la si passa tramite una nuova stringa (char [])?