स्प्रिंग बूट 2 और स्प्रिंग सिक्योरिटी 5 के साथ मल्टी-फैक्टर प्रमाणीकरण


11

मैं एक कोणीय और स्प्रिंग एप्लिकेशन के लिए TOTP सॉफ्ट टोकन के साथ मल्टी-फैक्टर प्रमाणीकरण जोड़ना चाहता हूं, जबकि स्प्रिंग बूट सिक्योरिटी स्टार्टर के डिफॉल्ट्स के लिए जितना संभव हो सके सब कुछ पास रखता हूं ।

टोकन-सत्यापन स्थानीय रूप से होता है (एरोगियर-ओटीपी-जावा लाइब्रेरी के साथ), कोई तीसरा पक्ष एपीआई प्रदाता नहीं।

उपयोगकर्ता कार्यों के लिए टोकन सेट करना, लेकिन स्प्रिंग सिक्योरिटी ऑथेंटिकेशन मैनेजर / प्रोवाइडर्स का लाभ उठाकर उन्हें मान्य नहीं करता है।

टी एल; डॉ

  • स्प्रिंग बूट सिक्योरिटी स्टार्टर कॉन्फ़िगर सिस्टम में एक अतिरिक्त AuthenticationProvider को एकीकृत करने का आधिकारिक तरीका क्या है ?
  • रिप्ले हमलों को रोकने के लिए अनुशंसित तरीके क्या हैं?

दीर्घ संस्करण

एपीआई में एक एंडपॉइंट /auth/tokenहोता है, जिसमें से सामने वाले को उपयोगकर्ता नाम और पासवर्ड प्रदान करके JWT टोकन मिल सकता है। प्रतिक्रिया भी एक प्रमाणीकरण-स्थिति, जो या तो किया जा सकता है शामिल प्रमाणीकृत या PRE_AUTHENTICATED_MFA_REQUIRED

यदि उपयोगकर्ता को एमएफए की आवश्यकता होती है, तो टोकन जारी किया गया एकल प्राधिकारी PRE_AUTHENTICATED_MFA_REQUIREDऔर 5 मिनट की समाप्ति समय के साथ जारी किया जाता है । यह उपयोगकर्ता को समापन बिंदु तक पहुंचने की अनुमति देता है /auth/mfa-tokenजहां वे अपने प्रमाणक ऐप से टीओटीपी कोड प्रदान कर सकते हैं और साइट तक पहुंचने के लिए पूरी तरह से प्रमाणित टोकन प्राप्त कर सकते हैं।

प्रदाता और टोकन

मैंने अपना रिवाज बनाया है 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);
    }

और OneTimePasswordAuthenticationTokenजो AbstractAuthenticationTokenउपयोगकर्ता नाम (हस्ताक्षरित JWT से लिया गया) और OTP कोड को रखने के लिए विस्तारित होता है।

कॉन्फ़िग

मेरे पास मेरा रिवाज है WebSecurityConfigurerAdapter, जहां मैं अपने रिवाज AuthenticationProviderको जोड़ता हूं http.authenticationProvider()। JavaDoc में आने से यह सही जगह लगता है:

का उपयोग करने के लिए एक अतिरिक्त प्रमाणीकरण जोड़ने की अनुमति देता है

मेरे संबंधित भाग 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

स्प्रिंग सुरक्षा मेरे प्रमाणीकरण प्रदाता को क्यों नहीं उठाती है? नियंत्रक को डीबग करना मुझे दिखाता है कि DaoAuthenticationProviderएकमात्र प्रमाणीकरण प्रदाता है AuthenticationProviderManager

अगर मैं अपने MfaAuthenticationProviderसेम के रूप में उजागर करता हूं , तो यह एकमात्र प्रदाता है जो पंजीकृत है, इसलिए मुझे इसके विपरीत मिलता है:

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

तो, मैं दोनों कैसे प्राप्त करूं?

मेरा प्रश्न

स्प्रिंग बूट सिक्योरिटी स्टार्टर कॉन्फ़िगर सिस्टम AuthenticationProviderमें एक अतिरिक्त को एकीकृत करने के लिए अनुशंसित तरीका क्या है , ताकि मुझे दोनों मिलें, और मेरा अपना रिवाज ? मैं स्प्रिंग बूट स्क्यूरिटी स्टार्टर के डिफॉल्ट्स रखना चाहता हूं और इसके अलावा मेरे खुद के प्रदाता भी हैं।DaoAuthenticationProviderMfaAuthenticationProvider

रिप्ले हमले की रोकथाम

मुझे पता है कि ओटीपी एल्गोरिथ्म समय के स्लाइस जिसमें कोड मान्य है के भीतर रिप्ले हमलों से खुद की रक्षा नहीं करता है; RFC 6238 यह स्पष्ट करता है

पहले OTP के लिए सफल सत्यापन जारी होने के बाद सत्यापनकर्ता को OTP के दूसरे प्रयास को स्वीकार नहीं करना चाहिए, जो एक OTP के केवल एक बार उपयोग को सुनिश्चित करता है।

मैं सोच रहा था कि क्या सुरक्षा को लागू करने का कोई अनुशंसित तरीका है। चूंकि ओटीपी टोकन समय आधारित है इसलिए मैं उपयोगकर्ता के मॉडल पर अंतिम सफल लॉगिन को संग्रहीत करने के बारे में सोच रहा हूं और सुनिश्चित करता हूं कि प्रति 30 सेकंड के समय के स्लाइस में केवल एक सफल लॉगिन है। यह निश्चित रूप से उपयोगकर्ता मॉडल पर सिंक्रनाइज़ेशन का मतलब है। कोई बेहतर तरीका?

धन्यवाद।

-

पुनश्च: चूंकि यह सुरक्षा के बारे में एक प्रश्न है, जिसे मैं विश्वसनीय और / या आधिकारिक स्रोतों से उत्तर आरेखण के लिए देख रहा हूं। धन्यवाद।

जवाबों:


0

अपने स्वयं के प्रश्न का उत्तर देने के लिए, इस तरह से मैंने इसे लागू किया, आगे के शोध के बाद।

मेरे पास एक प्रदाता है जो एक पूजो के रूप में लागू होता है AuthenticationProvider। यह जानबूझकर बीन / घटक नहीं है। अन्यथा स्प्रिंग इसे एकमात्र प्रदाता के रूप में पंजीकृत करेगा।

public class MfaAuthenticationProvider implements AuthenticationProvider {
    private final AccountService accountService;

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

अपने SecurityConfig में, मैंने स्प्रिंग को ऑटोवॉयर करने दिया 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 के दिए गए प्राधिकारी के साथ पूर्व प्रमाणित हैं । यह उन्हें एक ही endpoint का उपयोग करने की अनुमति देता है /auth/mfa-token। यह समापन बिंदु मान्य JWT और प्रदान की गई TOTP से उपयोगकर्ता नाम लेता है और इसे authenticate()प्रमाणीकरण प्रबंधक की विधि को भेजता है , जो MfaAuthenticationProviderइसे संभाल सकता है के रूप में चुनता है OneTimePasswordAuthenticationToken

    var authenticationToken = new OneTimePasswordAuthenticationToken(usernameFromJwt, providedOtp);
    var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.