본문 바로가기
NodeJS, NPM, Koa

JWT(Json Web Token)와 세션관리에 대해서 알아보자

by Developer88 2020. 5. 18.
반응형

오늘은 JWT(Json Web Token)와 세션관리에 대해서 정리보겠습니다.

 

1. JSON Web Token(JWT)

JWT는 토큰 정보와, 해당 토큰이 유효함을 증명할 수 있는 signature가 포함되어 있는 토큰인데요.

 

JWT는 Header, Payload, 그리고 Signature로 구성되어 있습니다.

1-1. Header

Header에서는 토큰의 유형과 사용된 서명 알고리즘을 지정합니다.

비대칭인 'RS256'과 같은 알고리즘을 쓸 수도 있고, 'HS256'을 쓸 수도 있겠지요.

아래에서는 'HS256'알고리즘을 사용하였습니다.

{  
 "alg": "HS256",   
 "typ": "JWT"
}

 

1-2. Payload

Payload에는 토큰이 전달하고자 하는 실제 데이터가 담겨 있습니다.

이 부분에는 토큰 발급 대상자, 만료 시간, 주제 등 다양한 클레임(claims)을 포함할 수 있습니다.

 

유저에 대한 상태나 정보등을 담을 수 있는데요.

아래와 같은 정보들을 담을 수 있습니다.

{
 "sub": "77823323",
 "name": "Big Park",
 "iat": 1516239122
}

 

1-3. Signature

Signature는 토큰이 변조되지 않았음을 증명하기 위한 서명으로,

토큰의 Header와 Payload를 서명 알고리즘으로 해시하고,

비밀키 또는 공개키/개인키 쌍을 사용하여 생성합니다.

 

HMACSHA256(  
 base64UrlEncode(header) + "." +  
 base64UrlEncode(payload),  
secret)

 

https://jwt.io/ 에 가면 아래와 같이 실제로 인코딩된 값을 볼 수 있습니다.

 

 

이러한 3가지 구조덕분에,

JWT는 보안이 필요한 환경에서 데이터를 안전하게 전송하고,

클라이언트 사이드에서 토큰의 유효성을 검증할 수 있게 해 줍니다.

 

2. JWT 구현

2-1. JWT라이브러리 설치

JWT라이브러리는 www.jwt.io/를 참고하면 알 수 있는데요.

Nodejs에서는 jsonwebtoken 모듈을 사용할 수 있습니다.

 

아래 npm명령어로 jsonwebtoken을 설치해 줍니다.

npm install --save jsonwebtoken

 

해당 패키지에 대한 공식git허브 페이지는 아래와 같습니다.

https://github.com/auth0/node-jsonwebtoken

 

그럼 이제 본격적으로 JWT를 구현 하겠습니다.

로그인과 로그아웃의 UI 및 컨트롤러가 구현된 상태라고 가정하고,

JWT를 발행하고 검증하는 단계에 대해서 정리해 보도록 하겠습니다.

 

먼저 jwt모듈을 require 합니다.

const jwt = require('jsonwebtoken');

 

2-2. 토큰 발행하기

토큰을 발행하기 위해 아래와 같은 형식을 따르는데요.

payload는 객체나 string형태로 모두 전달 가능하구요.

secret은 토큰의 Signature 부분을 생성할 때 사용되는 문자열로,

이 secret 문자열을 사용해 토큰의 유효성을 검증합니다.

중요한 점은, secret이 외부에 노출되어서는 안 된다는 것입니다. 

Secret이 클라이언트나 외부에 노출되면, 

누군가가 그 secret을 사용하여 유효한 토큰을 위조할 수 있기 때문입니다. 

만약 secret이 노출된 경우, 이를 신속하게 변경하고 기존의 토큰들을 무효화시켜야 합니다. 

 

jwt.sign(payload, secret, options, [callback])

 

아래 코드로 토큰을 발행 할 수 있습니다.

위에서 본 인자 순서대로 값을 넣어 주는 것 인데요.

여기에서 expiresIn는 유효기간인데,

"2 days", "10h", "7d" 같이 시간, 일 등의 단위를 사용할 수 있습니다.

만약 단위를 사용하지 않는다면 ms단위로 처리가 됩니다.

jwt.sign(
  { nickname: 'ladida'}, 
  'secret', 
  { expiresIn: '1h' }
);

 

이렇게 발행된 토큰을 서버에서 HTTP의 Response 헤더에 넣어서 클라이언트에게 전달해 주면 되겠지요.

access token의 경우는 x-access-token (사용자 정의라는 의미에서 붙이는 x를 추가함)과 x-refresh-token과 같은 이름으로 넣어줄 수 있겠습니다.

 

2-3. 토큰 검증하기

토큰 발행하는 것처럼 토큰을 검증하는 로직도 복잡하지 않습니다.

jwt.verify(token, 'wrong-secret', function(err, decoded) {
   if(err) console.log(err);
   console.log("decoded payload", decoded);
});

 

이렇게 해서 토큰을 발행하고 검증하는 것에 대해 알아보았구요.

이러한 토큰을 좀 더 안전하게 사용하는 방법으로서의

액세스 토큰과 리프레쉬 토큰에 대해서 알아보겠습니다.

 

3. 세션관리와 JWT토큰

위에서 세션관리에 사용할 JWT토큰에 대해서 정리해 보았는데요.

세션관리에 대해서 좀 더 보도록 하겠습니다.

 

사용자들은 크롬이나 익스플로러 같은 브라우저를 가지고 접속을 하게 되는데요.
접속후에 로그인이라는 과정을 거쳐 해당사이트의 컨텐츠를 사용하거나 특정 요청을 할 수 있습니다.
그러나 계속 로그인을 하게 하는 것은 매우 불편한 일이므로 보통은 사용자의 세션에 그 상태를 저장하는데요.

 

3-1. 서버에 세션을 저장하는 방법

세션은, 서버의 메모리에 저장하기도 하고, 데이터베이스에 넣을 수도 있습니다.
사용자가 많을수록, 메모리든 데이터베이스든 부하를 받게 되는 부분이 있지만,

언제든 문제가 생기면 무효화 할 수 있으므로 대응이 빠르다고 할 수 있습니다.

 

3-2. 클라이언트에 세션을 저장하는 방법

세션을 클라이언트에 저장할 경우, 암호화된 JWT토큰을 사용해 저장할 수 있는데요.
유저의 로그인정보를 서버에 담지 않을 수 있다는 점에서 부하를 덜 수 있지만,
혹여 탈취되었을 때의 문제가 있으므로 세심한 주의가 필요합니다.

유저가 로그인인증을 하면 서버에서 사인된 토큰을 발급받구요.
클라이언트에서 이 토큰을 저장한 다음,

헤더값에 토큰값을 전송해서 서버에서 검증받는 방식입니다.

 

서버에 상태를 저장하지 않으므로,
서버부하를 상당히 줄여준다는 면에서는 아주 좋습니다.
하지만 반대로 서버가 컨트롤 할 수가 없으므로,
클라이언트의 컴퓨터가 탈취당한 경우 유효시간까지는,
강제로 개별 클라이언트에 해당토큰을 무효화 할 방법이 존재하지 않습니다.

유저가 비밀번호를 변경한 경우에도,
이전 비밀번호로 유효했던 토큰에 대해 서버가 개별적으로 무효화 시킬 수 없으므로,
유효기간이 다 되기까지 계속 유효한 상태가 되버릴 수 있구요.

 

여기에 대한 대안으로, 액세스 토큰과 리프레쉬 토큰을 나누어서 사용하는 방법이 있는데요.

아래에서 보도록 하겠습니다.

 

4. 액세스 토큰과 리프레쉬 토큰

4-1. 유효기간이 짧은 액세스 토큰과 기간이 긴 리프레쉬 토큰의 발급

매번 사용자가 로그인 인증을 하게 할 수는 없고, 토큰의 유효기간을 길게 주면,

사용자의 컴퓨터가 탈취당할 경우, 유효기간까지는 아이디/패스워드가 빼앗긴거나 다름이 없는 상태가 되는데요.

이에 대한 대응을 하기 위해서 액세스 토큰과 리프레쉬 토큰을 사용합니다.

 

유효기간이 짧은 액세스 토큰과 유효기간이 긴 리프레쉬 토큰을 발행해 사용하는데요.

