MFA(Multi-Factor Authentication)이란 무엇인가요?

김상욱·2024년 12월 31일
0

MFA(Multi-Factor Authentication)이란 무엇인가요?

다단계 인증(MFA)은 사용자가 시스템에 접근할 때 두 가지 이상의 인증 요소를 요구하는 보안 절차입니다. 이는 단순히 아이디와 비밀번호를 사용하는 단일 인증(Single-Factor-Authentication, SFA)에 비해 보안을 크게 강화합니다.

인증 요소의 종류

MFA는 보통 다음 세 가지 인증 요소 중 두 가지 이상을 결합하여 사용합니다.
1. 지식 요소(Something you know) : ex) 비밀번호, PIN, 보안 질문 등
2. 소유 요소(Something you have) : ex) 스마트폰, 보안, 토큰, OTP 생성기 등
3. 고유 요소(Something you are) : ex) 지문, 얼굴 인식, 음성 인식 등

왜 MFA가 중요한가?

  • 보안 강화 : 비밀번호가 유출되더라도 추가 인증 요소가 필요하기 때문에 무단 접근을 방지할 수 있습니다.
  • 데이터 보호 : 민감한 사용자 데이터나 시스템에 대한 접근을 보호하여 데이터 유출을 막습니다.
  • 규제 준수 : 많은 산업 분야에서 MFA 사용을 법적으로 요구하고 있습니다.

Java/Spring 환경에서 MFA 구현하기

  1. 사용자 인증 흐름 설계 : 사용자가 로그인 시도 시, 첫 번째 인증 단계. 비밀번호가 맞으면 두 번째 인증 단계로 이동
  2. Spring Security 활용 : Spring Security는 인증 및 권한 부여를 관리하는 강력한 프레임워크. 기본적인 인증 과정을 설정한 후, 추가적인 MFA 단계를 구현할 수 있습니다.
  3. OTP(One-Time Password) 생성 및 검증 : Google Authenticator나 Authy와 같은 OTP 앱을 사용할 수 있습니다. Java 라이브러리인 Google Authenticator API 등을 사용하여 서버에서 OTP를 생성하고 검증할 수 있습니다.
  4. SMS 또는 이메일을 통한 인증 : 사용자의 휴대폰 번호나 이메일 주소로 인증 코드를 전송하는 방법입니다. Twilio나 SendGrid와 같은 서비스 API를 활용할 수 있습니다.
  5. 2단계 인증 예제 코드(간단한 개념):
// 예시: Spring Security 설정 클래스
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login", "/mfa").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .successHandler(authenticationSuccessHandler())
                .and()
            .addFilterAfter(mfaFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new CustomAuthenticationSuccessHandler();
    }

    @Bean
    public OncePerRequestFilter mfaFilter() {
        return new MFAFilter();
    }
}
// 예시: MFA 필터
public class MFAFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
                                    throws ServletException, IOException {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && auth.isAuthenticated() && !isMFACompleted(auth)) {
            response.sendRedirect("/mfa");
            return;
        }
        filterChain.doFilter(request, response);
    }

    private boolean isMFACompleted(Authentication auth) {
        // MFA 완료 여부 확인 로직
        return (boolean) auth.getDetails();
    }
}

취업 준비를 하시는 신입 Java/Spring 백엔드 개발자 분께 실습을 통해 MFA(Multi-Factor Authentication)를 직접 구현해보는 것은 매우 유익한 경험이 될 것입니다. 아래에 단계별로 따라 할 수 있는 실습 예제를 제안드릴게요. 이 실습을 통해 이론을 실제로 적용해보며 이해도를 높일 수 있습니다.

실습 목표

  1. 기본 사용자 인증 시스템 구축: Spring Boot와 Spring Security를 사용하여 사용자 등록 및 로그인 기능 구현.
  2. MFA 추가: OTP(One-Time Password)를 사용한 2단계 인증 구현.
  3. OTP 전송 방식 구현: 이메일 또는 SMS를 통해 OTP 전송.
  4. 테스트 및 검증: Postman이나 간단한 프론트엔드를 사용하여 기능 테스트.

사전 준비

  • 개발 환경 설정: Java JDK, Maven 또는 Gradle, IDE(IntelliJ IDEA 추천)
  • 이메일/SMS 서비스 계정: 예를 들어, 이메일 전송을 위해 SendGrid 또는 SMS 전송을 위해 Twilio 계정 생성
  • 기본적인 Spring Boot 애플리케이션 이해

1. 기본 사용자 인증 시스템 구축

1.1 Spring Boot 프로젝트 생성

  • Spring Initializr를 사용하여 새로운 Spring Boot 프로젝트 생성
    • Dependencies: Spring Web, Spring Security, Spring Data JPA, H2 Database (또는 다른 DB), Lombok

1.2 사용자 엔티티 및 레포지토리 생성

// User.java
@Entity
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    private String email;
    // getters and setters
}
// UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
}

1.3 Spring Security 설정

// SecurityConfig.java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private UserDetailsService userDetailsService;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/register", "/login").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll();
    }
}

