英文:
How password hash algorithm for stateless basic authentication api?
问题
我有一个使用spring
框架的Web服务API,它会从一个属性文件中对用户进行身份验证。到目前为止,密码是使用bcrypt
算法进行存储的。
问题:我的API是无状态的,因此任何basic auth
请求都会强制进行bcrypt身份验证的重新计算,从而导致每个请求大约100ms的延迟。
问题:在无状态API的每个请求中都要使用的用户密码加密算法(无论是在属性文件还是数据库中),有哪些建议使用的算法?
重点考虑身份验证性能,但不忽视安全性。
英文:
I have a spring
webservice api that authenticates users from a properties file. The passwords are stored in bcrypt
algorithm so far.
Problem: my api is stateless, thus any basic auth
request will force bcrypt authentication to recalculate, causing delays of approx 100ms on each request.
Question: which algorithm is advised to be used for encrypting user passwords (no matter if in properties file or db), that are to be used on every request on a stateless api?
With focus on authentication performance, but not neglecting security.
答案1
得分: 3
我会跳过加密密码,改用令牌。一个这样的解决方案是JWT(Json Web Tokens),Spring已经默认支持它们。
你可以在这里查看一个示例:这里。
你甚至可以更进一步(这是我的做法),将所有这些都委托给一个OAuth提供者。我通常使用Keycloak来实现这一点。
英文:
I'd skip encrypting the password and use tokens instead. One such solution is JWT (Json Web Tokens) and Spring comes with support for them out of the box.
You can take a look at an example here.
You can go even further (this is what I do) and delegate all of this into an OAuth provider. I usually use Keycloak for this.
答案2
得分: -2
最终,我决定创建一个缓存身份验证处理程序。它会缓存基本身份验证的密码,以及相应的哈希 bcrypt 字符串。但仅在认证成功时才会缓存。
这样,我可以加速已知客户端的有效身份验证(因为我不必为它们重新计算 bcrypt 哈希匹配),但仍然保留了 bcrypt
算法的防暴力破解优势。
如下是 Spring 特定的实现:
static class CachingDelegatingPasswordEncoder implements PasswordEncoder {
private final PasswordEncoder delegate = PasswordEncoderFactories.createDelegatingPasswordEncoder();
private final Map<String, String> cache = new HashMap<>();
@Override
public String encode(CharSequence rawPassword) {
return delegate.encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
String cachedPassword = cache.get(rawPassword);
if (cachedPassword != null && StringUtils.equals(cachedPassword, encodedPassword))
return true;
boolean match = delegate.matches(rawPassword, encodedPassword);
if (match) cache.put(rawPassword.toString(), encodedPassword);
return match;
}
}
@Configuration
static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
DaoAuthenticationProvider p = new DaoAuthenticationProvider();
p.setPasswordEncoder(new CachingDelegatingPasswordEncoder());
p.setUserDetailsService(userDetailsService);
p.afterPropertiesSet();
builder.authenticationProvider(p);
}
}
英文:
In the end I decided to create a caching authentication handler. It cashes the password from basic auth, and the belonging hashed bcrypt string. But only if authentication was successful.
This way I can speed up known clients with valid authentications (as I won't have to recalculate the bcrypt hash match for them), but still keep the brutforce benefits from bcrypt
algorithm.
As follows the spring-specific implementation:
static class CachingDelegatingPasswordEncoder implements PasswordEncoder {
private final PasswordEncoder delegate = PasswordEncoderFactories.createDelegatingPasswordEncoder();
private final Map<String, String> cache = new HashMap<>();
@Override
public String encode(CharSequence rawPassword) {
return delegate.encode(rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
String cachedPassword = cache.get(rawPassword);
if (cachedPassword != null && StringUtils.equals(cachedPassword, encodedPassword))
return true;
boolean match = delegate.matches(rawPassword, encodedPassword);
if (match) cache.put(rawPassword.toString(), encodedPassword);
return match;
}
}
@Configuration
static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
DaoAuthenticationProvider p = new DaoAuthenticationProvider();
p.setPasswordEncoder(new CachingDelegatingPasswordEncoder());
p.setUserDetailsService(userDetailsService);
p.afterPropertiesSet();
builder.authenticationProvider(p);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论