스프링 시큐리티를 이용해서 애플리케이션의 로그인처리를 하는 예제는 많이 있다. 거기에 OTP(One Time Password) 를 함께 사용하여 보안을 조금 더 강화해보도록 하겠다. 보통 TOTP(Time based One-time Password) 라고도 하는데 사용자에게 할당된 비밀키SecretKey와 OTP 킷에서 시간을 기준으로 생성하는 검증코드(Verification Code) 를 조합하여 인증가능한지 여부를 확인하는 것이다. 이때 사용자에게 할당되는 비밀키는 일회성으로 요청에 따라 새롭게 발급하는 식으로 운영이 된다. 

보통은 issuer + username + secretKey 를 이용하여 등록가능한 QR코드로 애플리케이션에 등록하는 형식을 취하게 된다.

QR리더를 이용해서 담겨있는 내용을 읽어보면 다음과 같다:

otpauth://totp/{issuer}:{username}?secret={secretKey}&issuer={issuer}

의 형식을 취한다. 이때 생성되는 secretKey는 일회성이다. 만약 외부에 노출될 경우에는 새롭게 secretKey를 발급받고 기존에 등록하 OTP 정보를 삭제하고 다시 등록해줘야 한다.

사용자에게 비밀번호를 변경하라(?)는 부담을 주지 않아도 된다. OTP 용 비밀키만 재발급하면 된다. 응?

2단계 인증과 관련한 내용은 구글의 2단계인증 과정에 대한 설명을 살펴보시면 더욱 좋을 듯 하다.

그럼 예제를 살펴보도록 하자.

spring-security-2step-verification 프로젝트

h2databse 는 기본구성이 메모리로 실행된다.

application.yml 설정

spring:
  datasource:
    initialize: true # data.sql 을 이용한 DB 초기화 작업
  mail:
    default-encoding: UTF-8
    username: #${username}
    password: #${password}
    host: smtp.gmail.com
    port: 587
    protocol: smtp
    properties:
      mail.smtp.starttls.enable: true
      mail.smtp.auth: true
  h2: # jdbc ur: jdbc:h2:mem:testdb
    console: # http://localhost:8080/h2-console
      enabled: true

OTP SecretKey 발급

UserServiceImpl
@Override
	public User insert(User user) {
		GoogleAuthenticatorKey key = googleAuthenticator.createCredentials();
		sendTOTPRegistrationMail(user, key);
		user.setOtpSecretKey(key.getKey());

		encodePassword(user);
		return repository.save(user);
	}

	private void sendTOTPRegistrationMail(User user, GoogleAuthenticatorKey key) {
		String qrCodeUrl = GoogleAuthenticatorQRGenerator.getOtpAuthURL(ISSUER, user.getUsername(), key);
		Map<String, Object> attributes = new HashMap<>();
		attributes.put("qrCodeUrl", qrCodeUrl);

		MailMessage mailMessage = MailMessage.builder()
				.templateName(MailMessage.OTP_REGISTRATION)
				.to(new String[]{user.getUsername()})
				.subject("Honeymon TOTP Registration mail")
				.attributes(attributes)
				.build();
		mailService.send(mailMessage);
	}

위의 코드를 보면 알겠지만 사용자 엔티티를 생성하는 순간에 인증키를 생성하여 secretKey를 사용자 엔티티에 할당하고, 그와 동시에 등록된 사용자 계정으로 메일을 발송한다.

사용자 계정생성 -> OTP 비밀키 생성 및 할당 -> OTP 등록 QR코드를 내장한 메일 발송 -> 사용자: OTP 디바이스에 등록 -> 로그인시 사용자명/비밀번호/OTP 검증코드

로그인때 입력하는 검증코드는 일반적으로 6자리를 사용하며 일정시간동안만 유효성을 가진다.

그 결과는 다음과 같으며,

이 QR코드를 Google OTP 앱에서 읽어들이면 다음과 같이 추가된다.


이제 OTP 코드는 일정간격으로 갱신된다. 


갱신되는 타이밍이 맞지 않으면 인증실패가 발생한다.

정상적으로 로그인하면 다음과 같은 화면을 볼 수 있다.


정리

2단계 인증은 비밀번호와 단말기(스마트폰, OTP 킷)가 있어야 로그인을 할 수 있다. 서버쪽에서 계정정보가 유출된다면, 바로 OTP용 비밀키를 초기화하고 사용자에게 재등록하도록 안내를 하면 계정이 도용되는 사례를 쉽게 막을 수 있는 장점을 제공한다.

2단계 인증을…​ 제대로 활용할 수 있다면 꽤 쓸만할 듯 하다.

참고문헌


  1. 나그네 2018.06.29 13:15 신고

    그런데 가상화폐 거래소는 아이디 패스워드 입력 후 페이지 이동 후 otp번호 입력하던데 이렇게 따로따로 전송하는건 어떻게 구현하는 걸까요?

    • 잘?

      로그인하고 인증하는 절차는 시스템에 따라 얼마든지 구현이 가능하죠.

      1차적으로 기본인증(사용자명+비밀번호)을 마쳤다면 1차 인증마쳤다는 정보를 세션이나 헤더 등에 저장시켜놓고 2차 인증(OTP)하는 페이지로 리다이렉트 시켜서 최종구현하면 되니까....

  2. 나그네 2018.06.30 11:04 신고

    그렇군요 근데 spring security를 잘 모르니깐 커스터마이징하기가 쉽지는않네요ㅋㅋ 일단 연구해봐야겟네용

Spring Boot + Gradle 을 이용한 프로젝트 시작할 때 쓸 목적으로 프로젝트를 하나 만들었다.

먼저 로컬에서 작업하고, 깃헙에서 저장소Repository를 만들었다.

  • 빈 저장소에서 친절하게 설명해주는 깃헙씨.
  • http와 ssh 프로토콜 지원하고,
  • 프로젝트를 새로 만들었을 때 깃을 이용하여 등록하고 깃헙에 생성한 저장소에 밀어넣는 방법.
  • 이미 만들어진 저장소를 깃헙에 밀어넣는 방법.
  • 혹은 다른 버전관리시스템에서 불러오는 방법

친절하다.

회사에서 진행하는 프로젝트는 스프링부트Springboot 를 이용해서 진행해보고 싶은 욕심이 생겼다.


 

기타 참고사항 

 

[File - New - Spring Starter Project]를 선택하면 다음의 창이 뜬다. 

다음[Next]을 누르면 spring starter를 다운로드 받는다. Spring Boot를 이용해서 starter 프로젝트를 다운로드 받고 프로젝트를 구성한다.

 


이 spring-starter의 핵심은 @EnableAutoConfiguration 이다.

이 Spring starter 의 핵심은 @EnableAutoConfiguration 이지 싶다. 프로젝트에 사용되는 @Annotation의 타입을 스캔해서 관련된 라이브러리를 자동으로 추가하여 구동시키는 것으로 보여진다. 오늘 잠깐 살펴보며 짐작한 것은 그렇다.

[Run as - Spring Boot App]을 선택하면 자동으로 실행된다.


+ Recent posts