我们在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秒。这没有明显的原因。
更多信息:
之前有其他人见过这个问题吗?
答案 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>可能使用阻止SeedGenerator
。 Here是对这些课程的一个很好的描述。
bugreport还会报告BCrypt中的性能问题,并将它们追溯到种子生成器,显示完整的堆栈跟踪。 BCrypt实现不同,但/dev/random
以下的堆栈跟踪必须与spring实现相同。他们的解决方案是降低BCrypt的重新种植频率。
答案 2 :(得分:0)
更改为urandom标签仅在JDK8或更高版本上有效,我们面对了很长时间,在1.7中更改为urandom并没有帮助,但在1.8中确实解决了该问题。
答案 3 :(得分:0)
对于遇到同样问题的任何人,我们都通过安装rng-tools
解决了此问题