Node 강의/숙련

1-10 JWT

kagan-draca 2024. 9. 7. 23:26

1. JWT가 무엇인가?

 

JWT(Json Web Token)은 웹 표준으로써, 서버와 클라이언트 사이에서

정보를 안전하게 전송하기 위해 도움을 주는 웹 토큰(Web Token)입니다.

 

- JSON 형태의 데이터를 안전하게 전송하고 검증할 수 있는 기능을 제공합니다.

- 인터넷 표준으로서 자리잡은 규격입니다.

- 다양한 암호화 알고리즘을 사용할 수 있어, 신뢰성을 보장합니다.

- header.payload.signature 의 형식으로 3가지의 데이터를 포함합니다.

→ 때문에, JWT 형식으로 변환 된 데이터는 ~~~.~~~.~~~ 데이터 입니다.

 

1) 어떻게 생겼는가?

JWT(Json Web Token)은 크게 헤더(Header), 페이로드(PayLoad), 서명(Signature)로 구성 됩니다.

각각의 부분은 점(.)으로 분리됩니다.

 

Header(머리) : 헤더는 토큰의 타입과 어떤 암호화를 사용하여 생성된 데이터인지 정의 돼 있습니다.

{
    "alg": "HS256",
    "typ": "JWT"
}

 

Payload(가슴) : 페이로드는 실제 전달하려는 데이터를 담고 있습니다.
                          대표적으로 개발자가 원하는 데이터를 저장합니다.

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

 

Signature(배) : 서명은 헤더와 페이로드, 그리고 비밀 키(Secret Key)를 이용하여 생성됩니다.

                         이 서명은 토큰이 변조되지 않은 정상적인 토큰인지 확인할 수 있게 도와줍니다.

HMACSHA256(
    base64UrlEncode(header) + "." +
    base64UrlEncode(payload),
      secret
)

 

각 부분을 Base64로 인코딩하여 점(.)으로 연결하면 최종적으로 JWT를 생성하게 됩니다.

이렇게 생성된 JWT는 이전에 배운 쿠키(Cookie) 또는 Path Parameter를 통해 전달될 수 있습니다.

 

2) 더 알아두어야 할 특성

JWT의 특성 정리하기

 

1. JWT는 비밀 키를 모르더라도 복호화(Decode)가 가능합니다.

  • JWT를 가진 사람이라면 누구나 해당 토큰에 어떤 데이터가 담겨있는지 확인할 수 있습니다.
  • 변조만 불가능 할 뿐, 누구나 복호화하여 보는 것은 가능하다는 의미

2. 민감한 정보(개인정보, 비밀번호 등)는 담지 않도록 해야합니다.

  • JWT의 페이로드는 누구나 복호화하여 볼 수 있기 때문입니다.

3. JavaScript와 같이 특정 언어에서만 사용 가능한 것은 아닙니다.

  • JWT는 단순히 데이터 형식일 뿐, 단지 개념으로서 존재하고,                                                                                        이 개념을 코드로 구현하여 공개된 코드를 우리가 사용하는게 일반적입니다.

 

3) JWT는 쿠키, 세션과 어떻게 다른가?

 

데이터를 교환하고 관리하는 방식인 쿠기 / 새션과 달리,

 

JWT 단순히 데이터를 표현하는 형식입니다.

 

 

JWT로 만든 데이터 변조가 어렵고, 서버에 별도의 상태 정보를 저장하지 않기 때문에,

서버를 Stateless(무상태)로 관리할 수 있습니다.

 

쿠키와 세션 사용자의 로그인 정보나 세션 데이터를 서버에 저장하므로 상태를 유지합니다.

때문에, Stateful(상태 보존)하게 데이터가 관리됩니다.

 

(중요) Stateless(무상태),  Stateful(상태 보존) 비교

Stateless(무상태) Stateful(상태보존)
서버가 죽었다 살아나도
똑같이 동작
서버가 죽었다 살아났을 때
다르게 동작

 

2. JWT VsCode에서 사용하는 법

 

yarn add jsonwebtoken

# jsonwebtoken 라이브러리 설치하기

 

(yarn을 초기화 하고 express도 설치해주세요)

