본문 바로가기

Network

로그인 인증/인가 방식 2. 토큰 방식

세션 방식에 이어서, 토큰 방식에 대해서 알아보자.

 

인증/인가에 사용되는 토큰은 JWT(Json Web Token)이고 구조는 아래와 같다.

 

# JWT 구성

XXX.YYY.ZZZZ와 같이 두 개의 점을 기준으로 세 파트로 나눠져있다.

각각 1. Header, 2.Payload, 3.(verify) Signature로 구분.

 

1. Header

Header에는 토큰의 종류(타입)해싱 알고리즘의 종류, 이렇게 2가지 정보가 담겨있다.

 

1) typ

토큰의 종류(type). 여기엔 언제나 고정값인 'JWT'가 들어간다.

typ값이 'JWT'여야 이 토큰의 종류가 JWT가 된다.

 

2) alg

해싱 알고리즘. 3번 Signature을 만드는데 사용될 알고리즘이 지정된다.

보통 HS256(HMAC-SHA256) 또는 RSA가 지정된다.

 

위와 같이 JSON형식으로 되어있고, Base64 방식으로 인코딩하여 "eyJhbGci.."인 문자열형태로 변환되었다.

더보기

** 인코딩 : 정보를 특정한 형식으로 표현하는 방법. 주로 데이터를 변환하거나 전송할 때 사용된다.

ex : URL인코딩, Base64 인코딩 등이 있다.

- Base64 : 인코딩 방식 중 하나. 데이터의 이진 표현을 텍스트로 변환하는데 사용된다. 역변환도 가능.

순서

1. JSON 형식의 header

2. UTF-8 인코딩 : header를 UTF-8 형식으로 인코딩한다.

- UTF-8 : 유니코드 기반의 문자들을 이진 데이터(0과 1)로 변환하는 인코딩 방식 중 하나.

3. UTF-8 방식으로 인코딩된 이진 데이터를 Base64 방식으로 인코딩(0과 1 -> 텍스트)

2021.10.31 - [Computer Science] - 문자열 세트(또는 문자셋) 및 유니코드 & 인코딩, UTF-8란?

 

문자열 세트(또는 문자셋) 및 유니코드 & 인코딩, UTF-8란?

1. 문자열 세트(또는 문자셋) 컴퓨터는 정보들을 기본적으로 0과 1, 이 두 개로만 저장한다. 그렇다고 사람보고 0과 1만 가지고 정보들을 읽고 쓰라고 하면 매우 힘들다. 그래서 우리가 사용하는

lion284.tistory.com

 

 

 

2. Payload

Base64 방식으로 인코딩된 payload를 디코딩해보면, JSON형식으로 여러 정보들이 들어있다.

이 토큰에는 누가 누구에게 발급했는지, 언제까지 유효한지, 유저의 닉네임, 서비스상의 레벨, 관리자 여부 등을 서비스측에서 원하는대로 담을 수 있다.

여기에 담는 정보의 한 '조각'을 claim이라고 부르고, 이는 name-value의 한 쌍으로 이루어져있다.

토큰에는 여러 개의 claim들을 넣을 수 있다.

 

클레임의 종류는 다음과 같이 크게 3분류로 나눠져있다.

- 등록된(registered) 클레임

- 공개(public) 클레임,

- 비공개(private) 클레임

 

1) 등록된(registered) 클레임

서비스에서 필요한 정보들이 아닌, 토큰에 대한 정보들을 담기위하여 이름이 이미 정해진 클레임들.

등록된 클레임의 사용은 모두 선택적(optional)이며, 이에 포함된 클레임 이름들은 다음과 같다.

- iss : 토큰 발급자(issuer)

- sub : 토큰 제목(subject)

- aud : 토큰 대상자 (audience)

- exp : 토큰의 만료시간(expiration). 시간은 NumericDate 형식으로 되어있어야 하며(예:14808413434) 언제나 현재 시간 이후로 설정되어있어야 한다.

- nbf: Not Before를 의미하며, 토큰의 활성 날짜와 비슷한 개념. 여기에도 NumbericDate형식으로 날짜를 지정하며, 이 날짜가 지나기전까지는 토큰이 처리되지 않는다.

- iat : 토큰이 발급된 시간(issued at). 이 값을 사용하여 토큰의 age가 얼마나 되었는지 판단할 수 있다.

- jti : JWT의 고유 식별자. 주로 중복적인 처리를 방지하기 위하여 사용된다.

 

 

2) 공개(public) 클레임

공개 클레임들은 충돌이 방지된(collision-resistant) 이름을 가지고 있어야 한다.

충돌을 방지하기 위해서는 클레임 이름을 URI 형식으로 짓는다.

{
    "https://velopert.com/jwt_claims/is_admin": true
}

 

 

3) 비공개(private) 클레임

등록된 클레임도, 공개된 클레임도 아니다. 클라이언트-서버의 협의 하에 사용되는 클레임 이름들이다.

공개 클레임과는 달리, 이름이 중복되어 충돌될 수 있으니 사용할 때 유의해야한다.

{
  "name": "John Doe"
}

 

 

아래 Payload의 예시를 보면, 2개의 등록된(registered) 클레임 + 1개의 비공개(private) 클레임으로 구성되어있다.

{
  "sub": "1234567890",
  "name": "John Don",
  "iat": 1516239022
}

 

이렇게 토큰에 담긴 사용자 정보 등의 데이터를 claim이라고 한다.

사용자가 로그인을 하고나서 받는 토큰에 이 정보들이 claim이라는 걸로 실려온다.

그 이후, 요청들마다 유저로부터 서버에게 보내지는 것.

토큰 자체에 이런 정보들이 들어있으면, 서버가 요청마다 일일이 데이터베이스에서 뒤져봐야할 것들이 줄어든다.

 

 

## payload의 유출위험..?

하지만, 특별한 암호화도 아니고, Base64로 인코딩된거면 사용자가 다시 decoding해서 볼 수 있지 않을까?

그러면 사용자가 이걸 조작해서 악용할 수 있지 않을까?

예를 들면... 토큰의 유효기간을 50년으로 늘려놓는다거나, 관리자여부를 false → true로 바꿔서 관리자 권한으로 인가를 받거나.

그래서 1번 header와 3번 verify signature이 존재한다.

 

 

3. (verify) Signature

1번 header + 2번 payload + '서버에 감춰놓은 비밀값' 이 셋을 1번 header의"alg"에 적혀있는 암호화 알고리즘(해쉬 알고리즘)에 넣고 돌리면(해시함수로 해싱하면) 3번 서명값이 나온다.

서버는 요청에 토큰값이 실려오면, 1,2번 값을 '서버의 비밀키'와 함께 돌린 후 나온 값과 3번 서명값이 일치하는지 확인한다.

3번 서명값과 계산값이 일치 + 유효기간이 지나지 않았다면, 그 사용자는 로그인된 회원으로 인가를 받는다.

그래서 서버는 사용자의 상태를 따로 기억해 둘 필요없이, 이 '비밀키'만 저장하고 있으면, 요청이 들어올 때마다 사용자들을 필터링 할 수 있다.

 

 

만약 해커가 토큰을 탈취해도, 3번 signature의 값을 알려면 '서버에 숨겨진 비밀키'를 알고 있어야 한다.

하지만 해커는 "서버만 알고있는 그 비밀값"을 찾아낼 방법이 없다.

 

또한, 암호화 알고리즘(해시 알고리즘)은 글자 하나만 바뀌어도 출력값이 완전히 달라진다.

따라서 2번 payload의 정보가 조금이라도 수정된다면 3번 signature의 값은 완전히 달라지므로 정보를 조작한 유저거나 해커인걸로 간주되어 거부된다.

2021.11.26 - [인증 보안] - Hashing, 그리고 Salt값

 

Hashing, 그리고 Salt값

먼저, 유저가 클라이언트를 통해 이메일 관련 데이터를 요청할 때, 이메일주소와 password를 가지고 요청한다고 생각해보자. CASE 1 : DB에 password가 그대로 저장되어있는 경우 1> 서버가 클라이언트

lion284.tistory.com

 

 

이렇게 시간에 따라 바뀌는 어떤 상태값을 가지지 않는 특징을 "stateless"하다고 한다.

반면, 세션은 모든 사용자의 상태를 기억하고 있으므로 stateful한 특징을 가지고 있다.

 

 

# JWT 방식의 단점

하지만, 세션을 대체하기에는 JWT에 큰 결점이 있다.

 

서버 측에서 제어 불가능

세션처럼 stateful해서 모든 사용자의 상태를 기억하고있다는 건 구현하기 부담되고 고려사항도 많지만, 기억하는 대상의 상태들을 언제든 제어할 수 있다는 의미.

ex: 한 기기에만 로그인 가능한 서비스를 만드려는 경우, PC에서 로그인한 상태의 어떤 사용자가 핸드폰으로 또 로그인을 하면 PC에서는 로그아웃되도록 기존 세션을 종료할 수 있다.

