그저 내가 되었고

항해99) 4주차:: 주특기 숙련; 📚쿠키 & 세션 & JWT 본문

개발/항해99 9기

항해99) 4주차:: 주특기 숙련; 📚쿠키 & 세션 & JWT

hyuunii 2022. 10. 11. 16:10

01. 쿠키와 세션

- 보통 서버가 클라이언트 인증을 확인하는 방식은 대표적으로 쿠키, 세션, 토큰의 3가지.

- 쿠키는 클라가 어떤 웹사이트를 방문할 경우, 그 사이트를 사용하고 있는 서버를 통해 클라의 브라우저에 설치되는 작은 기록 조각 파일들(내용은 key-value 형식의 문자열 덩어리임) 각 사용자마다 브라우저에 정보를 저장하니 고유 정보 식별이 가능. 브라우저(클라이언트)가 서버에 요청(접속)을 보내면, 서버는 클라의 요청에 대한 응답을 작성할 때 클라이언트측에 저장하고 싶은 정보를 응답 헤더의 set-cookie에 담음. 이후 해당 클라는 요청을 보낼 때마다, 매번 저장된 쿠키를 요청 헤더의 cookie에 담아 보냄. 서버는 쿠키에 대한 정보를 바탕으로 해당 요청의 클라이언트가 누군지 식별하거나 정보를 바탕으로 추천 광고를 띄우거나 한다.

- 쿠키는 요청시마다 그 값을 그대로 주고받기에 유출 및 조작 당할 위험이 존재함. 이러한 보안 이슈 때문에 세션은 비번 등 클라의 민감한 인증 정보를 브라우저가 아닌 서버 측에 저장하고 관리. 단점은 세션 저장소에 요청 잔뜩 쌓이면 서버 부하 과해지는 것, 세션 ID 자체를 탈취당하면 즉각 대응하긴 힘들다는 점(서버에서 IP 특정을 통해 해결할 수 있긴 함)


02. JWT(Json Web Token)가 무엇?

header(어떤 암호화를 사용하여 생성된 데이터인지), payload(개발자가 원하는 데이터를 저장), signature(이 토큰이 변조된건지 아닌지 확인)로 구성

- 단순히 데이터를 표현하는 방식(토큰 기반 인증 시스템? 클라가 서버에 접속을 하면 서버에서 해당 클라에게 인증되었다는 의미의 '토큰'을 부여함. 이것은 유일하며, 토큰을 발급받은 클라는 또 다시 서버에 요청을 보낼 때 요청 헤더에 토큰을 심어서 보냄. 서버는 그걸 서버의 토큰과 일치하는지 체크하여 인증 과정 처리)

- 쿠키, 세션과의 차이점은? jwt 데이터를 브라우저로 보내도 쿠키처럼 자동으로 저장 안됨. 변조가 거의 불가능하고 서버에 데이터를 저장하지 않아 서버를 Stateless(무상태, 서버가 언제든 죽었다 살아나도 똑같은 동작 함)로 관리할 수 있기 때문에 최근 많이 쓰임. Stateful(상태 보존? 서버가 죽었다 살아났을 때 조금이라도 동작이 다른 경우. 로그인 정보를 서버에 저장하는 경우!) 서버가 스스로 어떤 기억을 갖고 다른 결정을 하냐 마냐의 차이라고 생각하면 더 쉽다.

- 기존의 세션기반 인증은 서버가 파일이나 db에 세션정보를 가지고 있어야 하고 이를 조회하는 과정이 필요하기 때문에 많은 오버헤드가 발생. 하지만 토큰은 세션과는 달리 서버가 아닌 클라에 저장되기 때문에, 메모리나 스토리지 등을 통해 세션을 관리했던 서버의 부담을 덜 수 있음. 토큰 자체에 데이터가 들어있기 때문에 클라에서 받아 위조되었는지 판별만 하면 되기 때문.

 

- json 형태의 데이터를 안전하게 교환하여 사용할 수 있게 해줌

- 변조가 불가능, 복호화는 아무데서나 가능(그래서 serialize라고 부름)

- 서버에서 JWT를 serialize를 해서 다른데 보내면 변조는 불가능해도 아무데서나 열어볼수는 있음. 애초에 어디서든 열어볼 수 있게 만든것. 그러니까 중요하지 않은 정보만 담아서 보낼것.


03. JWT는 어떻게 쓰는것?

1) 오픈소스 라이브러리 이용

: node.js에서 가장 사용량이 많은 라이브러리인 jsonwebtoken 사용ㄱㄱ

npm init
npm i jsonwebtoken -S

 

2) 우리가 원하는 json 데이터 암호화

- 데이터 암호화

const jwt = require("jsonwebtoken");

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

개발자가 담은 데이터는 payload에 담기기 때문에 jwt.io에서 위의 코드 치고 발급받은 토큰을 Encoded에 넣고 돌리면 Decoded 된 데이터가 payload에 담겨서 출력된다. 이때 verify signature 부분에 secret key까지 제대로 입력하면 signature verified라고 잘 뜸.

 

