为什么Spring Boot Security基本身份验证速度很慢?

时间:2018-08-10 00:40:12

标签: java spring-boot spring-security basic-authentication bcrypt

我有一个Spring boot 2.0.1服务,在其中添加了使用BCrypt进行哈希处理的基本身份验证。但是,此服务以前平均每次添加400毫秒,然后才添加基本身份验证,现在花费的时间超过1秒。我正在使用用户详细信息服务,该服务在哈希图中查找发送的用户名并返回UserDetails。我尝试将BCrypt舍入减少到4,但这并没有太大的区别。

以前,我启用了无状态身份验证,后来又禁用了该功能,但是性能仍然很差。该服务托管在Docker容器中。

下面是我的安全配置。

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private UserDetailsService userDetailsService;

    @Autowired
    public SecurityConfig(UserDetailsServiceImpl service) {
        this.userDetailsService = service;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        Map encoders = new HashMap<>();
        encoders.put(BCRYPT_ID, new BCryptPasswordEncoder(BCRYPT_ROUNDS));
        return new DelegatingPasswordEncoder(BCRYPT_ID,encoders);
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.cors()
            .and()
            .csrf().disable()
            .httpBasic();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
}

请让我知道我是否缺少任何东西。

更新:我运行了基准测试,看起来BCrypt编码器正在使应用程序变慢。我发现一些堆栈溢出答案,讨论BCrypt哈希计算是一个阻塞调用。

关于硬件:服务主机具有Intel Xeon E5、16 GB内存。它托管4个Spring Boot服务,每个服务分配给2 GB的内存,并在Docker容器中运行。

4 个答案:

答案 0 :(得分:2)

您正在创建一个BCryptPasswordEncoder实例而不传递一个SecureRandom。因此,每次encode密码BCrypt都会创建一个SecureRandom的新实例(这是CPU密集操作,需要生成盐)。您可以检查BCrypt.class源代码。

public static String gensalt(int log_rounds) {
    return gensalt(log_rounds, new SecureRandom());
}

public static String gensalt() {
    return gensalt(10);
}

public static String gensalt(int log_rounds, SecureRandom random) {
    ...
}

还有BCryptPasswordEncoder.class

    if (this.random != null) {
        salt = BCrypt.gensalt(this.strength, this.random);
    } else {
        salt = BCrypt.gensalt(this.strength);
    }

因此,请使用public BCryptPasswordEncoder(int strength, SecureRandom random)构造函数,但请记住,每次创建SecureRandom实例比始终使用同一实例更安全。

答案 1 :(得分:1)

较慢的哈希函数不会对可用性造成很大影响,但可以更好地保护其免受暴力攻击。

https://security.stackexchange.com/questions/150620/what-is-the-purpose-of-slowing-down-the-calculation-of-a-password-hash

答案 2 :(得分:1)

因此,在搜索了很多有关BCrypt编码和解码的信息之后,我终于找到了一种解决方案,可以在不因BCrypt编码和解码引起较大延迟的情况下保持spring boot项目的性能。

因此BCrypt哈希算法在某些回合中有效。 BCrypt编码中使用的回合数量越多,项目用于编码和解码(密码)所消耗的空间和内存就越大。

话虽如此,我也想提一提,您在编码密码时使用的回合次数越多,密码的安全性就越强。

我们大多数人将使用以下这些代码行在我们的代码中生成bycrypt凭据

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16); // here no of rounds is 16
String result = encoder.encode("password");
system.out.println("encoded password" + result );

此Java代码用于生成BCrypt编码密码的轮数为16,这太高了。可以帮助在时间,内存和安全性之间取得平衡的标准轮次是10。

因此,如果我们需要更改下面的BCrypt编码中的回合数,则需要设置

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10);

当我使用16个回合时,我花了6秒钟才能通过基本身份验证打入该服务(编码和解码BCrypt密码平均需要3到4秒钟)

当我使用10发子弹时,我平均能够在1到1.5秒之间找到服务

您没有标准的回合数。您应在应用程序中使用性能允许的最大回合数。轮数是一个减慢因子,您可以在正常使用情况下使用这种减慢因子,这种减慢对您的影响微不足道(用户不会看到它,额外的CPU成本并不意味着购买更大的服务器,并且依此类推)。这在很大程度上取决于操作上下文:涉及哪些计算机,每秒多少用户身份验证...,因此没有一种千篇一律的响应。

答案 3 :(得分:1)

前一段时间,我在为客户处理项目时遇到服务器问题。 我使用jvisualvm分析了该服务。结果非常清楚:

bcrypt jvisualvm sampling

我使用jmeter对服务的api发出了5000个请求。我还为每次测量重新启动了该服务,并始终运行5000个样本以在每次测量之前预热JIT编译器。

我的结果是这样的:

Samples     |    avg [ms]|    min [ms]|    max [ms]| throughput [requests/s]
------------+------------+------------+------------+------------------------
    5000    |         125|          71|         363|                    78.1    
------------+------------+------------+------------+------------------------

我分析的服务是一个非常简单的数据访问服务,仅通过REST接口提供对数据库的访问。因此,我可以通过jmeter测试找出bcrypt的影响有多大。

然后我使用不同的密码编码器(例如MD5,SHA-256,bcrypt,scrypt和pbkdf2)运行多个jmeter测试。

以下是我的一些度量标准,可以帮助您做出决定或进一步调查:

algorithm   |    avg [ms]|    min [ms]|    max [ms]| throughput [requests/s]
------------+------------+------------+------------+------------------------
MD5         |           5|           1|          61|                    1443    
------------+------------+------------+------------+------------------------
SHA-256     |           5|           2|          34|                    1464    
------------+------------+------------+------------+------------------------
bcrypt      |         125|          71|         363|                    78.1    
------------+------------+------------+------------+------------------------
scrypt      |         122|          54|        1232|                    79.2    
------------+------------+------------+------------+------------------------
pbkdf2      |         833|         421|        1606|                      12    
------------+------------+------------+------------+------------------------

我分析的服务未公开到互联网。它在安全的网络区域中运行,其性能比安全性更为关键。因此,我们决定使用SHA-256代替bcrypt。

我认为:

  1. 如果性能和安全性很重要:选择最安全的算法,并通过添加更多计算机来扩展服务的方式设计服务。
  2. 如果性能比安全更重要:请选择足够快的算法来满足您的要求。在我的情况下为SHA-256。

但是您应该始终尝试实现1。

我显示的测量值应相对解释,而不应视为绝对值。

PS: 我知道最好单独执行性能测试以测试加密api,而不是通过服务的REST接口对其进行测试,但是我还没有时间设置这种测试。希望我将来有更多时间进行调查,如果可以,我会回来更新此答案。