서버에 사용자의 상태를 저장하고 있으므로 컨트롤이 가능하다.

 

세션방식에서는 기존의 세션을 종료하면 되는데, JWT에서는 그런게 불가능하다.

이미 클라이언트에 줘버린 토큰을 뺏을 수도 없고, 토큰의 발급내역이나 정보를 서버가 기록해서 추적하고 있는것도 아니므로..

서버 측에서는 유저의 상태를 저장할 필요가 없어서 편하기는 한데, 그래서 통제를 못한다.

그렇다고 상태를 기억해두려고 하면, 세션과 다를바가 없으므로 토큰 방식의 의미가 없어진다.

 

 

또한, 토큰을 해커들에게 탈취당한 경우, 토큰을 무효화할 방법이 없다.

그러므로 실서비스 중, JWT만으로 인가를 구현하는 곳은 생각보다 그렇게 많지 않다.

 

 

# 단점 보완방법 : access Token & refresh Token

이런점을 나름대로 보완하기 위한 방법들이 있다.

→ 만료시간을 짧게 해 토큰의 수명을 아주 짧게 준다.

 

Q. 그러면..몇 분 있다가 또 로그인을 해야하는 불편함이 있지 않을까?

그래서 로그인 후, 토큰을 2개 준다.

 

1/ access token

 - 수명이 몇 시간이나 몇분 이하로 짧다.

 - 매번 인가를 받을 때 쓰는 수명이 짧은 토큰

2/ refresh token

 - access token보다 수명이 긴 토큰. 보통 2주 정도의 수명.

 - access token을 재발급받을 때 쓰는 토큰

 

- 서버는 access tokenrefresh token을 발급하고 클라이언트에게 보낸다.

그리고, 데이터베이스에도 refresh token의 동일값을 저장한다.

- 클라이언트는 access token의 수명이 다하면, refresh token을 서버에게 보낸다.

- 서버는 클라이언트로부터 받은 refresh token을 데이터베이스에 저장된 값과 대조해보고, 맞다면 새 access token을 발급해준다.

 

 

- refresh token만 안전하게 관리된다면, 이 refresh token이 유효할 동안은 access token이 만료될때마다 다시 로그인할 필요없이 새로 발급받을 수 있다.

- 중간에 access token이 해커로부터 탈취당해도 access token 유효기간 자체가 짧기때문에 오래쓰지 못한다.

- 어떤 유저를 강제 로그아웃시키려면, refresh token을 DB에서 지워 토큰 갱신을 못하도록 하면된다.

 

# JWT의 한계

→ 하지만 이렇게 해도, 짧게나마 access token이 살아있는 동안은 바로 차단할 방법은 없다.

 

토큰 하나만 쓰는 것보다는 access token, refresh token 두 토큰을 쓰는게 더 낫지만, 빈틈없는 해결책은 아니다.

이것이 jwt의 한계!

 

** 참고
https://velog.io/@ye-ji/JWTJson-Web-Token%EC%97%90%EC%84%9C-%EC%A0%95%EB%B3%B4%EB%B9%BC%EA%B8%B0

 

JWT(Json Web Token)란 무엇인가!! 정리해보쟈~

1. JWT(Json Web Token)란? [ JWT 개념 ] JWT(Jso

velog.io

https://velopert.com/2389

 

[JWT] JSON Web Token 소개 및 구조 | VELOPERT.LOG

지난 포스트에서는 토큰 기반 인증 시스템의 기본적인 개념에 대하여 알아보았습니다. 이 포스트를 읽기 전에, 토큰 기반 인증 시스템에 대해서 잘 모르시는 분들은 지난 포스트를 꼭 읽어주세

velopert.com

https://hoime.tistory.com/94

 

토큰 기반의 로그인 인증 방법 이해하기 (JWT)

프로젝트를 진행하며 로그인 / 회원가입 부분을 담당하여 개발하게 되었습니다. 로그인을 구현하기 위해 공부한 내용을 정리하고 공유하고자 하는 목적으로 글을 작성합니다. 우선 사용자 로그

hoime.tistory.com

https://d-dual.tistory.com/3

 

[ 사용자 인증 / 인가 ] Session & Cookie VS Token

Session과 Token은 무엇일까? 👉 사용자가 로그인되어있는 상태를 서버가 인지할 수 있도록 하는 방법들 중 하나. 인터넷을 이용할 때 쓰는 HTTP는 Stateless(상태가 없는, 무상태의) 프로토콜이라, 모

d-dual.tistory.com