- 데이터 복호화: decode

const jwt = require("jsonwebtoken");

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0fQ.6XFgtNglH9hIzz5y8jAcI0g5kDnlAvnTTbxKIcL2CHY";
const decodedValue = jwt.decode(token);

 

- 데이터 검증: verify

const jwt = require("jsonwebtoken");

const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJteVBheWxvYWREYXRhIjoxMjM0fQ.6XFgtNglH9hIzz5y8jAcI0g5kDnlAvnTTbxKIcL2CHY";
const decodedValue = jwt.verify(token, "myesecretkey");

 

- 이걸 어떻게 쓰남?

: 클라이언트(브라우저)는 암호화 된 데이터를 전달받아 다양한 수단(쿠키, 로컬 스토리지 등)에 저장함. 그 후 API 서버에 요청을 할 때 서버가 요구하는 HTTP 인증 양식에 맞게 보내주어 인증을 시도함. 놀이공원의 자유이용권과 비슷.

  • 회원가입: 회원권 구매
  • 로그인: 회원권으로 놀이공원 입장
  • 로그인 확인: 놀이기구 탑승 전마다 유효한 회원권인지 확인
  • 내 정보 조회: 내 회원권이 목에 잘 걸려 있는지 확인하고, 내 이름과 사진, 바코드 확인

04. Access Token, Refresh Token

사용자 👉🏻 서버 : 1. ID, PW 보냄

사용자 👈🏻 서버 : 2. 액세스 토큰, 리프레시 토큰을 발급

사용자 👉🏻 서버 : 3. 클라는 서버한테서 받은 JWT를 로컬 스토리지 또는 쿠키 등에 저장. API를 서버에 요청할때 Authorization header에 Access Token을 담아 보냄

사용자 👈🏻 서버 : 4. 서버가 할 일은 클라가 header에 담아 보낸 jwt가 내 서버에서 발행한 토큰인지 일치 여부 체크. 일치하면 인증 통과, 아니면 통과 놉. 인증 통과되면 payload의 유저 정보들을 select해서 클라에 돌려줌.

사용자 👉🏻 서버 : 5. 클라가 서버에 요청을 했는데, 만일 액세스 토큰의 시간이 만료되면 클라는 리프레시 토큰을 이용해서

사용자 👈🏻 서버 : 6. 서버로부터 새로운 액세스 오큰 발급받음.

 

1) Access Token: 사용자의 권한이 확인(e.g. 로그인) 되었을 경우 해당 사용자를 인증하는 용도로 발급

- 이전에 cookie로 jwt를 발급하고 설정한 expire 기간이 지날 때 인증 만료되게 하는 것 또한 Access Token.

- 사용자가 Access Token을 가지고 인증을 요청할 경우 Token을 생성할 때 사용한 비밀키(secret key)를 가지고 인증하기 때문에, 복잡한 설계없이 코드를 구현할 수 있고 여러 분기를 거치지 않아도 됨.

- Access Token의 경우 Statelss 방식. 즉, jwt를 이용해 사용자의 인증 여부는 확인할 수 있지만, 처음 발급한 사용자 본인인지는 확인할 수 없음.

- Access Token은 그 자체로도 사용자를 인증하는 모든 정보를 갖고 있음. 그렇기 때문에 토큰을 가지고 있는 시간이 늘어날 수록 탈취되었을 때 피해가 더욱 커지게 됨.

- 만약 토큰이 탈취되었다고 인지하더라도 개발자들은 해당 토큰이 탈취된 토큰인지 알 수 없고, 고의적으로 만료를 시킬 수도 없을 것. 그러므로 언제든지 사용자의 토큰이 탈취될 수 있다고 생각을 하고, 피해를 최소화 할 수 있는 방향으로 개발을 진행해야 함😊

 

2) Refresh Token: Access Token처럼 해당하는 사용자의 모든 인증 정보를 관리하는 것이 아닌, 특정한 사용자가 Access Token을 발급 받을 수 있게 하기 위한 용도로만 사용됨.

- Refresh token은 사용자의 인증정보를 사용자가 가지고 있는 게 아닌, 서버에서 해당 사용자의 정보를 저장소 또는 별도 db에 저장하여 관리. 그렇기에 서버에서 특정 token 만료가 필요할 경우 저장된 token을 제거하여 사용자의 인증 여부를 언제든지 제어할 수 있음. 즉, 사용자에게 발급한 token이 탈취당할 경우 피해를 최소화할 수 있음.

- 실제 세계에서 사용하는 otp와 같이 짧은 시간 내에서만 인증 정보를 사용할 수 있게 한 후 주기적으로 재발급하여 토큰이 유출되더라도 오랜 기간동안 피래를 입는 게 아닌 짧은 기간동안만 사용하도록 하여 피해를 최소화 할 수 있게 함.

- 언제든지 토큰이 탈취될 수 있다는 것을 가정하고, 탈취를 막는것이 어렵다면 탈취된 토큰자체를 사용할 수 있는 기간을 줄여서 피해를 막아 보자는 취지.