해당 포스트는
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재발급
- 만약