[all] OTP를 통한 2FA 인증 정리 #60
JuneParkCode
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
지금 진행하고 있는 웹 과제
ft_transcendence
에서는42 intra
를 통한 로그인 이후2FA
를 요구하고 있다. 서비스에 적용한TOTP
방식의 인증을 이해하고, 해당 프로젝트가NestJS
를 기반으로 진행되기에 2FA 인증을Javascript
에서 구현하는 과정을 소개하고자 한다.2FA 란?
지식 기반 인증
사용자가 알고 있는 정보, 유출되기 쉽다는 단점이 있음.
소유 기반 인증
사용자가 소유하고 있는 것을 통한 인증.
생체 인증
사용자 그 자체를 증명할 수 있는 수단
참고
http://wiki.hash.kr/index.php/이중인증
https://m.blog.naver.com/n_privacy/221131898198
https://www.microsoft.com/ko-kr/security/business/security-101/what-is-two-factor-authentication-2fahttps://duo.com/product/multi-factor-authentication-mfa/two-factor-authentication-2fa
선택
Google Authenticator, EMAIL, 2차 비밀번호 총 3가지의 고려대상이 있었다.
TOTP
기반의 인증을 수행한다. 위에 고려했던 방법과 확실히 인증 방식의 차별점이 있었고, OTP 인증에 대한 이해를 해보고자 선택을 하게 되었다.OTP
OTP 란?
One Time Password 의 준말로 무작위로 생성되는 1회용 비밀번호를 이용하는 방식이다. 우리가 은행에서 발급받아 사용하는 하드웨어 OTP 기기부터 게임 로그인에 활용하는 OTP 앱 모두를 포함한다.
HOTP
간단하게 살펴보는 HOTP 인증 과정
secret key
를 생성한다.secret key
는 유저마다 할당되며, 임의의 문자열로 생성된다.secret key
를 유저에게 전달하는 동시에, 서버도 유저마다의secret key
를 저장한다.secret key
를 OTP 생성기에 등록한다.secret key
를 바탕으로 토큰을 생성한다.counter
가 동기화되지 않은 상태일 수 있으므로,resyncronize
과정이 포함될 수 있다.토큰은 어떻게 생성되는가?
Key
와Counter
를 통해HMAC-SHA-1
계산을 해서 나온 20 바이트의 결과값을dynamic truncate
하여 얻게된다. (이 과정은 rfc4226 5.3 에 잘 설명되어있다.)Counter
와Key
이다. 서버와 유저가 서로 공유하고 있는Key
와 같거나 가까운 값으로 유지되고 있는Counter
를 통해서 토큰이 생성되고, 이를 통해서 유저가 제안한 토큰이 유효한지 검사할 수 있기 때문이다.Key
의 경우 클라이언트와 서버가 모두 저장하고 있는 상황이기 때문에 서로가 정상적으로 저장했다면, 해당 과정에서는 큰 문제는 아니다.counter
의 값은 서버와 클라이언트가 서로 다른 값을 바라보고 있을 수 있다. 서버는 요청이 유효할 때만counter
의 값을 증가시키고 클라이언트는 매 요청에서counter
를 증가시키기 때문이다. 따라서 이 간극을 맞추기 위한resynchronization
이 필요하다.server counter ≤ client counter
이므로 서버는resynchronization
위해서 이후의counter
에 대해서 매칭이 되는지 확인하고 이를 동기화하면 된다.resynchronize
를 위한look ahead
파라미터를 별도로 설정하여 다음 카운터 값 중에 매칭되는지 확인을 하는 것을 권장한다.보안에 대한 고려
Throttling
이라는 파라미터도 권장 요소이다. 유저가 일정 이상의 OTP 인증에 실패한 경우, 일시적으로 인증을 잠금을 하며, 전체 세션에 대해서 적용할 수 있도록 하여 병렬적으로 진행되는brute force attack
으로부터 방지할 수 있다.secret key
의 경우 안전하게 생성된 임의의 문자열으로 구성되어야하며 이를 저장할 때 또한 보안을 고려하여 보호해야한다.참고
https://datatracker.ietf.org/doc/html/rfc4226
TOTP
우리 서비스에서 활용하는 Google Authenticator의 경우
TOTP
방식을 활용한다. 간단하게 작동 과정에 대해서 살펴보자.TOTP
는HOTP
의 파생 방식으로 인증 과정은 동등하다. 다만counter
대신time-step
을 활용하여 토큰을 생성한다.TOTP 토큰은 어떻게 생성될까?
counter
대신 시간을 활용한다. 이 때 사용되는 시간은 일반적으로unix time
을 활용하게 되며, 현재의 정확한 시각이 아니라 시간대를 활용한다. RFC 6238 에서는 다음과 같이 설명하고 있다.이 때, 하나의
time-step
은 30초를 권장하고 있다. 이는 네트워크 지연, 보안을 모두 고려한 권장 사항이며 뒤에 있는 구현 과정에서 또한 30초로 설정되어 있다.resynchronization
과정을 권장한다.HOTP
에서counter
의slide
가 요구되었듯, 마찬가지로time-step
에 대한slide
를 사용하여resyncronization
을 수행한다.주의사항
참고
https://datatracker.ietf.org/doc/html/rfc6238
Google Authenticator
Google Authenticator는 앞에서 살펴본 TOTP 기반 토큰 생성기이다.
Google Authenticator 를 활용하는 방법은 TOTP 를 적용하는 과정과 동일하다. 서버는 유저에게 할당한
secret key
를 공유하고 Google Authenticator는 이를 앱에서 등록하여 토큰을 생성하는 것이다. 따라서 서버에서의secret key
발급 과정, Authenticator로부터 발행된 토큰의 인증 과정을 이해하면 된다.구현
가볍게 js에서 어떤 라이브러리를 사용하여 구현할 수 있는지 살펴보겠다.
OTP
Node.js 에서 OTP 발급, 인증 과정을 활용하기 위해서 otplib 라이브러리를 활용한다.
키 발행
유저가 2FA를 활성화 할 때, OTP의 secret key 발급이 필요하다. OTP Secret key에 활용할 임의의 문자열을 생성해서 클라이언트에게 전달하면 된다.
otplib
에서 이를 지원하기 때문에generateSecret()
함수를 이용하면 된다.이 결과값으로 임의의 문자열을 전달 얻을 수 있다. 이를 유저의 DB에 저장하고, 추후에 활용할 인증 과정에 활용하면 된다.
그런데, Google Authenticator 의 경우 QR 코드 인식을 지원한다. QR 코드에서는
otpauth://TYPE/LABEL?PARAMETERS
형식의 URI가 담겨있다. 이 또한otplib
라이브러리에서 지원한다.이 결과 우리가 생성할 결과는 다음과 같을 것이다.
otpauth://totp/ACME%20Co:john.doe@email.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30
이를 QR Code 형식의 이미지로 보내주고 싶기 때문에 qrcode 라이브러리를 활용할 것이다.
인증
인증 방식 또한 간단하다.
otplib
라이브러리를 다시 활용할 것이다.otplib
는 앞서 우리가 공부한 TOTP 의 인증 방식을 활용하여 해당 토큰의 유효성을 검증할 것이다.보안
Secret key 저장 방식
유저의
OTP Secret key
를 평문으로 DB에 저장하는 것은 굉장히 위험한 행위이다. DB가 유출된 경우,OTP Secret key
를 그대로 임의의 OTP 생성기에 적용하여 토큰을 획득하고 인증을 할 수 있기 때문이다. 따라서,OTP Secret key
의 경우 암호화 되어 저장되어야 한다.암호화를 고려할 때, 우리가 인증에 원본
OTP Secret key
가 필요함을 인식해야한다. 그래서 단방향으로 복호화가 불가능한 암호화 방식이 아닌, 복호화가 가능한 방식이 필요하다. 이를 위해서 나는AES-256
방식의 암호화를 사용했다.cryptoJS
Node.js 에서 AES 암호화를 사용하기 위해서는 cryptoJS 라이브러리를 활용하면 된다. 우리는 타입스크립트를 활용하고 있기 때문에 @types/crypto-js도 함께 설치하는 것을 추천한다. AES 암호화 방식 자체에 대해서 공부할 내용이 많지만 해당 글에서는 가장 쉽게 암호화와 복호화를 하는 과정만 코드로 간략하게 설명할 것이다.
encrypt
decrypt
인증 threshold 설정
RFC에서 권장하듯, 공격에 대해 대비하기 위해서
threshold
를 설정할 필요가 있다.otplib
에서는 별도로 제공을 하고 있지 않기 때문에, (해당이슈) 개발자가 직접 설정해줄 필요가 있다.Beta Was this translation helpful? Give feedback.
All reactions