2016-04-07 28 views
13

Abbiamo tre applicazioni Web (Spring MVC-Hibernate standard) in esecuzione all'interno di un server Jboss 6.1. Tutte e tre le applicazioni condividono un metodo di autenticazione comune che viene compilato come JAR e incluso in ogni file WAR. Il nostro metodo di autenticazione utilizza org.springframework.security.crypto.bcrypt.BCrypt hash delle password utente, vedere qui sotto:Aumento delle prestazioni di BCrypt

Opzioni
hashedPassword.equals(BCrypt.hashpw(plainTextPassword, salt)); 

JBOSS StartUp

set "JAVA_OPTS=-Xms2048m -Xmx4096m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -verbosegc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:gc.txt -XX:+UseParallelOldGC

Problema: Risulta che quando il server viene riavviato, Bcrypt.hashpw richiede 100 ms per decrittografare la password. Tuttavia, dopo un po 'di tempo (non c'è alcun motivo) improvvisamente le prestazioni di Bcrypt.hashpw aumentano da 100ms a 10s di secondi. Non c'è una ragione ovvia per questo.

Maggiori informazioni:

  • Hibernate Versione: 4.2.4.Final
  • Primavera Versione: 4.0.5.RELEASE Primavera
  • Versione Security: 3.2.4.RELEASE

Qualcun altro ha già visto questo problema prima?

+1

Collegare un profiler e capire quale parte impiega così tanto tempo. –

+0

Salve @ m-deinum, lo abbiamo già fatto e abbiamo scoperto che il problema riguarda la libreria stessa. Questo non spiega perché la libreria funzioni bene per un po 'di tempo e poi si innalza (e rimane così) a 10 secondi o più. –

+0

Quale libreria e quale parte della libreria ... Il fatto che sia * la libreria * non chiarisca le cose, potresti voler essere un po 'più specifico su quale parte della libreria. Metodo ecc. –

risposta

2

Una possibile spiegazione è che il SeedGenerator di SecureRandom causa i ritardi.

Springs bcrypt attuazione usesSecureRandom che a sua volta utilizza un SeedGenerator che a sua volta può utilizzare il blocco /dev/random. Here è una buona descrizione di quelle classi.

Che bugreport segnala anche problemi di prestazioni in BCrypt e li ha rintracciati al generatore di semi, mostrando stacktraces completi. L'implementazione di BCrypt è diversa ma lo stacktrace sotto SecureRandom deve essere identico all'implementazione della molla. La loro soluzione era ridurre la frequenza di resezione di BCrypt.

+0

Grazie per la tua risposta. Ma il nostro scenario è diverso. In breve, non stiamo usando Bcrypt.gensalt(). Non generiamo sale durante l'autenticazione dell'utente. Invece abbiamo già salt memorizzato in db e recuperarlo prima di utilizzare il metodo hashpw. –

7

Problema: Sembra che quando il server viene riavviato, Bcrypt.hashpw accetta 100 ms per decrittografare la password. Tuttavia, dopo un po 'di tempo (senza pattern) improvvisamente le prestazioni di Bcrypt.hashpw aumentano da 100ms a 10s di secondi. Non c'è una ragione ovvia per questo.

Il problema è /dev/random a volte isolati e quando lo fa appare essere casuale :) La cosa più confuso è che durante il tentativo di verificare come funziona si incorrerà contro l'Effetto Observer vale a dire durante il tentativo di osserva il comportamento casuale che stai generando entropia e questo può portare a un sacco di confusione, ovvero i miei risultati non saranno uguali ai tuoi ecc. Questo è anche il motivo per cui sembra che non ci siano pattern ..

Mostrerò il problema e mostrarti come ricrearlo (entro limiti ragionevoli) sui tuoi server in modo da poter testare le soluzioni. Proverò a fornire un paio di correzioni, si noti che questo è su Linux ma lo stesso problema si verificherà su qualsiasi sistema che richiede entropia per generare numeri casuali e si esaurisce.

