BCrypt性能恶化

时间:2016-04-07 09:17:32

标签: 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));

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

之前有其他人见过这个问题吗?

4 个答案:

答案 0 :(得分:12)

  

问题:当服务器重新启动时,Bcrypt.hashpw会显示   100ms解密密码。但是过了一段时间(没有模式)   Bcrypt.hashpw表现突然从100毫秒上升到10秒。   这没有明显的原因。

问题是/dev/random有时会阻塞,当它出现时它似乎是随机的:)更令人困惑的是,在尝试测试它的工作原理时,你会遇到观察者效应,即在尝试观察随机行为时,你会产生熵,这可能导致大量混乱,即我的结果不会与你的结果相同等。这也是为什么它看起来像是没有图案..

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

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

cat /proc/sys/kernel/random/entropy_avail

如果您运行以下bash脚本并同时监控entropy_avail,那么您将会这样做 请注意,当bash脚本使用它时,熵会急剧下降。

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

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

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

pv /dev/random

如果让pv运行它有效,它会消耗随机字节流,这意味着其他服务可能会开始阻塞。请注意pv也显示了它的输出,因此它也可能在系统上增加可用的en :)。

在使用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);
        }
    }
}

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

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

如果您在runnng RandTest之前运行之前的bash脚本,您将看到系统阻塞等待更多熵的大停顿。如果您运行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

该程序正在从/dev/random读取。测试熵的问题是 你可能会在尝试测试时产生更多,即观察者效应。

修复

第一个问题是从使用/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

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

答案 1 :(得分:4)

一种可能的解释是SeedGenerator的{​​{1}}导致了延迟。

Springs BCrypt实现uses SecureRandom反过来使用SecureRandom,而{em>可能使用阻止SeedGeneratorHere是对这些课程的一个很好的描述。

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

答案 2 :(得分:0)

更改为urandom标签仅在JDK8或更高版本上有效,我们面对了很长时间,在1.7中更改为urandom并没有帮助,但在1.8中确实解决了该问题。

答案 3 :(得分:0)

对于遇到同样问题的任何人,我们都通过安装rng-tools

解决了此问题