해당 포스트는
Token
,JWT
,Access-Token
,Refresh-Token
의 개념과 이론적인 사용법 정리하는 포스트입니다.
인증 ( Authentication ): 로그인, 해당 사이트의 회원임을 인증 받는 것
인가 ( Authorization ): 인증받은 사용자가 서비스의 기능을 사용하는 인가를 받는 것
🪸 Hashing
암호화만 가능한 방식을 말합니다.
즉, 복호화가 불가능하기 때문에 암호화된 값이 노출되어도 조금은 더 안전할 수 있습니다.
대표적인 해시 함수 SHA1
가 있고 주로 salt
라는 방식을 이용해서 암호화합니다.
( salt
를 사용하면 예측 가능한 암호들이 해킹당하는 것을 어느정도 막아줍니다. )
salt
: 해시를 사용하는 방식은 그대로인데 해시 함수에 넣는 문자열에 특수한 문자열을 더해서 연산하기Hashing
의 특징- 항상 같은 길이의 문자열을 리턴
- 동일한 해시 함수에 같은 문자열을 사용하면 항상 같은 값이 나옴
🪙 Token
인증에 사용하는 하나의 방식을 의미합니다.
( 다른 방식으로는 Session을 이용한 방식이 있습니다. )
0️⃣ Token vs Session
Token
에 대해 알아보기 위해서는 Session과 비교해서 이해하면 도움이 됩니다.
Session
은 서버의 메모리나 데이터베이스 즉, 서버의 자원을 이용해서 데이터를 관리합니다.
따라서 Session
을 사용하면 서버의 자원을 어느정도 사용하게 됩니다.
이게 문제가 될 수 있는 게 서버는 하나 혹은 여러 개지만 클라이언트는 셀 수 없이 많습니다.
따라서 사용자가 늘어날수록 서버는 기존보다 더 많은 작업을 하지만 추가로 메모리의 부담도 발생하게 되기 때문에 문제가 될 수 있습니다.
이러한 Session
의 한계를 극복하기 위해서 토큰 방식의 인증이 나왔습니다.
Session
보다Token
을 사용하는 것이 무조건 좋다는 의미가 아닙니다.
두 방식의 장단점에 대해 이후에 설명하겠습니다.
비교 | Token | Session |
---|---|---|
메모리 | 브라우저에 저장 | 서버에 저장 |
스케일링 | 스케일링하기 편함 | 스케일링 하기 불편함 |
관리 | 관리 불가능 | 관리 가능 |
스케일링이란 서버를 여러 대로 확장하는 것을 의미합니다.
만약 세션을 사용하는 서버를 스케일링한다면 나눠진 서버들이 모두 세션을 공유할 수 있도록 로직을 구현해야하게 됩니다.
반면 토큰의 경우에는 자체적으로 데이터를 갖기 때문에 토큰을 제작하는 데 사용한 비밀 키만 공유하면 됩니다.
관리측면에서는 세션은 서버에서 갖기 때문에 마음대로 수정/삭제를 할 수 있습니다.
하지만 토큰의 경우 브라우저가 갖기 때문에 한 번 내보내고 나면 서버에서 관리할 수 있는 방법이 없어집니다.
🍨 JsonWebToken
많은 Token
들이 존재할 것이고 그 중에서 사용하는 서비스나 환경에 맞게 선택하면 됩니다.
하지만 저는 JsonWebToken
만 사용해봤기 때문에 해당 토큰을 기준으로 작성하겠습니다.
JsonWebToken
이란 JSON
객체에 정보를 담고 이를 Token
으로 암호화하여 전송하는 기술입니다.
이후에는 어떻게 암호화하고 어떻게 검증하는지 알아보겠습니다.
0️⃣ 구조
크게 세 가지 구조로 나눠집니다.
0. Header
해당 Token
을 설명하는 메타 데이터를 담는 부분입니다.
alg
: 사용할 해시 알고리즘 (HS256
,RS256
,ES256
등 )typ
:Token
의 타입 (JWT
로 사용하면 됩니다. )
1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}
1. Payload
토큰에 넣고 싶은 데이터를 담는 부분입니다.
원하는 데이터를 담으면 되지만 너무 많이 담거나 노출되면 안 되는 정보를 담으면 안됩니다.
일반적으로 유저의 식별자나 DB
에서 검색 자주하는 간단한 데이터를 담는 것 같습니다.
- 약속된 값 ( 클레임 )
iss
: 토큰 발급자sub
: 토큰 제목aud
: 토큰 대상자exp
: 토큰 만료 시간nbf
: 작성한 시간 이후부터 유효한 토큰으로 처리iat
: 토큰이 발급된 시간jti
: 토큰 자체의 고유한 식별자
1
2
3
4
5
6
{
"id": "1440",
"admin": true,
"iat": 1678274675716, // 발급 시간
"exp": 1679484275716, // 만료 시간 ( 발급 시간으로부터 일주일 )
}
2. Signature
토큰이 유효한지 확인하는 데 사용하는 부분입니다.
SHA256
알고리즘을 사용하는 경우 Header
+ "."
+ Payload
+ Secret
을 합쳐서 Signature
를 생성합니다.
여기서 Secret
란 JWT
를 생성하고 검증할 때 사용할 고유의 문자열입니다.
어떤 값이라도 상관 없지만, secret
, keyboardcat
같은 흔한 문자열을 사용하면 다른 사람이 쉽게 알아낼 수 있기 때문에 고유한 값을 사용하면 좋습니다.
또한 생성할 때 Header
, Payload
를 사용하기 때문에 두 값을 변조한다면 검증을 통해 유효하지 않은 Token
임을 알아낼 수 있습니다.
서버가 아닌 다른 곳에서 위조하기 위해서는 Secret
이 필요하고 그 값이 노출되지 않는 것이 중요합니다.
1️⃣ Node에서 사용하기
Node.js
에서 JWT
를 생성하고 검증하는 방법에 대한 예시를 작성하겠습니다.
1. 설치
1
2
3
npm i jsonwebtoken
npm i -D @types/jsonwebtoken
2. 사용 예시
sign()
으로 JWT
를 생성하고 verify()
로 JWT
를 검증합니다.
생성한 JWT
는 일반적으로 Cookie
나 Authorization
으로 브라우저로 전달합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { sign, verify } from "jsonwebtoken";
const payload = { name: "Aatrox", age: 26 };
const myJWT = sign(payload, "keyboardcat", {
expiresIn: "1h",
});
console.log(myJWT);
/**
* 가독성을 위해서 "." 기준으로 줄바꿈
*
* Header => eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
* Payload => .eyJuYW1lIjoiQWF0cm94IiwiYWdlIjoyNiwiaWF0IjoxNjc4MjgyODkzLCJleHAiOjE2NzgyODY0OTN9
* Signature => .y-TMGGg01ymOLheLa154EKn8ryoyJu3v4e_SiqZZN_A
*/
try {
const verified1 = verify(myJWT, "keyboardcat");
console.log("verified1 >> ", verified1);
} catch (error) {
console.error("verified1 >> ", error);
}
try {
const verified2 = verify(myJWT, "stangeValue");
console.log("verified2 >> ", verified2);
} catch (error) {
console.error("verified2 >> ", error);
}
/**
* verified1 >> { name: 'Aatrox', age: 26, iat: 1678283131, exp: 1678286731 }
* verified2 >> JsonWebTokenError: invalid signature
*/
🍻 AccessToken과 RefreshToken
여기서 코드에 대한 예시보다는 이론적으로 어떤 방식으로 동작하는지에 대해서 작성하겠습니다.
Token
이라고 해서 특별한게 아니고 JWT
로 사용할 수 있습니다.
AccessToken
과 RefreshToken
을 사용하는 이유는 Token
의 한계점을 어느정도 보완하기 위해서입니다.
Token
은 Session
처럼 서버에서 관리하지 않기 때문에 한 번 생성하면 다시 회수하거나 제거할 수 없습니다.
따라서 한 번 탈취당하면 서버에서 대처를 할 수 없다는 의미입니다.
이런 위험성을 갖고 있기 때문에 인증을 처리하는 AccessToken
은 유효기간을 짧게 하고, RefreshToken
의 유효기간을 늘려서 주기적으로 AccessToken
을 재발급하는 방식을 주로 사용합니다.
물론 이 방식도 완벽하지 않습니다.
RefreshToken
조차도 탈취당할 수 있기 때문입니다.
보안적으로는 항상 완벽은 없다고 들어서 완벽하지 않은 방법중에서 가장 현재 상황에 맞는 방법을 선택하는 것이 중요한 것 같습니다.
0️⃣ AccessToken
인가를 위해 사용하는 Token
입니다.
1️⃣ RefreshToken
AccessToken
의 유효기간이 지난 경우 재발급하는데 사용할 Token
입니다.
2️⃣ 동작흐름
상황에 따라서 여러 방법이 있겠지만 Cookie
를 통해서 AccessToken
과 RefreshToken
을 브라우저에 전달한다고 가정하고 설명하겠습니다.
- 유저로 인증이 되었다면 해당 유저의 식별자를
Payload
로 갖는AccessToken
,RefreshToken
을 생성 - 생성한
Token
들을Cookie
에 동봉해서 브라우저로 전달 - 브라우저에서 응답을 보고 자체적으로 쿠키를 등록하고 서버에게 요청할 때도 동봉해서 요청
- 서버에서는 쿠키를 열고 그 값인
Token
을 유효한지 검사 후 인증된 유저인지 확인 후 특정 행동 실행- 만약
Token
이 유효하지 않다면401
응답 - 만약
Token
의 유효기간이 지났다면RefreshToken
확인 후 문제 없다면AccessToken
재발급
- 만약