Tech

[Tech] 로그인 인증 - 토큰 방식(JWT)

lonnie(동현) 2021. 9. 14. 15:24

 지난 포스팅에서는 로그인 인증 - 세션과 쿠키 인증 방식에 대해서 알아봤다. 이번엔 토큰 인증 방식에 대해서 알아보려고 한다.

그렇다면, 토큰을 이용한 인증 방식은 뭘까?

 토큰 인증 방식은 인증받은 사용자들에게 토큰을 발급하고, 서버에 요청을 할 때 헤더에 토큰을 함께 보내도록 하여 유효성 검사를 한다. 세션 방식과 달리 상태를 유지하지 않으므로 stateless 한 구조를 갖는다.

 

 이러한 토큰 인증 방식에서 대표적으로 사용되는 JWT 를 이용한 토큰 인증 방식에 대해서 알아보고자 한다.

JWT(Jason Web Token)이 뭘까?

 JWT 는 정보를 JSON 객체 형태로 사용자에 대한 속성 정보(Claim)를 저장하는 암호화된 문자열(token)이다. JWT는 토큰 자체를 정보로 사용하는 Self-contained 방식으로 정보를 안전하게 전달한다

JWT 구조

 JWT 는 아래와 같이 Header, Payload, Signature의 3 부분으로 이루어지며 JSON 형태인 각 부분은 Base64 url로 인코딩 되어 표현된다. 각각의 부분을 이어 주기 위해 . 구분자를 사용하여 구분한다. Base64 url으로 반환된 값은 암호화된 문자열이 아니고, 같은 문자열에 대해서 항상 같은 인코딩 문자열을 반환한다.


1. Header(헤더)

 Header는 typalg 두 가지 정보로 구성된다. alg는 Header를 암호화하는 것이 아니고 Signature를 해싱하기 위한 알고리즘을 지정하는 것이고, typ는 토큰의 타입을 지정한다.

  • typ : 토큰의 타입을 지정 ex) JWT
  • alg : 알고리즘 방식을 지정, 서명(Signature) 및 토큰 검증에 사용 ex) HS256(SHA256) 또는 RSA
{
    "alg": "RS256",
    "typ": "JWT"
}

2. Payload(페이로드)

 Payload에는 토큰을 통해 전달할 내용인 클레임(Claim) 정보를 포함하고 있다. Payload에 담는 정보의 한 조각을 클레임이라고 부르고, 이는 name/value의 한 쌍(JSON 형식)으로 이루어져 있다.

 

 토큰에는 여러 개의 클레임들을 넣을 수 있고, 클레임의 정보는 등록된(registered) 클레임, 공개(public) 클레임, 비공개(private) 클레임으로 세 종류가 있다.

 

2-1 등록된 클레임(Registered Claim)

 등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터들이다. "sub"는 주로 사용자 이메일을 사용한다.

{
    "iss": 토큰 발급자(issuer)
    "sub": 토큰 제목(subject)
    "aud": 토큰 대상자(audience)
    "exp": 토큰 만료 시간(expiration)
    // NumericDate 형식(Unix Time) ex) 1631598299000
    "nbf": 토큰 활성 날짜(not before)
    // 이 날이 지나기 전의 토큰은 활성화되지 않음
    "iat": 토큰 발급 시간(issued at)
    // 토큰 발급 이후 경과 시간을 알 수 있음
    "jti": JWT 토큰 식별자(JWT ID)
    // 중복 방지 및 일회성 토큰(Access Token)에 사용 
}

2-2 공개 클레임(Public Claim)

 공개 클레임은 사용자 정의 클레임으로, 공개용 정보를 위해서 사용된다. 충돌 방지를 위해서 URI 포맷을 이용한다. 예시는 아래와 같다

{
    "https://aosjehdgus.tistory.com/124":true
}

2-3 비공개 클레임(Private Claim)

 비공개 클레임은 사용자 정의 클레임으로, 서버와 클라이언트 사이에 임의로 지정한 정보를 저장한다. 예시는 아래와 같다.

{ 
    "token_type": access 
}

3. Signature(서명)

서명은 토큰을 인코딩하거나 유효성을 검증할 때 사용하는 고유한 암호화 코드이다. Signature는 Header와 Payload의 값을 각각   Base64 url로 인코딩하고, 인코딩한 값을 비밀 키를 이용해 Header에서 정의한 알고리즘으로 해싱을 하고, 이 값을 다시 Base64 url로 인코딩하여 생성한다.


인증 순서에 대해서 알아보자

  1. 사용자가 로그인을 한다.
  2. 서버에서 계정 정보(Claim)를 읽어 사용자를 확인 후, 사용자의 고유한 ID값을 부여한 후, 기타 정보와 함께 Payload에 넣는다.
  3. JWT 토큰의 유효 기간을 설정한다.
  4. 암호화할 Secret Key를 이용해 Access Token을 발급한다.
  5. 사용자는 Access Token을 받아 저장한 후, 인증이 필요한 요청마다 토큰을 헤더에 실어서 보낸다.
  6. 서버에서는 해당 토큰의 Signature를 Secret Key로 복호화한 후, 조작 여부, 유효 기간을 확인한다.
  7. 검증이 완료된다면, Payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져온다.

장점

  1. JWT를 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요기 때문에 stateless 한 서버를 만들 수 있다. 따라서 서버를 확장하거나 유지, 보수하는데 유리하다.
  2. 확장성이 뛰어나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능하다. 예를 들어, 소셜 로그인을 들 수 있다.
  3. 트래픽에 대한 부담이 적다.

단점

  1. 이미 발급된 JWT는 돌이킬 수 없다. 따라서 악의적인 사용자는 유효기간 전까지 해당 토큰을 이용하여 해킹할 수 있다.

→ 기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급한다.

  1. Payload 정보가 제한적이다. Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. 따라서 유저의 중요한 정보들을 Payload에 넣을 수 없다.

참고

728x90
반응형