2016-06-04 17 views
9

Per impedire , è talvolta necessario un tempo costante equals. C'è MessageDigest.isEqual non documentato come metodo a tempo costante e guava HashCode.equals e altri. Tutti loro fare qualcosa di simileIl tempo costante è uguale a

boolean areEqual = true; 
for (int i = 0; i < this.bytes.length; i++) { 
    areEqual &= (this.bytes[i] == that.getBytesInternal()[i]); 
} 
return areEqual; 

o

int result = 0; 
    for (int i = 0; i < digesta.length; i++) { 
     result |= digesta[i]^digestb[i]; 
    } 
    return result == 0; 

ma chi dice che il JIT non può introdurre un corto circuito quando l'ottimizzazione?

Non è così difficile scoprire, ad es., Che lo areEqual non diventerà mai più vero e interromperà il ciclo.


diedi a try on CR calcolando un valore dipendente tutti i bit di ingresso e alimentandola a un fatto in casa Blackhole.

risposta

0

Uno potrebbe impedire l'ottimizzazione in quanto tale:

int res = 0; 
for (int i = 0; i < this.bytes.length; i++) { 
    res |= this.bytes[i]^that.getBytesInternal()[i]; 
} 
Logger.getLogger(...).log(FINEST, "{0}", res); 
return res == 0; 

Ma sul codice originale:

Con il vecchio codice di uno forse dovrebbero smontare con javap per vedere che nessuno di ottimizzazione è stato fatto. Per un altro compilatore java (come java 9) bisognerebbe ripeterlo.

JIT calcia in ritardo, ma poi hai ragione, l'ottimizzazione potrebbe accadere: avrebbe bisogno di un test aggiuntivo nel ciclo (che di per sé rallenta ogni ciclo verso il basso).

Quindi hai ragione. Si può solo sperare che l'effetto sia trascurabile nell'intera misura. E qualche altra guardia sicura aiuta, se solo un ritardo casuale su fallisce, che è sempre un bel inciampo.

+0

In che modo una dichiarazione di registro all'esterno impedisce il cortocircuito? –

+0

@SleimanJneidi Poiché il risultato 'res' viene registrato, deve essere calcolato fino alla fine, quindi il loop non può essere cortocircuitato. –

+0

... a meno che non si ottenga 'res == -1' ad un certo punto e quindi si può uscire dal ciclo. Ma questo sarebbe un test diverso e non riesco a immaginare un compilatore che lo faccia. – maaartinus

5

Non si può conoscere il futuro

È fondamentalmente non si può prevedere cosa ottimizzatori futuro potrebbe o non potrebbe fare in qualsiasi lingua.

Per guardare al futuro, le migliori probabilità sono per il sistema operativo stesso di fornire test costanti di temporizzazione, in modo che possano essere correttamente testati e utilizzati in tutti gli ambienti.

Questo è già in corso da un po 'di tempo. Per esempio. La funzione timingsafe_bcmp() in libc è apparsa per la prima volta in OpenBSD 4.9. (Rilasciato a maggio 2011).

Ovviamente gli ambienti di programmazione devono prelevarli e/o fornire le proprie funzioni che garantiscono che non saranno ottimizzati.

ispezionare il codice assembly

V'è una certa discussione di ottimizzatori here. È C (e C++), ma è davvero indipendente dalla lingua che puoi solo guardare a ciò che gli attuali ottimizzatori possono fare, non a ciò che potrebbero fare i futuri ottimizzatori. Ad ogni modo raccomandano giustamente di controllare il codice assembly per sapere cosa fa il tuo ottimizzatore.

Per java che non è necessariamente così "facile" come per C o C++ data la sua natura, ma non dovrebbe essere impossibile per specifiche funzioni di sicurezza fare effettivamente questo sforzo per gli ambienti attuali.

evitare potrebbe essere possibile

Si potrebbe provare a evitare l'attacco di temporizzazione.

Es .:

Anche se intuitivamente l'aggiunta di tempo casuale potrebbe sembrare il ting da fare, non funziona: gli attaccanti sta già utilizzando l'analisi statistica in tempi gli attacchi, si deve semplicemente aggiungere un po 'più rumore.