1.4 사용자 등록 및 로그인 API 구현

// AuthController.java
@RestController
public class AuthController {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody User user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        userRepository.save(user);
        return ResponseEntity.ok("User registered successfully");
    }
    
    // 로그인은 Spring Security가 처리
}

2. MFA 추가: OTP 기반 2단계 인증 구현

2.1 OTP 생성 및 검증 로직 구현

  • Google Authenticator 라이브러리 사용 예시
  • 의존성 추가 (pom.xml)
<dependency>
    <groupId>com.warrenstrange</groupId>
    <artifactId>googleauth</artifactId>
    <version>1.4.0</version>
</dependency>

2.2 OTP 서비스 구현

// OtpService.java
@Service
public class OtpService {
    private final GoogleAuthenticator gAuth = new GoogleAuthenticator();
    
    public String generateSecretKey() {
        final GoogleAuthenticatorKey key = gAuth.createCredentials();
        return key.getKey();
    }
    
    public boolean verifyCode(String secret, int code) {
        return gAuth.authorize(secret, code);
    }
}

2.3 MFA 플로우 구현

  • 사용자 로그인 시도 후 OTP 입력 단계 추가
// CustomAuthenticationSuccessHandler.java
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    
    @Autowired
    private OtpService otpService;
    
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        // 로그인 성공 후 OTP 단계로 리다이렉트
        response.sendRedirect("/mfa");
    }
}
// MFAController.java
@RestController
public class MFAController {
    
    @Autowired
    private OtpService otpService;
    
    @PostMapping("/mfa")
    public ResponseEntity<?> verifyOtp(@RequestParam String username, @RequestParam int otp) {
        User user = userRepository.findByUsername(username)
                      .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        // 사용자별 비밀키 저장 필요 (예: DB에 저장)
        boolean isValid = otpService.verifyCode(user.getSecretKey(), otp);
        if(isValid){
            return ResponseEntity.ok("MFA Success");
        } else {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid OTP");
        }
    }
}

3. OTP 전송 방식 구현: 이메일을 통한 OTP 전송

3.1 이메일 전송을 위한 설정

  • 의존성 추가 (pom.xml)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

3.2 이메일 설정 (application.properties)

spring.mail.host=smtp.sendgrid.net
spring.mail.port=587
spring.mail.username=your_sendgrid_username
spring.mail.password=your_sendgrid_password
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

3.3 이메일 서비스 구현

// EmailService.java
@Service
public class EmailService {
    
    @Autowired
    private JavaMailSender mailSender;
    
    public void sendOtpEmail(String to, String otp) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(to);
        message.setSubject("Your OTP Code");
        message.setText("Your OTP code is: " + otp);
        mailSender.send(message);
    }
}

3.4 OTP 전송 로직 통합

// MFAController.java (추가)
@Autowired
private EmailService emailService;

@PostMapping("/send-otp")
public ResponseEntity<?> sendOtp(@RequestParam String username) {
    User user = userRepository.findByUsername(username)
                  .orElseThrow(() -> new UsernameNotFoundException("User not found"));
    String secret = user.getSecretKey();
    int otp = gAuth.getTotpPassword(secret);
    emailService.sendOtpEmail(user.getEmail(), String.valueOf(otp));
    return ResponseEntity.ok("OTP sent to email");
}

4. 테스트 및 검증

4.1 사용자 등록 및 로그인 흐름 테스트

  1. 사용자 등록: /register 엔드포인트를 통해 새로운 사용자 등록
  2. 로그인 시도: /login 엔드포인트로 로그인 (Spring Security가 처리)
  3. OTP 전송: /send-otp 엔드포인트 호출하여 OTP 전송
  4. OTP 검증: /mfa 엔드포인트로 OTP 입력 및 검증

4.2 Postman을 사용한 API 테스트

  • POST /register: 사용자 등록
  • POST /login: 로그인 (세션 또는 JWT 사용)
  • POST /send-otp: OTP 전송
  • POST /mfa: OTP 검증

추가 실습 및 확장 아이디어

  • JWT와의 통합: 세션 대신 JWT를 사용하여 인증 상태 관리
  • SMS 전송 구현: Twilio API를 사용하여 SMS로 OTP 전송
  • 프론트엔드 구현: 간단한 React 또는 Vue.js 애플리케이션을 만들어 사용자 경험 향상
  • 보안 강화: 비밀키를 안전하게 저장하고, OTP 만료 시간 설정 등 보안 강화
  • 로그인 실패 및 잠금 기능 추가: 여러 번의 실패 시 계정 잠금

마무리

이 실습을 통해 Spring Boot와 Spring Security를 사용하여 기본적인 사용자 인증 시스템을 구축하고, MFA를 추가하여 보안을 강화하는 방법을 배울 수 있습니다. 실제 프로젝트에서 이러한 기능을 구현해보며 경험을 쌓는 것이 중요합니다. 실습 중에 발생하는 문제나 궁금한 점이 있다면 언제든지 질문해 주세요!

성공적인 취업 준비를 응원합니다!

0개의 댓글