내가 아는 jwt는 아래가 전부였다.🙄
{
"alg": "HS256",
"typ": "JWT"
}
{
"sub": 1,
"iat": 1641550513,
"exp": 4233550513
}
jsonwebtoken
를 보면 이런 코드가 나온다.
export interface JwtHeader {
alg: string | Algorithm;
typ?: string | undefined;
cty?: string | undefined;
crit?: Array<string | Exclude<keyof JwtHeader, 'crit'>> | undefined;
kid?: string | undefined;
jku?: string | undefined;
x5u?: string | string[] | undefined;
'x5t#S256'?: string | undefined;
x5t?: string | undefined;
x5c?: string | string[] | undefined;
}
export interface JwtPayload {
[key: string]: any;
iss?: string | undefined;
sub?: string | undefined;
aud?: string | string[] | undefined;
exp?: number | undefined;
nbf?: number | undefined;
iat?: number | undefined;
jti?: string | undefined;
}
사실 단순히 서버와 클라이언트 사이에서 사용자를 인증하는 용도로 사용한다면 크게 신경쓰지 않아도 좋다. 서버-클라이언트 사이의 단순 통신을 넘어 제 3자도 JWT 토큰을 사용할 때 충돌이 일어나지 않도록 합의된 클레임이라고 생각하면 된다.
function base64(json) {
// JSON을 문자열화
const stringified = JSON.stringify(json);
// 문자열화 된 JSON 을 Base64 로 인코딩
const base64Encoded = Buffer.from(stringified).toString("base64");
// Base 64 의 Padding(= or ==) 을 제거
const paddingRemoved = base64Encoded.replaceAll("=", "");
return paddingRemoved;
}
Base64 로 문자열을 인코딩 하면, 결과물 마지막에 = 혹은 == 가 가끔 같이 나오는 경우가 존재한다. 이를 Padding 이라고 하는데, 이를 제거하지 않으면 URL Safe 하지 않게 되므로 반드시 제거하자. 제거해도 Decode 를 정상적으로 할 수 있다.
const header = {
alg: "HS256",
typ: "JWT",
};
const encodedHeader = base64(header);
const payload = {
email: "hocaron@hocaron.com",
name: "hocaron",
};
const encodedPayload = base64(payload);
// eyJlbWFpbCI6ImRldmh1ZGlAZ21haWwuY29tIiwibmFtZSI6Ikh1ZGkiLCJpc0FkbWluIjp0cnVlfQ
const signature = crypto
.createHmac("sha256", "secret_key")
.update(`${encodedHeader}.${encodedPayload}`)
.digest("base64")
.replaceAll("=", "");
HMAC (Keyed-hash Message Authentication Code) 이란, 메시지 인증 코드 (MAC) 의 한 유형으로서 특정 Key 와 함께 특정 Message 를 Hash 값으로 만드는 암호화 방식이다. 공격자로 하여금 레인보우 테이블 기법의 해킹을 어렵게 하기 위해 원문과 함께 비밀키를 더하여 해싱하는 것 이다.
const jwt = `${encodedHeader}.${encodedPayload}.${signature}`;
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImhvY2Fyb25AaG9jYXJvbi5jb20iLCJuYW1lIjoiaG9jYXJvbiIsImlhdCI6MTUxNjIzOTAyMn0.Zt8PAM872GjhXezWR1booXAF487wf5SOXDtFPCgXKHE
원래는 jwt payload에 userId만 있었는데, email로 변경해서 인증해야하는 api가 있다고 하자. 여기에 대응하기 위해, jwt header에 version을 기입해서 관리한다고 한다.