스프링 부트 구글 OTP 인증 적용

아두치·2023년 10월 10일
0

보통은 아아디/패스워드 기반 인증으로 애플리케이션의 인증을 처리하지만
보안에 민감한 애플리케이션의 경우 MFA를 적용하기도 한다.
MFA는 쉽게 말해서 추가 인증 수단이라고 생각하면 되는데, 아이디/패스워드가 유출되더라도 MFA가 적용되어 있으면 해킹을 상당부분 예방할 수 있다.

근데 국내 애플리케이션에서 MFA 가 적용된 서비스는 못본 것 같기도 하다..
외국 서비스도 사용자 편의성 때문에 무조건 MFA를 적용하진 않고 사용자의 선택사항으로 적용하는 것 같다.

아무튼, 오늘 실습할 주제는 스프링 부트 애플리케이션에 MFA 를 적용할건데, MFA 수단은 구글 OTP (일회성 암호) 이다.

우선 구글 OTP 인증의 큰 흐름과 동작 원리를 이해해야하는데, 구글 OTP 는 Google Authenticator 라는 앱에서 해당 유저의 OTP (일회성 암호)를 특정 시간 간격마다 새로 생성한다.
사용자는 OTP 값을 애플리케이션에 입력하면 애플리케이션은 입력받은 OTP 값을 검증하기만 하면 된다.
근데, 구글 Authenticator 앱에서 어떻게 나의 애플리케이션을 사용하는 사용자의 OTP를 생성할 수 있을까?
OTP 는 특정 문자열로 표현되는 키 값과 키 값이 생성된 시간과 특정 주기의 시간을 이용해 생성된다.
그러니까 Authenticator 앱에 이 키값을 등록하기만 하면 OTP는 특정 시간 주기마다 생성이 될 것이고, 이 키 값을 디비에 유저와 매핑해서 들고있기만 하면 검증도 가능하다는 이야기다.
그리고 키 값을 Authenticator 앱에 등록하는 수단은 QR 코드이다.
즉, 애플리케이션에서 Google Auth 라이브러리를 이용해 새로운 key를 발급받고 이 key를 이용해 QR 코드를 생성한 뒤 사용자가 Authenticator 앱에 QR 코드를 등록하기만 하면 된다.
이것을 사용자마다 적용하면 되는 것이다.
물론 key는 사용자의 ID와 함께 특정 테이블에 저장해놔야 OTP 검증이 가능하다.

하지만.. 이 글의 목적은 스프링 부트에서 OTP 연동이기 때문에 유저 인증이나 DB에 key를 저장하는 부분은 모두 다 생략하고 순수 구글 OTP 연동 방법만 설명할 예정이다.

우선, Google Auth 라이브러리 의존 관계를 추가해보자.

implementation 'com.warrenstrange:googleauth:1.4.0'

사실 이 의존 관계만 추가해도 80% 는 완성된거라서.. ㅎㅎ
다음으로 해야할 일은, 사용자의 Authenticator 앱에 등록할 QR 코드를 생성하는 일이다.

@SpringBootApplication
public class MfaApplication {
    private static final Logger logger = LoggerFactory.getLogger(MfaApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(MfaApplication.class, args);

        GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
        GoogleAuthenticatorKey googleAuthenticatorKey = googleAuthenticator.createCredentials();

        // 실제론 생성한 key를 DB에 저장해놔야 나중에 OTP를 검증할 수 있음
        String key = googleAuthenticatorKey.getKey();
        logger.info("key : " + key);

        String QRUrl = GoogleAuthenticatorQRGenerator.getOtpAuthURL("adduci", "userId", googleAuthenticatorKey);
        logger.info("QR URL : " + QRUrl);
    }

}

애플리케이션을 실행하면 QR URL 이 표시된다.
(참고로 GoogleAuthenticatorQRGenerator.getOtpAuthURL("adduci", "userId", googleAuthenticatorKey) 에서 adduci와 userId 부분은 마음대로 지정해도 되고, adduci 부분은 QR을 생성한 주체를, userId 부분은 QR을 등록하는 주체를 의미한다.)

이 QR URL에 접속해서 나오는 QR 이미지를 Authenticator 앱에 등록해보자.

그럼 adduci: userId 라는 이름의 OTP가 생성되는게 보일 것이다.
아까 adduci와 userId 파라미터의 의미를 이제 이해할 수 있을 것이다.
(즉, adduci 부분엔 서비스의 이름을, userId 부분엔 애플리케이션 내에서 사용자의 id를 사용하는게 일반적이다.)

이제 이 OTP 값을 애플리케이션에게 던져주면, 유효한 OTP 값인지 체크하는 기능을 구현해보자.

GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();

String key = "2UJNWSKOIWVMXEXY";

boolean verify = googleAuthenticator.authorize(key, 95283);

logger.info("Verify : {}", verify);

생각보다 너무 간단하다.
아까 생성한 key (실제론 DB에 저장되어 있겠지) 와, 입력한 OTP 를 이용해서 authorize 메소드만 호출하면 특정 알고리즘으로 유효한지 검증하고 결과를 알려준다.

여기까지가 마무리인데, 각자 현재 서비스에 잘 녹여서 유용하게 사용해보자~

profile
HAVE YOU TRIED IT?

2개의 댓글

comment-user-thumbnail
2023년 10월 10일

아 참고로, OTP 첫글자가 0이면 0은 생략해도 된다

답글 달기
comment-user-thumbnail
2024년 7월 10일

안녕하세요~ 혹시 commons-codec 라이브러리를 이용해 직접 구현하지 않고 해당 라이브러리를 선택하신 이유가 있으실까요 ??

답글 달기