Su Linux /dev/random è un flusso di byte casuali. Come si legge dal flusso si esaurisce l'entropia disponibile. Quando raggiunge un certo punto legge dal blocco /dev/random. Si può vedere a disposizione l'entropia di utilizzare questo comando

cat /proc/sys/kernel/random/entropy_avail 

Se si esegue il seguente script bash e anche monitorare entropy_avail avrete avviso che l'entropia tuffi drammaticamente come lo script bash consuma.

while : 
do 
    cat /dev/random > /dev/null 
done 

Questo dovrebbe anche dare un suggerimento su come ricreare questo problema sui server cioè eseguire lo script bash sopra per ridurre l'entropia disponibile e il problema si manifesterà.

Se volete vedere quanti byte al secondo sistema è la creazione si possibile utilizzare pv per misurarla cioè

pv /dev/random 

Se si lascia pv esecuzione ha un effetto, è consumando il flusso casuale di byte che significa che altri servizi potrebbero iniziare a bloccare. Notare che pv sta anche visualizzando il suo output, quindi potrebbe anche essere disponibile in aumento sul sistema :).

Su sistemi con poca o nessuna entropia utilizzando pv /dev/random sembrerà glaciale lento. Ho anche sperimentato che a volte le VM hanno grossi problemi con la generazione di entropia.

per ricreare il problema creare utilizzare la seguente classe ...

import java.security.SecureRandom; 
import org.mindrot.jbcrypt.BCrypt; 
public class RandTest { 
    public static void main(String[] args) { 
     SecureRandom sr = new SecureRandom(); 
     int out = 0; 
     String password = "very-strong-password-1729"; 
     String hashed; 
     for (int i = 0; i < 200000 ; i++) { 
      hashed = BCrypt.hashpw(password, BCrypt.gensalt()); 
      //If we print, we're generating entroy :) System.out.println(hashed); 
     } 
    } 
} 

Ho scaricato bcrypt in una directory locale. Ho compilato ed eseguito come segue

javac -cp ./jBCrypt-0.4/src/ RandTest.java 
java -cp ./jBCrypt-0.4/src/:. RandTest 

Se quindi si esegue lo script bash dalla prima, mentre runnng RandTest vedrete grandi pause in cui il sistema sta bloccando in attesa di più l'entropia. Se si esegue strace vedrete il seguente ...

1067 [pid 22481] open("/dev/random", O_RDONLY|O_LARGEFILE) = 12 
11068 [pid 22481] fstat64(12, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 8), ...}) = 0 
11069 [pid 22481] fcntl64(12, F_GETFD)  = 0 
11070 [pid 22481] fcntl64(12, F_SETFD, FD_CLOEXEC) = 0 
..... 
11510 [pid 22481] read(12, "\320\244\317RB\370", 8) = 6 

Il programma sta leggendo dal /dev/random. Il problema con l'entropia di prova è e potresti generarne di più mentre provi a testarlo, ad esempio l'effetto Observer.

correzioni

La prima soluzione consiste nel modificare dall'utilizzo /dev/random a /dev/urandom cioè

time java -Djava.security.egd=file:///dev/./urandom -cp ./jBCrypt-0.4/src/:. RandTest 

Una soluzione alternativa è quella di ricreare il dispositivo /dev/random come dispositivo /dev/urandom. Puoi trovare come fare questo forma la pagina man, invece di crearli ...

mknod -m 644 /dev/random c 1 8 
mknod -m 644 /dev/urandom c 1 9 
chown root:root /dev/random /dev/urandom 

cancelliamo uno e falso cioè

rm /dev/random 
mknod -m 644 /dev/random c 1 9 
chown root:root /dev/random 

/dev/random è ora effettivamente /dev/urandom

La cosa fondamentale da ricordare sta testando dati casuali che richiede entroy dal sistema sei test è difficile a causa dell'effetto Observer.

+2

Grazie per la tua risposta. Ma il nostro scenario è diverso. In breve, non stiamo usando Bcrypt.gensalt(). Non generiamo sale durante l'autenticazione dell'utente. Invece abbiamo già salt memorizzato in db e recuperarlo prima di utilizzare il metodo hashpw. –