가능한 한 모든 것을 Spring Boot Security Starter 의 기본값에 가깝게 유지하면서 TOTP 소프트 토큰을 사용하는 다단계 인증을 Angular & Spring 애플리케이션에 추가하고 싶습니다 .
토큰 유효성 검사는 타사 API 공급자가 아닌 로컬 (aerogear-otp-java 라이브러리 사용)에서 발생합니다.
사용자를위한 토큰 설정은 작동하지만 Spring Security Authentication Manager / 제공자를 활용하여 유효성을 검증하는 것은 아닙니다.
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
제어 장치
는 AuthController
있다 AuthenticationManagerBuilder
주입 모두 함께 당기는.
@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
내 자신의 정의 MfaAuthenticationProvider
? Spring Boot Scurity Starter 의 기본값을 유지하고 추가로 자체 공급자를 갖고 싶습니다.
리플레이 공격 방지
OTP 알고리즘 자체가 코드가 유효한 타임 슬라이스 내에서 재생 공격으로부터 보호하지 못한다는 것을 알고 있습니다. RFC 6238이이를 명확하게합니다.
검증자는 첫 번째 OTP에 대해 성공적인 검증이 발행 된 후 OTP의 두 번째 시도를 수락해서는 안되며, 이는 OTP의 일회성 사용을 보장합니다.
보호를 구현하는 권장 방법이 있는지 궁금합니다. OTP 토큰은 시간 기반이므로 사용자의 모델에 마지막으로 성공한 로그인을 저장하고 30 초마다 하나의 성공적인 로그인이 있는지 확인하려고합니다. 이것은 물론 사용자 모델에서의 동기화를 의미합니다. 더 나은 접근 방법이 있습니까?
감사합니다.
-
추신 : 이것은 보안에 관한 질문이므로 신뢰할 수 있고 공식적인 출처에서 답을 찾고 있습니다. 감사합니다.