https://security.stackexchange.com/questions/96489/can-i-prevent-timing-attacks-with-random-delays

Ancora: non significa che non si può fare un tempo costante implementazione se l'applicazione può essere abbastanza lento. Ad esempio: aspetta abbastanza a lungo. Per esempio. potresti aspettare che un timer si spenga e solo allora continuare ad elaborare il risultato del confronto evitando comunque l'attacco a tempo.

Detection

Dovrebbe essere possibile scrivere rivelazione di temporizzazione vulnerabilità attacco in applicazioni utilizzando un'implementazione di temporizzazione confronto costante.

Ether:

  • alcuni test che viene eseguito durante l'inizializzazione
  • lo stesso test regolarmente come parte della normale operatività.

Ancora una volta l'ottimizzatore sarà difficile da gestire come può (ea volte lo farà) persino cambiare l'ordine di esecuzione delle cose. Ma ad es. utilizzando input che il programma non ha nel suo codice (ad esempio un file esterno) e eseguendolo due volte: una volta con un confronto normale e stringhe identiche, una volta con stringhe completamente diverse (xored ad es.) e poi di nuovo con quegli input ma con un tempo costante confrontare. Ora hai 4 tempi: il confronto normale non dovrebbe essere lo stesso, il confronto costante dovrebbe essere più lento e uguale. Se fallisce: avvertire l'utente/manutentore dell'applicazione, è probabile che il tempo costante venga interrotto durante l'utilizzo della produzione.

  • Un'opzione teorica è quella di raccogliere autonomamente i tempi effettivi (registrare errori/successi) e analizzarli statisticamente. Ma sarebbe difficile da eseguire in pratica poiché le tue misurazioni avrebbero bisogno di essere estremamente accurate dato che non puoi looparlo un paio di volte, hai a che fare con la misurazione di un solo confronto e non avrai la risoluzione per misurarlo con precisione. ..
5

JIT non è solo permesso fare queste ottimizzazioni, ma in realtà fa volte.

Ecco a sample bug che ho trovato in JMH, dove l'ottimizzazione del cortocircuito ha portato a punteggi di benchmark instabili. JIT ha ottimizzato la valutazione di , nonostante sia stato utilizzato & anziché &&, anche se e bool2 sono stati dichiarati volatile.

JIT non fornisce garanzie su cosa ottimizza e cosa no. Anche se verifichi che funzioni come vuoi, le future versioni di JVM potrebbero rompere questi presupposti. Idealmente dovrebbero esserci metodi intrinseci nelle librerie JDK di base per primitive di sicurezza così importanti.

Si può provare a evitare ottimizzazioni indesiderate con determinate tecniche, ad es.

  • coinvolgere campi volatili;
  • applicare l'accumulo incrementale;
  • effetti collaterali prodotti, ad esempio, scrivere a memoria condivisa, ecc

ma sono anche a prova di proiettile non al 100%, in modo da avere per verificare il codice assembly generato e rewview dopo ogni importante aggiornamento di Java .

1

In effetti non è possibile prevedere cosa farà un ottimizzatore. Tuttavia, in questo caso si potrebbe ragionevolmente fare quanto segue:

  1. Calcolare l'OR esclusivo dei valori confrontati. Il tempo impiegato dipende solo dalla lunghezza dei valori.
  2. Calcolare un hash dei byte risultanti. Utilizzare una funzione di hash che restituisce un singolo numero intero.
  3. Esclusivo-OPPURE questo hash con l'precompilato hash di una sequenza di uguale lunghezza di zeri.

Penso che sia una scommessa abbastanza sicura che le funzioni di hash non sono qualcosa che verrà ottimizzato. E un OR esclusivo tra interi è uguale alla velocità indipendentemente dal risultato.

+2

Restituire un 'int' porterebbe a collisioni con una probabilità di' 2 ** - 32', cioè '2.3e-10', che è semplicemente troppo per qualsiasi cosa sensibile alla sicurezza. Ridurrebbe a indovinare il segreto (possibilità come '2 ** - 256', cioè impossibile) di indovinare una collisione. +++ Usare 'long' sarebbe meglio (e anche il tempo costante su una JVM a 64 bit), ma non ancora abbastanza buono. – maaartinus