data life

[팀프로젝트] 로그인(Login) 구현 - JWT란? 본문

회고록

[팀프로젝트] 로그인(Login) 구현 - JWT란?

주술회전목마 2023. 6. 6. 14:39

출처 : https://img1.daumcdn.net/thumb/R1280x0.fpng/?fname=http://t1.daumcdn.net/brunch/service/user/dkta/image/mZyUgQW1H1vk_TFaK2FZbvZqyBM.png

이번 팀프로젝트에서 로그인/회원가입 구현 페이지를 맡게 되었다. 사실 첫 팀플인지라 개인프로젝트때와는 달리 백엔드 분들과의 소통도 굉장히 중요하다고 느꼈고 모든 게 처음이라 오랜만에 블로그 글을 작성하면서 끄적끄적,, 내용이나 생각 정리할 필요성도 뼈저리게 느끼게 되었다..ㅋㅋ 

개인프로젝트에서는 기본 로그인과 소셜로그인(google)을 이용하였다. 사실 firebase 라는 NoSQL을 처음 이용해보았고 생각보다 쉬웠다라고 생각해서 손쉽게 구현할 줄 알았지만 백엔드 지식이 무지한지라...

 

백엔드 분들이 알려주신 기본적인 로그인 구현 형식은 다음과 같다.

 

로그인 방식

  • 회원 아이디, 패스워드 입력
  • JWT 토큰 발행 ( 유지 시간 5 ~ 6 hour )
  • Refresh 토큰은 추가 행동때 새로고침
  • 사이트 내에서 회원가입한 회원만 기본 로그인 가능

소셜로그인 (카카오 + 네이버)

  • GET - 인가 코드
  • 카카오 계정, 패스워드 입력
  • 카카오 정보 활용 동의
  • 비동의 시 버튼 비활성화
  • 동의 → POST - 토큰 발급
  • 토큰 유효성 검증 후 로그인

user ERD는 다음과 같다.

id key PK Long
user_id Field 유저 아이디 varchar(50)
nick_name Field2 닉네임 varchar(50)
password Field5 비밀번호 varchar(50)
location Field3 지역 varchar(50)
profile_url Field4 유저 사진 varchar(255)

 

먼저, JWT 토큰에 대해 알아보기 전에 클라이언트 인증 방식에는 대표적으로 3가지가 있다는 것을 알아봄!

1. 쿠키인증
2. 세션인증
3. 토큰인증

 

1. 쿠키인증 🍪

 쿠키(Cookie)란?

브라우저 로컬에 저장되는 Key-Value 형태의 문자열 데이터 파일로, 브라우저가 종료되어도 파일로 남아있는 것이 특징이다.

각 사용자마다의 브라우저에 정보를 저장하니 고유 정보 식별이 가능한 인증 방법이다. 

단점이 있다면,

  • 보안 취약
  • 용량 제약 => 제한적 정보 저장
  • 쿠키 size ⬆️ 네트워크 과부하

2. 세션인증

 세션(Session) 이란?

브라우저가 종료되기 전까지 유저의 요청을 유지하게 해주는 기술로 요청마다 서버에서 처리하므로 쿠키보단 상대적으로 느린 것이 특징이다. 하지만 쿠키의 보안 단점을 어느정도 해결해 줄 수 있는 인증 방법이다.

세션 객체는 Key에 해당하는 SESSION ID와 이에 대응하는 Value로 구성되어 있다.
Value에는 세션 생성 시간, 마지막 접근 시간  User가 저장한 속성 등 이 Map 형태로 저장된다.

  • 쿠키를 포함한 요청이 외부에 노출되더라도 세션 ID 자체는 유의미한 개인정보를 담고 있지는 않음.
  • 요청이 많아지면 서버에 과부하 발생

3. 토큰인증

클라이언트가 서버에 접속했을 시, 인증의 의미로 '토큰(Token)'을 부여한다. (이때, 부여받은 토큰은 유일함)

위의 세션 인증과 달리 클라이언트에 저장하는 방식이기 대문에 서버의 부담을 덜 수 있고 토큰 자체에 데이터가 들어가 있기 때문에 위조가 되었는지 판별만하면 된다. 웹에는 쿠키와 세션이 존재하나 앱에는 존재하지 않기 때문에 앱에서 더 많이 사용 중!

 

1️⃣ 유저가 로그인함

2️⃣ 서버 측에서 유저에게 유일한 토큰을 발급해줌

3️⃣ 유저는 서버 측에서 받은 토큰을 쿠키나 스토리지에 저장해두고, 서버에 요청 시마다 해당 토큰을 HTTP 요청 헤더에 포함시켜 전달

4️⃣ 서버는 전달받은 토큰을 검증하고 요청에 응답함

5️⃣ 토큰에는 유저의 정보가 담겨 있어 db를 조회하지 않고도 유저정보를 알 수 있음.

📌 만일 액세스 토큰의 시간이 만료가 된다면, 클라이언트는 리프래시 토큰을 이용해서 서버로부터 새로운 엑세스 토큰을 발급받게 됨!

 

  • 토큰 자체의 데이터가 길어서 인증 요청이 많아질 경우 네트워크 과부하 발생
  • Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.
  • 토큰을 탈취당하면 대처가 어려움 =>  따라서 사용 기간 제한을 설정하는 식으로 극복.

