-
⌨️ 로그인 방식
로그인을 처리하는 방법으로 생각할 수 있는 방법
- 유저에게 ID / Password를 받아 서버의 DB에 있는 데이터와 비교하는 방법
- 세션 방식 사용
- Token 방식 사용
ID / Password 사용 방법 👎
- 로그인 유지가 되지 않는다.
- 페이지를 이동할 때마다 로그인을 다시 해야한다.
⇒ 로그인을 유지하면서 안전한 방법이 필요하다.
세션 방식 👊 - 서버 유지
세션 방식은 서버의 메모리, DB와 같은 서버의 자원들을 사용해 사용자의 정보를 유지시키는 방식이다.
- 토큰 방식보다 보안에 강하다
세션을 사용하는 경우 매 Request마다 서버는 쿠키를 받아, 세션 ID를 보고 해당 ID와 일치하는 유저를 찾아야한다.
- 요청이 있을 때마다 세션 DB를 조회해야 한다.
⇒ 유저가 증가할 수록 DB 리소스가 더 필요하다.
단점
- 서버의 확장성이 떨어진다.
- 서버의 자원이 많이 필요하다
- 트래픽을 분산하기 위해 여러 대의 서버를 사용할 때, 사용자는 처음 로그인한 서버에만 요청을 보내야한다.
- (만들어진 세션을 참조해야 하기 때문에)
Token 방식 👍 - 클라이언트 유지
토큰 방식은 사용자가 로그인을 하면 서버에서 발행한 토큰을 통해 브라우저의 저장소에 토큰을 유지시키는 방법
⇒ 토큰 = JWT
- 세션 DB가 필요가 없다.
- 유저 인증을 하기 위해 서버가 많은 일을 하지 않아도 된다.
유저 인증을 하는데 필요한 정보를 토큰에 저장한다.
- 이 토큰을 클라이언트(브라우저)에 전달한다.
- 페이지 요청시 서버는 해당 토큰이 유효한지만 검증하면 된다.
🚨 JWT는 암호화 되지 않았다
→ 누구나 열어서 해당 컨텐츠를 볼 수 있다.
따라서, 비밀정보를 JWT안에 뒤서는 안된다.
서버에 저장을 하지 않아 서버에 확장성이 있다.
- 로그인시 해당 토큰이 유효한지만 체크하면 어떤 서버로 요청을 보내도 상관이 없다.
🔥 JWT
💡 JWT (JSON Web Token)
두 개체에서 JSON 객체를 사용하여 가볍고 자가수용적인(self-contained)방식으로 정보를 안전성있게 전달당사자 간의 정보를 안전하게 전송하기 위한 컴팩트하고 독립적인 방법을 정의하는 개방형 표준(RFC 7519)
JWT는 JSON 데이터를 Base64 URL-safe Encode를 통해 인코딩하여 직렬화 한 것이고 토큰 내부에는 위변조 방지를 위해 개인키를 통한 전자서명도 들어있다.
Base64 URL-safe Encode
- 일반적인 Base64 Encode에서 URL에서 오류 없이 사용하기 위해
- ‘+’, ‘/’ 를 각각 ‘-’, ‘_’로 표현한 것.
JWT가 유용한 시나리오
- 회원 인증
- 두 개체 사이에 정보 전달을 할 때 사용
특징
- 자가수용적이다 (Self-contained)
- JWT는 필요한 모든 정보를 자체적으로 가지고 있다.
- JWT 시스템에서 발급된 토큰은 토큰에 대한 기본정보, 전달 할 정보, 토큰이 검증되었다는 것을 증명해주는 signature를 포함하고 있다.
- 쉽게 전달 가능하다.
- 자가수용적이기 때문에 두 개체에서 손쉽게 전달 가능
- HTTP 헤더에 넣어서 전달, URL 파라미터로 전달 가능 등
- HMAC 알고리즘으로 비밀 또는 RSA 또는 ECDSA를 사용하는 공용키/비밀키 쌍을 사용하여 서명할 수 있다.
⚒ Structure
JWT는 . 을 기준으로 세 파트로 나뉜다.
1. Header
{ "alg": "서명 시 사용하는 알고리즘", "typ": "타입" }
JWT에서 사용할 타입과 해시 알고리즘의 종류가 담겨져 있다.
2. Payload
{ "sub": "hyeonsu.jung", "exp": 1623235123, "name": "beom seok" }
토큰에서 사용할 정보의 조각들인 Claim 이 담겨 있다. (JWT를 통해 실제 알 수 있는 데이터)
- payload에는 여러 claim을 담을 수 있다.
- 서버와 클라이언트가 주고받는 시스템에서 실제로 사용될 정보에 대한 내용을 담고 있다.
Claim
key - value 형식으로 이루어진 한 쌍의 정보
Claim 종류
- registered(등록된) claim
- public(공개) claim
- private(비공개) claim
registered claim
- 토큰에 대한 정보를 담기위한 미리 정의된 클레임의 집합
public claim
- 사용자 정의 클레임, 공개용 정보 전달을 위해 사용한다.
private claim
- 비공개 클레임은 당사자간에 정보를 공유하기 위해서 만들어진 사용자지정 클레임이다.
- 이름이 중복되어 충돌이 일어날 수 있어 유의해야 한다.
3. Signature
- 시그니처에서 사용하는 알고리즘은 헤더에서 정의한 알고리즘 방식(alg)을 활용한다.
signature = Base64Url(Header) + . + Base64Url(Payload) + . + server's key
Header, Payload를 Base64 URL-safe Encode를 한 이후 Header에 명시된 해시함수를 적용해, 개인키(Private Key)로 서명한 전자서명이 담겨 있다.
Header / Payload는 단순히 인코딩된 값이라 제 3자가 복호화 및 조작할 수 있다.
- 하지만, Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화 할 수 없다. ❌
- 따라서, Signature는 토큰의 위변조 여부를 확인하는데 사용된다.
❗ How
인증 과정
- 사용자가 ID, Password를 입력하여 서버에 로그인 인증 요청
- 서버에서 클라이언트로부터 인증 요청을 받으면, [ Header, PayLoad, Signature ] 를 정의한다.
- Header, PayLoad, Signature를 각각 Base64로 암호화하여 JWT를 생성하고 이를 쿠키에 담아 클라이언트에게 발급한다.
- 클라이언트는 서버로부터 받은 JWT를 로컬 스토리지에 저장한다.
- API는 서버에 요청시 Authorization header에 AccessToken을 담아 보낸다.
- 서버는 Header에 담아 보낸 JWT가 서버에서 발행한 토큰인지 일치 여부 확인, 일치 여부 확인 후 인증 통과 시켜준다.
- 인증이 되면 페이로드에 있는 유저의 정보를 select해서 클라이언트에 돌려준다.
- 클라이언트가 서버에 요청을 했을 때, 엑세스 토큰이 만료되면 클라이언트는 리프레쉬 토큰을 이용해 서버로 부터 새로운 엑세스 토큰을 받는다.
토큰 인증 신뢰성
서버는 토큰이 유효한 토큰인지 확인하는 것이 중요하다.
→ 클라이언트로 부터 받은 JWT 헤더, payLoad를 서버의 Key 값을 이용해 시그니처를 다시 만들고 이를 비교해서 일치한 경우만 인증을 통과한다
JWT 장, 단점
JWT 장점
- Header와 PayLoad를 가지고 Signature를 생성하므로 데이터 위변조를 막을 수 있다.
- 인증 정보에 대한 별도의 저장소가 필요없다.
- JWT는 토큰에 대한 기본 정보와 전달한 정보 및 토큰이 검증됐음을 증명하는 서명 등 필요한 모든 정보를 자체적으로 가지고 있다.
- 서버는 무상태가 된다(Stateless)
- 확장성이 좋다.
- 토큰 기반으로 다른 로그인 시스템에 접근 및 권한 공유가 가능하다
- 모바일 어플리케이션 환경에서도 잘 동작한다(세션은 불가능)
JWT 단점
- JWT는 토큰의 길이가 길어, 인증 요청이 많아지면 네트워크 부하가 심해진다.
- PayLoad는 암호화 되지 않아 중요 정보는 담을 수 없다.
- 토큰을 탈취당하면 대처하기 어렵다.
🔑 Access Token & Refresh Token
Refresh Token이 필요한 이유
Access Token 만 사용하는 인증 방식의 문제는 제 3자에게 탈취당할 경우 보안에 취약하다는 점 이다
- Access Token은 토큰이 만료되기 전까지, 토큰을 획득한 사람은 누구나 접근이 가능하기 때문
JWT는 토큰에 유효시간을 부여하는 식으로 탈취 문제에 대응
- 유효기간을 짧게 하면 그만큼 로그인을 자주해서 새로운 Token을 발급받아야 하는 불편함이 있다.
- 유효기간을 길게 하면 토큰을 탈취당했을 때 보안에 취약해진다.
유효기간을 짧게 하는 좋은 방법??? ⇒ Refresh Token
Refresh Token
Access Token과 똑같은 JWT이다.
Access Token은 접근에 관여, Refresh Token은 재발급에 관여하는 토큰
처음 로그인 시 서버는 클라이언트에게 Access Token과 Refresh Token을 동시에 발급한다.
- 서버는 DB에 Refresh Token을 저장하고
- 클라이언트는 Access Token 과 Refresh Token을 쿠키, 세션 혹은 웹 스토리지에 저장한다.
- 요청이 있을 때 마다 이 둘을 헤더에 담아서 보낸다.
Refresh Toekn은 긴 유효기간을 가지고, Access Token이 만료됐을 때 새로 재발급을 해주는 열쇠가 된다.
- Access Token이 만료되었다면, 서버는 같이 보내진 Refresh Token을 DB에 있는 것과 비교해서 일치할 시 다시 Access Token을 재발급하는 원리
사용자가 로그아웃을 할 경우 저장소에서 Refresh Token을 삭제해 사용이 불가능하도록 한다.
- 새로 로그인하면 서버에서 다시 재발급하여 DB에 저장
📦 Redis 로 JWT Refresh Token 관리하기
Refresh Token은 저장소에 저장을 해두고 사용자가 토큰을 재발급을 요청하면 검사를 해야한다.
- Refresh Token도 유효기간이 있기 때문
이때, Refresh Token을 RDBMS에 저장하면 배치를 이용해 주기적으로 삭제를 해줘야 하는 불편함이 생긴다.
- 이때, redis를 사용하면 생성할 때 유효기간을 정해두고 따로 작업이 필요없이 만료된 토큰은 삭제된다.
Why Redis ❓
Redis는 key-value 쌍으로 데이터를 관리할 수 있는 데이터 스토리지이다.
기본적으로 레디스는 in-memory로 데이터를 관리해, 저장된 데이터가 영속적이지 않다.
- 데이터를 영구적으로 저장할 수 없는 대신, 굉장히 빠른 엑세스 속도를 보장받을 수 있다.
빠른 엑세스 속도와 휘발성이라는 특징으로 캐시의 용도로 레디스를 사용한다.
Refresh Token의 저장소로 레디스를 선택한 이유
- 빠른 엑세스 속도로 사용자 로그인시 병목이 되지 않는다.
- 레디스는 기본적으로 데이터의 유효기간(time to live)을 지정할 수 있다.
- refresh token을 저장하기에 적합하다.
- refresh token은 제거되어도 다른 데이터에 비해 덜 치명적이다
- 로그아웃 되는 정도
참고 자료
https://www.youtube.com/watch?v=tosLBcAX1vk&t=5s&ab_channel=노마드코더NomadCoders
https://velog.io/@junghyeonsu/프론트에서-로그인을-처리하는-방법
https://velog.io/@ypo09/JWTJSON-Web-Token-란-무엇인가
https://inpa.tistory.com/entry/WEB-📚-JWTjson-web-token-란-💯-정리
https://inpa.tistory.com/entry/WEB-📚-Access-Token-Refresh-Token-원리-feat-JWT
https://hudi.blog/refresh-token-in-spring-boot-with-redis/
https://wildeveloperetrain.tistory.com/21
'공부방' 카테고리의 다른 글
에러 로그 발생시 슬랙 알림 보내기 (0) 2022.11.23 Swagger 설정 (1) 2022.10.27