다음과 같이 액세스 토큰은 짧게, 리프레쉬 토큰은 길게 유효기간을 설정해 줍니다.

  • 액세스 토큰: 일반적으로 15분에서 1시간 정도로 설정합니다
  • 리프레쉬 토큰: 1주일 ~ 1달 정도로 길게 설정
    • 리프레시 토큰은 안전하게 저장해야 하며, 유출되지 않도록 주의해야 합니다.

 

 

 

액세스 토큰은 아이디/패스워드와 같으므로 많은 일들을 하고 탈취당할 경우 아주 위험해 집니다.

따라서 유효기간을 짧게 해주고, 유효기간이 다 되면,

유저에게 아이디/패스워드를 물어보는 것이 아니고,

유효기간이 긴 리프레쉬 토큰을 이용해서 서버에서 액세스 토큰을 다시 재 발행해서 사용하도록 합니다.

그럼 유저는 굳이 인증을 하지 않으면서도, 서버는 유효성을 계속 검증할 수 있습니다.

 

액세스 토큰의 유효기간이 지나고, 리프레쉬 토큰으로 액세스 토큰을 재 발행받으려고 할 때,

해당 클라이언트가 탈취당한 사실을 서버에서 알고 있을 경우,

해당 id로 액세스 토큰을 발급받지 못하도록 막을 수 있겠지요.

 

이 방법의 단점은, 리프레쉬 토큰을 이용해 액세스 토큰을 발급 받을 때,

매번 서버에 검증을 위해 서버에 접속하므로 부하가 생길 수 있다는 점과,

액세스 토큰의 유효기간동안에는 역시 무방비 상태가 될 수 있다는 점 입니다.

 

5. 보안관리 팁들

5-1. JWT토큰 전송시 HTTPS 사용

JWT토큰 전송시에는 HTTPS를 사용해서,

통신을 암호화 해 주어야 합니다.

 

5-2. 토큰 저장시 HTTP 전용 쿠키(HttpOnly Cookie)사용

클라이언트에서 JWT 토큰을 저장할 때, HTTP전용쿠키를 사용해 줍니다.

HTTP 전용 쿠키는 자바스크립트에서 접근할 수 없으므로 XSS 공격으로부터 토큰을 보호할 수 있습니다.
쿠키에 Secure 플래그를 설정하여 HTTPS 연결에서만 쿠키가 전송되도록 합니다.

 

5-1. 환경변수로 시크릿 키 관리하기

시크릿 키는 환경 변수로 설정하고, 소스 코드에 직접 포함하지 않습니다.

 

5-2. 시크릿키 정기 관리

secret을 안전하게 보관하고 정기적으로 갱신하는 방안을 마련해야 합니다.
이는 JWT를 사용하는 시스템의 전체 보안 건전성을 유지하는 데 필수적인 부분입니다.

 

5-3. 꼼꼼한 토큰 검증

서버에서 받은 JWT 토큰을 항상 철저히 검증해야 합니다.
토큰의 유효성, 만료 시간, 서명 등을 확인하고, 
검증 실패 시 적절한 에러 처리를 하고, 

로그를 남겨 모니터링할 수 있도록 해 두어야 합니다.

 

5-4. 로그아웃된 토큰 무효화

로그아웃 기능을 구현할 때는 서버 측에서 해당 토큰을 무효화해야 합니다.
무효화된 토큰은 블랙리스트에 추가하거나, 

토큰의 식별자를 데이터베이스에 저장하여 관리할 수 있습니다.
로그아웃 시 클라이언트에서도 토큰을 삭제하도록 합니다.

 

5-5. IP주소 기록하기

JWT 토큰에 사용자의 IP 주소나 기기 정보를 포함하여 토큰 도용이 되더라도 흔적을 찾을 수 있습니다.

 

5-6. 모니터링

JWT 토큰 사용 시 인증 관련 이벤트를 모니터링하고 로깅하는 것은 안전한 사용을 위해서 매우 중요합니다.
인증 실패, 토큰 검증 오류, 비정상적인 토큰 사용 등을 감지하고,
로그를 분석해 보안 위협을 탐지하고 대응 방안을 수립할 수 있어야 합니다.

 

 

 

728x90

댓글