การรับรองความถูกต้องแบบหลายปัจจัยพร้อม Spring Boot 2 และ Spring Security 5


11

ฉันต้องการเพิ่มตรวจสอบปัจจัยหลายพร้อมด้วยสัญญาณอ่อน TOTP แอพลิเคชันเชิงมุมและฤดูใบไม้ผลิขณะที่การรักษาทุกอย่างใกล้เคียงเป็นไปได้ที่จะเป็นค่าเริ่มต้นของฤดูใบไม้ผลิ Boot รักษาความปลอดภัยเริ่มต้น

การตรวจสอบความถูกต้องของโทเค็นเกิดขึ้นภายในเครื่อง (ด้วยไลบรารี aerogear-otp-java) ไม่มีผู้ให้บริการ API บุคคลที่สาม

การตั้งค่าโทเค็นสำหรับผู้ใช้ทำงานได้ แต่การตรวจสอบความถูกต้องโดยการใช้ประโยชน์จาก Spring Security Authentication Manager / Providers ไม่ได้

TL; DR

  • วิธีการอย่างเป็นทางการในการรวม AuthenticationProvider เพิ่มเติมเข้ากับระบบกำหนดค่าเริ่มต้นของ Spring Boot Securityคืออะไร
  • วิธีที่แนะนำในการป้องกันการโจมตีซ้ำคืออะไร?

รุ่นยาว

API มีจุดปลายทาง/auth/tokenที่ส่วนหน้าสามารถรับโทเค็น JWT ได้โดยระบุชื่อผู้ใช้และรหัสผ่าน การตอบสนองยังรวมถึงการตรวจสอบสถานะซึ่งสามารถทั้งรับรองความถูกต้องหรือPRE_AUTHENTICATED_MFA_REQUIRED

หากผู้ใช้ต้องการ MFA โทเค็นจะถูกออกด้วยสิทธิ์ที่ได้รับเพียงครั้งเดียวPRE_AUTHENTICATED_MFA_REQUIREDและเวลาหมดอายุ 5 นาที สิ่งนี้ช่วยให้ผู้ใช้สามารถเข้าถึงจุดปลาย/auth/mfa-tokenซึ่งพวกเขาสามารถให้รหัส TOTP จากแอพ Authenticator และรับโทเค็นที่ได้รับการรับรองความถูกต้องครบถ้วนเพื่อเข้าถึงเว็บไซต์

ผู้ให้บริการและโทเค็น

ฉันได้สร้างประเพณี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);
    }

และ a OneTimePasswordAuthenticationTokenซึ่งขยายAbstractAuthenticationTokenเพื่อเก็บชื่อผู้ใช้ (นำมาจาก JWT ที่ลงนามแล้ว) และรหัส OTP

การกำหนดค่า

ฉันมีกำหนดเองของฉัน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 จึงไม่มารับผู้ให้บริการรับรองความถูกต้องของฉัน แก้จุดบกพร่องแสดงให้เห็นว่าการควบคุมผมว่าเป็นผู้ให้บริการการตรวจสอบสิทธิ์เฉพาะในDaoAuthenticationProviderAuthenticationProviderManager

หากฉันเปิดเผยMfaAuthenticationProviderว่าเป็น bean ผู้ให้บริการรายเดียวเท่านั้นที่ลงทะเบียนดังนั้นฉันจึงได้สิ่งที่ตรงกันข้าม:

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

ดังนั้นฉันจะได้รับทั้งสองได้อย่างไร

คำถามของฉัน

อะไรคือวิธีที่แนะนำในการรวมระบบเพิ่มเติมAuthenticationProviderเข้าไปในระบบที่กำหนดค่าของSpring Boot Security Starterเพื่อให้ฉันได้รับทั้งDaoAuthenticationProviderกำหนดเองและของฉันเองMfaAuthenticationProvider? ฉันต้องการเก็บค่าเริ่มต้นของSpring Boot Scurity Starterและให้ผู้ให้บริการของฉันเองเพิ่มเติม

การป้องกันการโจมตี Replay

ฉันรู้ว่าอัลกอริทึม OTP นั้นไม่ได้ป้องกันตัวเองจากการโจมตีซ้ำในเวลาที่รหัสถูกต้อง; RFC 6238 ทำให้สิ่งนี้ชัดเจน

ตัวตรวจสอบต้องไม่ยอมรับความพยายามครั้งที่สองของ OTP หลังจากการตรวจสอบความถูกต้องสำเร็จสำหรับ OTP แรกซึ่งทำให้มั่นใจได้ว่าจะใช้ OTP ครั้งเดียวเท่านั้น

ฉันสงสัยว่ามีวิธีแนะนำในการใช้การป้องกันไหม เนื่องจากโทเค็น OTP นั้นเป็นไปตามเวลาฉันจึงคิดว่าจะเก็บการเข้าสู่ระบบที่ประสบความสำเร็จครั้งล่าสุดในรูปแบบของผู้ใช้และทำให้แน่ใจว่ามีการเข้าสู่ระบบที่สำเร็จเพียงครั้งเดียวต่อการแบ่งเวลา 30 วินาที หลักสูตรนี้หมายถึงการซิงโครไนซ์กับรุ่นผู้ใช้ มีวิธีใดที่ดีกว่านี้?

ขอบคุณ.

-

PS: เนื่องจากนี่เป็นคำถามเกี่ยวกับความปลอดภัยฉันกำลังมองหาคำตอบจากแหล่งข้อมูลที่น่าเชื่อถือและ / หรือเป็นทางการ ขอบคุณ.

คำตอบ:


0

เพื่อตอบคำถามของฉันนี่คือวิธีที่ฉันใช้มันหลังจากการวิจัยเพิ่มเติม

ฉันมีให้บริการเป็น POJO AuthenticationProviderว่าการดำเนินการที่ มันไม่ได้เป็น Bean / Component อย่างจงใจ Spring จะลงทะเบียนเป็นผู้ให้บริการรายเดียวเท่านั้น

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);
}

หลังจากการตรวจสอบมาตรฐานหากผู้ใช้มีการเปิดใช้ไอ้เวรตะไลพวกเขาจะรับรองความถูกต้องก่อนที่มีอำนาจได้รับของPRE_AUTHENTICATED_MFA_REQUIRED สิ่งนี้ทำให้พวกเขาสามารถเข้าถึงจุดปลายเดียว, /auth/mfa-token. จุดปลายนี้ใช้ชื่อผู้ใช้จาก JWT ที่ถูกต้องและ TOTP ที่ให้มาและส่งไปยังauthenticate()วิธีการของ authenticationManagerBuilder ซึ่งเลือกสิ่งMfaAuthenticationProviderที่มันสามารถจัดการOneTimePasswordAuthenticationTokenได้

    var authenticationToken = new OneTimePasswordAuthenticationToken(usernameFromJwt, providedOtp);
    var authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.