(package.json에서 "type" : "module" 추가하기)

 

 

그 후, jw-test.js 파일을 만들고

import jwt from "jsonwebtoken";

const token = jwt.sign({ myPayloadData: 1234 }, "mysecretkey");
console.log(token);

 

위와 같은 코드를 추가해줍니다.

 

sign 메서드는 매개변수로는

 

payload : JWT에 담길 데이터

 

secretOrPrivateKey : JWT를 서명하는 데 사용되는 비밀 키(문자열 아무 값이 들어가도 됨)

 

[options] : 만료 시간, 발행자 등의 옵션을 포함할 수 있습니다.

 

이 있고,

 

sign 메서드는 전달 받은 데이터 JWT(jsonwebtoke)안의

Payload로 전달하는 역할을 담당합니다.

 

첫 번째 인자Payload 데이터를, 두 번째 인자비밀 키를 받아 JWT를 생성합니다.

→ 여기서, Payload는 문자열 뿐만 아니라, 객체도 할당할 수 있습니다.

 

위의 코드를 실행시켜보면,

 

같이

 

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0LCJpYXQiOjE3MjU3MTE4OTB9.lPboxYQ8av6KgVC2mKxYi0S63MN-3-c1KMfXEpziTKA

 

으로 결과가 출력되는걸 확인할 수 있습니다.

 

이 결과를

 