그렇다면 JWT이란 무엇인가?

JSON Web Token 

인증에 필요한 정보들을 암호화시킨 JSON 토큰을 의미한다.

즉, JSON 데이터를 Base64 URL-safe Encode 를 통해 인코딩하여 직렬화한 것이며, 토큰 내부에는 위변조 방지를 위해 개인키를 통한 전자서명도 들어있다. 따라서 사용자가 JWT 를 서버로 전송하면 서버는 서명을 검증하는 과정을 거치게 되며 검증이 완료되면 요청한 응답을 돌려준다.

 

JWT의 구조

출처 : https://hanam88.com/kho-tai-lieu/63/64/gioi-thieu-ve-json-web-token-jwt.html

. 을 기준으로 Header, Payload, Signature를 의미한다.

  • Header : JWT 에서 사용할 타입과 해시 알고리즘의 종류 
  • Payload : 서버에서 첨부한 사용자 권한 정보와 데이터
  • Signature : Header, Payload 를 Base64 URL-safe Encode 를 한 이후 Header 에 명시된 해시함수를 적용하고, 개인키(Private Key)로 서명한 전자서명이 담김
  1.  

JWT Access Token / Refresh Token

  • Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.

위와 같이 JWT도 토큰 탈취의 위험성이 있기 때문에, 그대로 사용하는것이 아닌 Access Token, Refresh Token 으로 이중으로 나누어 인증을 하는 방식도 존재한다.

  • Access Token : 실제로 유저의 정보가 담긴 토큰으로, 클라이언트에서 요청이 오면 서버에서 해당 토큰에 있는 정보를 활용하여 사용자 정보에 맞게 응답을 진행
  • Refresh Token: 새로운 Access Token을 발급해주기 위해 사용하는 토큰으로, 짧은 수명을 가지는 Access Token에게 새로운 토큰을 발급해주기 위해 사용. (보통 DB에 유저 정보와 같이 기록된다.)

💡 정리하자면,

Access Token은 접근에 관여하는 토큰,
Refresh Token은 재발급에 관여하는 토큰의 역할로 사용되는 JWT이다.

 

그래서 구현은 어떻게? 🤔

글을 정리하다보니 JWT의 토큰 기반의 인증이 아무래도 서버 인증보다는 확장성이나 안정성 면에서 뛰어난 것으로 보인다. 백엔드분들이 왜 이 방식을 선택하셨는 지 어느정도 이해할 수 있었던 시간.. 특히나, 소셜로그인을 기본적으로 구현하려고 계획했기 때문에 다른 웹 서비스에서도 로그인하기엔 적합한 JWT를 이용하는 게 맞다고 본다.

이를 토대로, 내가 구현해야할 것들을 정리해보았다.

1. 로그인 폼 구현 : 아이디와 비밀번호 입력 후, 서버로 전송되도록 함

2. 토큰 수신 및 저장 : 서버에서 발급하는 JWT 토큰을 안전한 장소에 저장(브라우저의 로컬 스토리지? 쿠키? 세션?)

3. 토큰 활용 : 저장한 JWT토큰을 요청에 포함시키도록 함 (일반적으로 HTTP 요청 헤더의 "Authorization" 필드에 "Bearer" 접두사를 붙여 토큰 전송)

4. 토큰 갱신 : 지정한 유지시간(5 ~ 6 hour)이 지나면 만료가 되는데 만료된 토큰을 서버에 전송 시, 서버에서 다시 재발급한 새로운 토큰을 수신하고 기존 토큰과 교체해서 업데이트된 토큰을 저장하도록 함.

 

2. 토큰 수신 및 저장

사실 기존 firebase를 이용하여 개인프로젝트를 진행할 때에는 Context API를 이용하여 유저 로그인 상태를 전역상태로 관리하여 주었다.

이번 팀프로젝트에선 토큰 방식의 로그인을 어떻게 구현해야할지 생각해본 결과

 

보안에 주의해야 하는 유저 정보를 과연 상태 관리 라이브러리로 저장해도 되는가?

- 사실 Recoil은 상태를 관리하는 용도이기 때문에 중요한 정보인 인증 토큰을 저장하기엔 안전하다고 생각하지 않는다.

 

그렇다면 세션 저장 방식은?

- Session Storage에 대한 접근 및 제어는 자바스크립트를 통해 이루어지기 때문에 XSS 등 스크립트 기반 공격이 가능할 수 있음. 

 

그렇다면 쿠키를 사용해보는 것은?

- 사실 가장 좋은 방법으로 소개된 건 브라우저의 웹 스토리지 중 보안에 뛰어난 "HTTP Only" 쿠키를 이용하는 것이다. 

클라이언트 측에서 직접 접근이 불가하고, 쿠키를 통한 인증은 브라우저가 알아서 자동으로 처리해주기 때문에 여러모로 보안을 높일 수 있다. 추가로 Secure Cookie option을 이용한다면 웹브라우저와 웹서버가 HTTPS로 통신하는 경우에만 웹브라우저가 쿠키를 서버로 전송해준다.

 

 

 

 

참고 

https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-JWTjson-web-token-%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC

https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies