Spring Security | 認証 | BCryptPasswordEncoder: パスワードハッシュ
パスワードをハッシュ化する理由
データが漏洩した場合、パスワードを平文で保存していると非常に危険です。通常、元の文字列を特定できない形式で保存します。復号が不要な場合は、ハッシュ関数を使用します。
BCrypt
MD5やSHAはよく知られたハッシュ関数ですが、パスワードのハッシュ化にはBCryptが便利です。
ハッシュ関数を1回適用するだけでは、総当たり攻撃、辞書攻撃、レインボーテーブルに対して脆弱です。
BCryptはランダムなsaltを追加し、複数回ハッシュ化するため、元のパスワードを推測しにくくします。Spring SecurityもBCryptを推奨しています。
実装
DaoAuthenticationProviderへPasswordEncoderを設定します。BCryptを使用する場合は、BCryptPasswordEncoder実装を使用します。BCryptのハッシュ値は事前に計算して保存します。
XML Configuration
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
...>
...
<bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<sec:authentication-manager>
<sec:authentication-provider>
<sec:user-service>
<sec:user
name="hoge"
password="$2a$08$CekzJRYhb5bzp5mx/eZmX.grG92fRXo267QVVyRs0IE.V.zeCIw8S"
authorities="ROLE_USER" />
</sec:user-service>
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
</beans>
BCryptPasswordEncoderをBeanとして定義し、<password-encoder>のref属性へ指定します。<password-encoder>は<authentication-provider>の子要素として設定します。
Java Configuration
MySpringSecurityConfig.java
package sample.spring.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
public class MySpringSecurityConfig extends WebSecurityConfigurerAdapter {
...
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public void configure(AuthenticationManagerBuilder auth, PasswordEncoder passwordEncoder) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder)
.withUser("hoge")
.password("$2a$08$CekzJRYhb5bzp5mx/eZmX.grG92fRXo267QVVyRs0IE.V.zeCIw8S")
.roles("USER");
}
}
AbstractDaoAuthenticationConfigurerのpasswordEncoder()メソッドへPasswordEncoderを指定します。AbstractDaoAuthenticationConfigurerは、AuthenticationManagerBuilder.inMemoryAuthentication()が返すInMemoryUserDetailsManagerConfigurerの親クラスです。
パスワードのエンコード
パスワードをエンコードするには、コンテナからPasswordEncoderを取得し、encode()を使用します。
EncodePasswordServlet.java
package sample.spring.security.servlet;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/encode-password")
public class EncodePasswordServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(req.getServletContext());
PasswordEncoder passwordEncoder = context.getBean(PasswordEncoder.class);
String password = req.getParameter("password");
String encode = passwordEncoder.encode(password);
System.out.println(encode);
}
}
- 設定は前述と同じで、
BCryptPasswordEncoderをBeanとして登録します。 - 例では動作確認のため
PasswordEncoderを明示的に取得しています。実際にはコンテナ管理Beanへ注入します。 - http://localhost:8080/encode-password?password=fugaへアクセスします。
サーバーコンソール出力
$2a$10$2qVDkAqxp8eDrxR8Be2ZpubYGOCVZ7Qy9uK/XzOIY1ZoxpChtrWDK