BCrypt 性能下降

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

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

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秒。这没有明显的原因。

更多信息:

  • 休眠版本: 4.2.4.最终版
  • 春季版:4.0.5.发布春季版
  • 安全版本: 3.2.4.发布

以前有没有人见过这个问题?


答案 1

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

问题有时是块,当它这样做时,它将看起来像是随机的:)更令人困惑的是,在尝试测试它的工作原理时,您将遇到观察者效应,即在尝试观察随机行为时,您正在生成熵,这可能导致大量的混乱,即我的结果不会与您的结果相同等。这也是为什么它看起来没有模式的原因。/dev/random

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

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

cat /proc/sys/kernel/random/entropy_avail

如果您运行以下 bash 脚本并进行监视,您会注意到,随着 bash 脚本消耗熵,熵会急剧下降。entropy_avail

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

这还应该给你一个提示,告诉你如何在你的服务器上重现这个问题,即运行上面的bash脚本来减少可用的熵,问题就会显现出来。

如果你想看看你的系统每秒创建多少字节,你可以用它来测量它,即pv

pv /dev/random

如果你保持运行状态,它会产生影响,它会消耗随机的字节流,这意味着其他服务可能会开始阻塞。请注意,它还显示其输出,因此它也可能增加系统上的可用:)。pvpv

在熵很少或没有熵的系统上,使用似乎很慢。我还体验过,VM有时在生成熵时会遇到重大问题。pv /dev/random

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

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);
        }
    }
}

我将bcrypt下载到本地目录。我编译并运行它,如下所示

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

如果你在 runnng 时运行之前的 bash 脚本,你会看到大暂停,系统正在阻塞等待更多的熵。如果运行,您将看到以下内容...RandTeststrace

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

程序正在读取 。测试熵的问题在于,在尝试测试熵(即观察者效应)时,您可能会生成更多熵。/dev/random

修复

第一个修复是从使用更改为ie/dev/random/dev/urandom

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

另一种解决方法是将设备重新创建为设备。你可以找到如何做到这一点从手册页,即,而不是创建它们.../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

我们删除一个并伪造它,即

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

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

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


答案 2

一种可能的解释是 of 导致延迟。SeedGeneratorSecureRandom

弹簧BCrypt实现使用,反过来使用a,反过来可以使用阻塞。以下是这些类的良好描述。SecureRandomSeedGenerator/dev/random

bugreport 还报告了 BCrypt 中的性能问题,并将它们追溯到种子生成器,显示了完整的堆栈跟踪。BCrypt 实现是不同的,但下面的堆栈跟踪必须与 spring 实现相同。他们的解决方案是降低BCrypt的种子重新播种频率。SecureRandom


推荐