Spring Boot 2およびSpring Security 5を使用した多要素認証


11

Spring Boot Security Starterのデフォルトにできるだけ近づけながら、Angular&SpringアプリケーションにTOTPソフトトークンを使用した多要素認証を追加したいと思います。

トークンの検証はローカルで行われ(aerogear-otp-javaライブラリを使用)、サードパーティのAPIプロバイダーは使用されません。

ユーザーのトークンの設定は機能しますが、Spring Security Authentication Manager / Providersを利用してトークンを検証することはできません。

TL; DR

  • 追加のAuthenticationProviderをSpring Boot Security Starter構成済みシステムに統合する公式の方法は何ですか?
  • リプレイ攻撃を防ぐために推奨される方法は何ですか?

ロングバージョン

APIにはエンドポイント/auth/tokenがあり、そこからフロントエンドはユーザー名とパスワードを提供することでJWTトークンを取得できます。応答には認証ステータスも含まれ、AUTHENTICATEDまたはPRE_AUTHENTICATED_MFA_REQUIREDのいずれかになります。

ユーザーがMFAを必要とする場合、トークンは、許可された単一の権限PRE_AUTHENTICATED_MFA_REQUIREDと5分の有効期限で発行されます。これにより、ユーザーはエンドポイントにアクセスして/auth/mfa-token、AuthenticatorアプリからTOTPコードを提供し、完全に認証されたトークンを取得してサイトにアクセスできます。

プロバイダーとトークン

私はMfaAuthenticationProvider実装する私のカスタムを作成しましたAuthenticationProvider

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // validate the OTP code
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return OneTimePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

そして、(署名されたJWTから取得した)ユーザー名とOTPコードを保持するようOneTimePasswordAuthenticationTokenに拡張AbstractAuthenticationTokenされたです。

構成

私は私のカスタム持ってWebSecurityConfigurerAdapter、私は私のカスタムを追加し、AuthenticationProvider経由しますhttp.authenticationProvider()。JavaDocによると、これは正しい場所のようです:

使用する追加のAuthenticationProviderを追加できます

私の関連部分はSecurityConfigこのように見えます。

    @Configuration
    @EnableWebSecurity
    @EnableJpaAuditing(auditorAwareRef = "appSecurityAuditorAware")
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        private final TokenProvider tokenProvider;

        public SecurityConfig(TokenProvider tokenProvider) {
            this.tokenProvider = tokenProvider;
        }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authenticationProvider(new MfaAuthenticationProvider());

        http.authorizeRequests()
            // Public endpoints, HTML, Assets, Error Pages and Login
            .antMatchers("/", "favicon.ico", "/asset/**", "/pages/**", "/api/auth/token").permitAll()

            // MFA auth endpoint
            .antMatchers("/api/auth/mfa-token").hasAuthority(ROLE_PRE_AUTH_MFA_REQUIRED)

            // much more config

コントローラ

AuthControllerAuthenticationManagerBuilder注射し、すべて一緒にそれを引っ張っています。

@RestController
@RequestMapping(AUTH)
public class AuthController {
    private final TokenProvider tokenProvider;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    public AuthController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) {
        this.tokenProvider = tokenProvider;
        this.authenticationManagerBuilder = authenticationManagerBuilder;
    }

    @PostMapping("/mfa-token")
    public ResponseEntity<Token> mfaToken(@Valid @RequestBody OneTimePassword oneTimePassword) {
        var username = SecurityUtils.getCurrentUserLogin().orElse("");
        var authenticationToken = new OneTimePasswordAuthenticationToken(username, oneTimePassword.getCode());
        var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

        // rest of class

ただし、投稿に対して/auth/mfa-tokenこのエラーが発生します。

"error": "Forbidden",
"message": "Access Denied",
"trace": "org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for de.....OneTimePasswordAuthenticationToken

Spring Securityが認証プロバイダーを取得しないのはなぜですか?コントローラをデバッグするDaoAuthenticationProviderと、で唯一の認証プロバイダーであることがわかりますAuthenticationProviderManager

MfaAuthenticationProviderをBeanとして公開すると、登録されているのは唯一のプロバイダーなので、反対になります。

No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken. 

それで、どうすれば両方を入手できますか?

私の質問

追加を統合するお勧めの方法は何であるAuthenticationProvider春ブーツセキュリティスターター構成されたシステムは、私は両方を取得していること、DaoAuthenticationProviderそして私自身のカスタムMfaAuthenticationProviderSpring Boot Scurity Starterのデフォルトを維持し、さらに独自のプロバイダーを追加したいと考えています。

リプレイ攻撃の防止

OTPアルゴリズム自体は、コードが有効なタイムスライス内のリプレイアタックから保護されないことを知っています。RFC 6238はこれを明確にしています

検証者は、最初のOTPに対して検証が正常に発行された後、OTPの2回目の試行を受け入れてはいけません。

保護を実装するための推奨される方法があるかどうか疑問に思っていました。OTPトークンは時間ベースなので、最後に成功したログインをユーザーのモデルに保存し、30秒のタイムスライスごとに1回だけ成功したログインがあることを確認します。もちろん、これはユーザーモデルの同期を意味します。より良いアプローチはありますか?

ありがとうございました。

-

PS:これはセキュリティに関する質問なので、信頼できる情報源や公式な情報源からの回答を探しています。ありがとうございました。

回答:


0

私自身の質問に答えるために、これは私がそれをどのように実装したか、さらに調査した後です。

私はを実装するポージョとしてプロバイダーを持っていますAuthenticationProvider。意図的にBean /コンポーネントではありません。それ以外の場合、Springはそれを唯一のプロバイダーとして登録します。

public class MfaAuthenticationProvider implements AuthenticationProvider {
    private final AccountService accountService;

    @Override
    public Authentication authenticate(Authentication authentication) {
        // here be code 
        }

私のSecurityConfigで、Springに自動ワイヤリングさせAuthenticationManagerBuilder、手動でMfaAuthenticationProvider

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
       private final AuthenticationManagerBuilder authenticationManagerBuilder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // other code  
        authenticationManagerBuilder.authenticationProvider(getMfaAuthenticationProvider());
        // more code
}

// package private for testing purposes. 
MfaAuthenticationProvider getMfaAuthenticationProvider() {
    return new MfaAuthenticationProvider(accountService);
}

標準認証の後、ユーザーがMFAを有効にしている場合、ユーザーはPRE_AUTHENTICATED_MFA_REQUIREDの付与された権限で事前認証されます。これにより、ユーザーは単一のエンドポイントにアクセスできます/auth/mfa-token。このエンドポイントは、有効なJWTと提供されたTOTPからユーザー名を取得し、それをauthenticate()authenticationManagerBuilder のメソッドに送信します。authenticationBuilder は、MfaAuthenticationProvider処理可能なを選択しOneTimePasswordAuthenticationTokenます。

    var authenticationToken = new OneTimePasswordAuthenticationToken(usernameFromJwt, providedOtp);
    var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.