jwt.io 사이트에 접속해(https://jwt.io/)

 

 

Encoded 부분에 넣으면 Decoded 부분의 myPayloadData에서

 

myPayloadData : "1234"

 

로 출력되는 걸 확인할 수 있습니다.

 

그 밑의, iat는 jwt가 자동적으로 만든 key와 value로

 

jwt의 유효기간이 얼마나 남았는지 알려줍니다.

 

jwt.io 사이트에서 확인한 것 처럼

 

jwt같은 경우 데이터를 내부에 저장하지만,

비밀키가 없더라 동일하게 Payload 안에 있는

Data를 볼 수 있습니다.

 

이제 암호화한 내용을 복호화 해보겠습니다.

 

import jwt from "jsonwebtoken";

const token = jwt.sign({ myPayloadData: 1234 }, "mysecretkey");
console.log(token);

// jwt payload 복호화
const decodeValue = jwt.decode(token);
console.log(decodeValue);

 

아래와 같이 코드를 수정해줍니다.

 

token 으로 웹 사이트에서 나온 값을 넣고

 

jwt.decode()로 실행시키면

 

암호문이 복호화된 걸 확인할 수 있습니다.

 

그런데,

 

암호화된 Key가 누군가에 의해 악의적으로 변조되지

않은 데이터인지 검증해야할 필요성이 있다는 생각이 듭니다.

 

이럴 경우,

 

jsonwebtoken 라이브러리verify 메서드를 사용해 JWT를 검증할 수 있습니다.

 

(중요)verify 메서드

import jwt from "jsonwebtoken";

const token = jwt.sign({ myPayloadData: 1234 }, "mysecretkey");
console.log(token);


// jwt payload 검증 코드
const decodeValueByVerify = jwt.verify(token, "mysecretkey");
console.log(decodeValueByVerify);

 

위와 같이 작성하고 실행시켜보면

 

위와 같이 결과가 출력 됩니다.

 

만약, 암호화에 사용한 키와 복호화에 사용한 키가 다르다면,

 

 

위와 같이 오류가 발생하게 됩니다.

 

2. JWT 사용의 중요성 깨닫기

 

만약, JWT를 사용하지 않은 상태에서 사용자 로그인을 구현하려고 하면 어떻게 될까요?

 

JWT를 적용하지 않은 로그인 API를 만들어봅시다!

 

app.js

import express from "express";

const app = express();
const PORT = 5002;

app.use(express.json());

app.post("/login", (req, res, next) => {
  const user = {
    userId: 203,
    email: "dddd@naver.com",
    name: "이름 없음",
  };

  res.cookie("sparta", user);
  return res.status(200).end();
});

app.listen(PORT, (req, res, next) => {
  console.log(PORT + "로 서버가 열렸습니다!");
});

로 서버를 열어줍니다.

 

Insomnia로 localhost:5002/login 을 실행시켜보면

아무 값 Client에게 응답(Response)으로 온게 없지만,

Cookie를 보면 

 

위와 같이 Cookie가 전달된 것을 확인할 수 있습니다.

 

Cookie의 정보가 암호화 된 것 처럼 보이지만

HTTP 통신에서 해당 정보를 전달하기 위해서

특수문자를 만들어놓은 것 입니다.

(암호화되지 않은 것)(그래서 잘 보면 userId, email로 dddd가 보인다.)(헷갈리지말자)

JWT를 적용시켜 로그인 API를 만들어봅시다!

import JWT from "jsonwebtoken";

추가 후,

 

app.post("/login", (req, res, next) => {
  const user = {
    userId: 203,
    email: "dddd@naver.com",
    name: "이름 없음",
  };

  // 사용자 정보를 JWT로 생성
  const userJWT = JWT.sign(
    user, // user 변수의 데이터를 payload에 할당
    "secretOrPrivateKey", // JWT의 비밀키(아무 값이나 와도 된다) 문자열 할당
    { expiresIn: "1h" } // JWT의 인증 만료시간을 1시간으로 설정
  );

  // userJWT 변수를 sparta 라는 이름을 가진 쿠키에
  // Bearer 토큰 형식으로 할당
  res.cookie("sparta", userJWT);
  return res.status(200).end();
});

로 코드 작성 및 수정이 가능합니다.

 

기존 사용자 정보 객체

 

JWT.sign()

 

첫번 째 매개변수로 주고,

"secretOrPrivateKey" 라는 비밀키를 만든 후,

{expiresIn : "1h"} // JWT의 인증 만료 시간을 1시간으로 설정한다.

 

*h : * 시간 이후 만료,

*m : * 분 이후 만료,

 

 

res.cookie로

쿠키의 이름을 "sparta"로,

userJWT로 쿠키를 줍니다.

 

 

Insomnia에서 localhost:5002/login 해보면,

위와 같이 완벽하게 암호화 된 jwt 암호문이 출력됩니다.

 

이렇게 암호화된 쿠키를 https://jwt.io/ 에서 Encoded 해보면,

 

userId, email, name정보를 확인할 수 있고,

 

iat쿠키생성 날짜

exp쿠키만료 날짜를 

 

확인할 수 있고, 3000초 = 1시간 차이가 나는걸 확인할 수 있습니다!

 

3. 암호화 된 데이터는 어떻게 쓰이나?

 

보통 암호화 된 데이터는 클라이언트(브라우저)가 전달받아 다양한 수단(쿠키, 로컬스토리지 등)을

통해 저장하여 API 서버에 요청을 할 때 서버가 요구하는 HTTP 인증 양식에 맞게 보내주어 인증을 시도합니다.

 

비유하자면, 놀이공원의 자유이용권과 비슷한거죠!

  1. 회원가입: 회원권 구매
    • 클라이언트는 회원가입에서 이메일, 패스워드와 같은 정보를 제공합니다.
    • 이 정보는 서버에 저장되어, 이후 인증 과정에서 사용됩니다.
  2. 로그인: 회원권으로 놀이공원 입장
    • 클라이언트는 이메일, 패스워드로 로그인합니다.
    • 서버는 이 정보를 검증한 후, 유효하다면 JWT를 생성하여 클라이언트에게 제공합니다.
  3. 로그인 확인: 놀이기구 탑승 전마다 유효한 회원권인지 확인
    • 클라이언트는 로그인 후 모든 API 요청에 JWT를 포함하여 전송합니다.
    • 서버는 JWT를 확인하고, 유효하다면 요청된 API를 수행합니다.
  4. 내 정보 조회: 내 회원권이 목에 잘 걸려 있는지 확인하고, 내 이름과 사진, 바코드 확인
    • 클라이언트는 JWT를 사용해 자신의 정보를 조회할 수 있습니다.
    • 서버는 JWT를 복호화하여 내부에 저장된 정보를 확인하고, 이 정보를 바탕으로 사용자의 세부 정보를 조회하여 전달해줍니다.