BCrypt性能恶化

Geo*_*iou 19 java performance spring-mvc spring-security bcrypt

我们在Jboss服务器6.1中运行了三个Web应用程序(标准的Spring MVC-Hibernate).所有这三个应用程序共享一个通用的身份验证方法,该方法被编译为JAR并包含在每个WAR文件中.我们的身份验证方法使用org.springframework.security.crypto.bcrypt.BCrypt来哈希用户密码,请参阅以下内容:

hashedPassword.equals(BCrypt.hashpw(plainTextPassword, salt));
Run Code Online (Sandbox Code Playgroud)

JBOSS启动选项

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

问题: 当服务器重新启动时,Bcrypt.hashpw需要100ms来解密密码.然而,经过一段时间(没有模式),Bcrypt.hashpw性能突然从100毫秒上升到10秒.这没有明显的原因.

更多信息:

  • Hibernate版本:4.2.4.Final
  • Spring Version:4.0.5.RELEASE Spring
  • 安全版本:3.2.4.RELEASE

有没有其他人见过这个问题?

Har*_*rry 16

问题:当服务器重新启动时,Bcrypt.hashpw需要100ms来解密密码.然而,经过一段时间(没有模式),Bcrypt.hashpw性能突然从100毫秒上升到10秒.这没有明显的原因.

这个问题/dev/random有时是块,当它出现时它会显得随机:)更令人困惑的是,在尝试测试它是如何工作的时候,你会遇到观察者效应,即在尝试观察你正在产生的随机行为时熵,这可能导致大量的混乱,即我的结果将不会与你的相同等.这也是为什么它看起来没有模式..

我将演示该问题,并向您展示如何在您自己的服务器上重新创建它(在合理范围内),以便您可以测试解决方案.我将尝试提供一些修复,请注意这是在Linux上,但同样的问题将发生在需要熵生成随机数并耗尽的任何系统上.

在Linux上/dev/random是一个随机字节流.当你从流中读取时,你耗尽了可用的熵.当它到达某一点时从/dev/random块读取.您可以使用此命令查看可用的熵

cat /proc/sys/kernel/random/entropy_avail
Run Code Online (Sandbox Code Playgroud)

如果您运行以下bash脚本并且还监视entropy_avail您将注意到当bash脚本使用它时熵会急剧下降.

while :
do
  cat /dev/random > /dev/null
done
Run Code Online (Sandbox Code Playgroud)

这还应该为您提供有关如何在服务器上重新创建此问题的提示,即运行上面的bash脚本以减少可用的熵,问题就会显现出来.

如果您想查看系统每秒创建的字节数,您可以使用pv它来测量它,即

pv /dev/random
Run Code Online (Sandbox Code Playgroud)

如果你继续pv运行它会产生影响,它会消耗随机的字节流,这意味着其他服务可能会开始阻塞.注意,pv它也显示它的输出,所以它也可能在系统上增加可用的修改:).

在使用很少或没有熵的系统上pv /dev/random看起来似乎很慢.我也经历过VM有时会产生熵的主要问题.

要重新创建该问题,请使用以下类...

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我将bcrypt下载到本地目录.我编译并运行如下

javac -cp ./jBCrypt-0.4/src/   RandTest.java
java  -cp ./jBCrypt-0.4/src/:. RandTest
Run Code Online (Sandbox Code Playgroud)

如果你在运行时运行之前的bash脚本,RandTest你会看到系统阻塞等待更多熵的大停顿.如果你跑步,strace你会看到以下......

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
Run Code Online (Sandbox Code Playgroud)

该计划正在阅读/dev/random.测试熵的问题是你可能在尝试测试它时产生更多,即观察者效应.

修复

第一个修复是从使用/dev/random改为/dev/urandomie

time java  -Djava.security.egd=file:///dev/./urandom -cp ./jBCrypt-0.4/src/:.  RandTest
Run Code Online (Sandbox Code Playgroud)

另一种解决方法是将/dev/random设备重新创建为/dev/urandom设备.您可以在手册页中找到如何执行此操作,而不是创建它们...

mknod -m 644 /dev/random c 1 8
mknod -m 644 /dev/urandom c 1 9
chown root:root /dev/random /dev/urandom
Run Code Online (Sandbox Code Playgroud)

我们删除一个并伪造它即

rm /dev/random
mknod -m 644 /dev/random c 1 9
chown root:root /dev/random
Run Code Online (Sandbox Code Playgroud)

/dev/random 现在实际上 /dev/urandom

要记住的关键是要测试随机数据,由于观察者效应,需要从您正在测试的系统中输入.

  • 谢谢你的回应.但我们的情况有所不同.简而言之,我们没有使用Bcrypt.gensalt().我们在验证用户时不会生成盐.相反,我们已经在db中存储了salt并在使用hashpw方法之前检索它. (3认同)