为啥 Spring boot Security Basic Authentication 慢?

Posted

技术标签:

【中文标题】为啥 Spring boot Security Basic Authentication 慢?【英文标题】:Why is Spring boot Security Basic Authentication slow?为什么 Spring boot Security Basic Authentication 慢? 【发布时间】:2019-01-17 12:51:14 【问题描述】:

我有一个 Spring boot 2.0.1 服务,我添加了使用 BCrypt 进行散列的基本身份验证。但是这项服务在添加基本身份验证之前平均需要 400 毫秒,现在需要超过 1 秒。我正在使用用户详细信息服务,它在哈希映射中查找发送的用户名并返回用户详细信息。我尝试将 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 编码器使应用程序变慢。我发现一些 Stack Overflow 的答案讨论 BCrypt 哈希计算是一个阻塞调用。

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

【问题讨论】:

您是否对您的应用程序进行了基准测试?你知道哪里它很慢吗?说“我有一个非常复杂的黑匣子。我在另一个非常复杂的黑匣子中运行它。我正在另一个非常复杂的黑匣子中运行它。”对人们提供体面的答案没有帮助。看看one provided - 这基本上是一个没有根据的猜测。 同意。我运行了基准测试并更新了问题。 BCrypt 应该很慢。因此,如果是 BCrypt 让事情变慢,那么这就是预期的行为。 我遇到了这个 SO 问题:***.com/questions/36471723/…您认为这是预期行为还是 Spring 安全 BCrypt 实现问题? @BoristheSpider 另外,我尝试使用 SCrypt 编码器代替 BCrypt,它的平均性能为 300 毫秒,而 BCrypt 为 1000 毫秒。从我读到现在的内容来看,SCrypt 虽然是最近才出现的,但它是一个很好的散列算法。所以只是想知道 SCrypt 是否旨在提供良好的性能和安全性? 【参考方案1】:

所以在搜索了很多关于BCrypt编码和解码的信息后,我终于找到了保持spring boot项目性能的解决方案,而不会因BCrypt编码和解码造成大的延迟。

所以 BCrypt 散列算法适用于某些轮次。您在 BCrypt 编码中使用的轮数越多,您的项目将消耗更多的空间和内存来进行编码和解码(密码)。

话虽如此,我还想提一下,您在编码密码时使用的轮数越多,它就越安全。

我们大多数人会使用下面这些代码行在我们的代码中生成密码凭证

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 成本并不意味着购买更大的服务器,并且很快)。这在很大程度上取决于操作环境:涉及哪些机器,每秒有多少用户身份验证......所以没有一刀切的响应。

【讨论】:

【参考方案2】:

前段时间,我在为客户处理项目时遇到了与服务器相同的问题。 我用 jvisualvm 分析了服务。结果真的很清楚:

我使用 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。

我认为:

    如果性能和安全性很重要:选择最安全的算法并设计您的服务,使其可以通过添加更多机器来扩展。 如果性能比安全更重要:选择对您的要求足够快的算法。在我的例子中是 SHA-256。

但你应该始终努力实现 1。

我展示的测量值应该被解释为相对的,而不是被视为绝对值。

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

【讨论】:

感谢您的信息!在您的测试中为 BCrypt 设置了多少轮哈希(强度)?是 Spring Boot 的标准 10 还是一些自定义值?【参考方案3】:

您正在创建一个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 实例比一直使用同一个实例更安全。

【讨论】:

不确定在使用 BCrypt 方面有哪些好的做法。到目前为止,我看到的所有文档都没有明确提供 Secure Random。 @Ketan 这个建议可能影响不大,但不会影响 BCrypt 的安全性,所以请随意尝试。 作为附加说明。安全哈希算法必须很慢,因为这种缓慢可以防止暴力攻击。【参考方案4】:

较慢的哈希函数不会对可用性产生很大影响,但可以更好地防止暴力攻击。

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

【讨论】:

以上是关于为啥 Spring boot Security Basic Authentication 慢?的主要内容,如果未能解决你的问题,请参考以下文章

BCryptPasswordEncoder 未作为 spring-boot-starter-security 的一部分导入

基于 SAML 的 SSO 用于身份验证和 LDAP 用于授权 - Spring Boot Security

Spring Security with Boot

spring boot Security 简单使用

Spring Boot Security

Understand Spring Security Architecture and implement Spring Boot Security