문제 상황
1.
로그인할 때, 사용자가 입력하는 id, password를 db에 기억하고 있다가 같으면 로그인 X
-> db가 해킹 당하면 전부 유출되는 문제가 존재.
2.
페이지를 들어갈 때마다 로그인 상태를 '유지' 시켜주는 문제
So, 인증, 인가가 중요
- 인증 : Authentication (로그인)
- 인가 : Authorization (로그인이 유지되는 상태에서 일어나는 것)
Authorization 전통적인 방식 - session
1. 사용자가 로그인에 성공하면 '서버'는 '세션 딱지'같은 걸 출력한다.
이 딱지를 찢어서 반쪽은 '사용자의 브라우저'로, 반쪽은 '메모리, db ... ' 등 에 저장함.
(메모리에 저장하는게 다른 저장 공간보다는 빠르게 처리해준다.)
2. 브라우저가 이 딱지를 session ID란 이름의 쿠키로 저장 (쿠키 : 브라우저에 저장되는 정보)
이 브라우저는 다음 사이트에 요청을 보낼때마다 이 딱지를 실어 보냄.
3. 요청에 session ID라는게 들어오면 -> 서버는 메모리에서 맞는 짝이 있는지를 찾는 것
어떤 사용자가 서버에 로그인 되어있음이 지속되는 상태를 '세션'이라고 함
- 서버가 표 반쪽을 기억해두고 있는 자체
허점
- 사용자가 동시에 많이 접속하면 메모리가 부족해질 것
-> 메모리 특성상 서버에 문제가 있어서 꺼져버리기라도 하면 정보가 날라감 (휘발성)
사용자는 다시 로그인해야하는 상황이 발생
- 메모리 대신 하드에 저장을 하면? 메모리보다 느리다.
- 서버를 여러대 둔다면? (서버를 분산해서 load balancing)
만약에 로그인은 1번 서버, 검색 요청하는 서버는 2번 서버에 연결을 하면 2번 서버에는 그 사용자에 대한 정보(딱지)가 없으니 세션 유지가 되지 않음.
- 복잡한 구성과 환경에서 (사용자가 많아져서 메모리가 부족해진다던지, db에 연결을 하면 무거워진다던지) 어떤 상태를 기억해야한다는 설계가 힘듦
session 방식 해결 -> '토큰 방식'인 jwt
- json web token
- jwt를 사용하는 서비스에서는 사용자가 로그인을 하면 'token'이라는 표를 (세션때와 마찬가지로) 출력해서 줌
대신 세션 때와 다른 점은 '찍어서 주지 않고' 그냥 준다. 즉, 서버가 뭔가를 기억하고 있지 않다는 점
- 토큰의 생김새 : feihfisfnjskldoujkalHUgirgeg....
인코딩/암호화된 3가지 데이터리를 이어붙임 (잘 살펴보면 중간에 마침표가 2개 들어가있다.)
각각 header, payload, verify signature로 구분됨
(1) payload 부분
- (base64로) 디코딩을 해보면 json 형식으로 여러 정보들이 들어있음
이 토큰을 누가 누구에게 발급했는지, 언제까지 유효한지, 서비스가 사용자에게 이 토큰을 공개하기 원하는 내용 (사용자 닉네임, 관리자 여부 등) 등의 정보가 있는데 서비스 측에서 원하는대로 담을 수 있음
- 이렇게 토큰에 담긴 사용자 정보 등의 데이터를 claim이라고 함
- 사용자가 이런 정보(claim)을 갖고 있으니, 서버는 요청이 들어올 때마다 일일히 db에서 찾아봐야 할 수고가 줄어들게 됨
- 허점 : 디코딩해서 볼 수 있기에, 보안에 허점이 존재 (그래서 header, verify signature가 필요)
(2) header 부분
- 디코딩해서 보면
1) type : 토큰 타입 (여기에는 언제나 jwt가 들어간다. 타입이 jwt여야 jwt인 것이기에 고정값이다.)
2) alg : 알고리즘의 약자. verify signature을 만드는데 사용될 알고리즘이 지정됨.
예를 들어 HS256 등 여러 암호화 방식 중 하나를 지정한다.
header, payload, '서버에 감춰놓은 비밀 값' 이렇게 3개를 이 알고리즘에 넣으면 -> verify signature가 나온다.
- 서버는 요청에 토큰이 들어오면, header, payload, '서버에 감춰놓은 비밀 값'을 계산해봐서 계산된 결과값이 verify signature 와 같은지를 확인한다.
- 만약 header, payload 중에 조금이라도 어떤 정보가 수정되었다면 패스가 되지 않음.
✅ 이처럼 시간에 따라 바뀌는 어떤 상태값을 '안' 갖는 걸 'stateless하다'고 함 (반대는 stateful)
그럼 다 jwt 방식으로 쓰면 되는 것인가
- 세션을 대체하기에는 jwt 방식에는 큰 결점이 존재
세션처럼 stateful해서 모든 사용자들의 상태를 기억하고 있으면, (구현이 부담스럽지만) 기억하는 대상의 상태를 언제든 제어할 수 있음
ex) 기기 한 대에서만 로그인 가능한 서비스를 만들었는데, 세션 방식으로 하면 다른 기기에서 로그인을 해왔을 때 pc에서는 로그아웃되도록 컨트롤 가능
<-> 반대로 토큰은 한 번 줘버리 토큰은 회수도 추적도 불가능. 즉, 내가 정보를 가지고 있지 않아서 편하기는하나 통제가 불가능
더 최악의 상황은 어떤 해커가 토큰을 가져가버리게 되면 그 토큰을 무효화시킬 방법이 없음
- 서버가 아는 것은 오직 토큰이 유효한지 여부. 유저 계정을 잘 관리해야 하는 경우 session을 활용한다.
(session처럼 db에 저장하지 않기 때문에 현재 인스타그램에 로그인한 계정이 몇 개인지, 넷플릭스 한 아이디에 몇 명의 유저가 쓰고 있는건지 등을 확인 불가)
- 그래서 생각보다 jwt를 많이 쓰지 않는다.
jwt를 사용하는 경우는 만료 시간을 가깝게 잡아서 토큰의 수명을 아주 짧게 주는 경우.
1) access 토큰 : 수명이 몇 시간이나 몇 분이하로 짧음
2) refresh 토큰 : 꽤 길게 (2주 정도) 수명이 있음
- access 토큰과 refresh 토큰을 발급하고 클라이언트에게 보냄
- refresh 토큰은 상용값을 db에도 저장함
- 클라이언트는 access 토큰 수명이 다하면 refresh 토큰을 보냄
- 서버는 그 토큰을 db에 저장된 값과 대조해보고 맞다면 새로운 access 토큰을 발급해줌
- refresh 토큰만 안전하게 관리된다면 이게 유효할동안은 access 토큰이 만료될 때마다 다시 로그인할 필요가 x
(아래는 기존에 작성했던 내용인데 삭제하려다가, 위에 내용으로 확실히 개념 잡고 다시 읽어봐도 될 것 같아서 남긴다. (흐름이 산재되어 있기에 개념 잡고 읽으면 흐름을 스스로 잡는데 도움이 될 듯ㅎㅎ))
쿠키
- 서버가 나의 브라우저에 데이터를 넣을 수 있음
for ‘나'를 기억하기 위해 - 만약 내가 구글에 접속하고 검색어를 입력하면 서버는 내 요청에 대해 response를 주는데 이때 같이 ‘쿠키’도 주는 것. 그리고 내 브라우저는 그 쿠키를 간직해놨다가, 내가 웹사이트에 방문할 때마다 request와 함께 쿠키를 보내게 됨. (들고다니는 열쇠같은 것)
- 쿠키는 인증에 대한 정보 뿐만 아니라 다른 정보들도 담을 수 있음. ex) 언어 설정 (그래서 내가 다음에 방문할 때는 영어가 아니라 한국어가 뜬다던지)
- 제한 (1) 쿠키는 도메인에 따라 제한됨. 만약 내가 구글 쿠키를 받았으면 구글 웹사이트에서만 유효
- 제한 (2) 유효기간 존재. 서버가 정함
stateless 개념
- 서버로 가는 요청들이 이전 request와는 독립적으로 다뤄진다는 것
- 요청이 끝나면 서버는 이 요청이 누구건지 다 잊어버림 -> 요청할 때마다 우리가 누구인지 알려줘야 함
✅ 이 방법 중 하나가 바로, session
session
- 세션을 활용해서 안드로이드, ios도 다 만들 수 있지만 이때는 쿠키 사용이 불가. (쿠키는 브라우저에만 있기에)
✅ 그래서 token을 사용 - 세션에서 기억해둘 점은, 현재 로그인한 유저들의 모든 세션 id를 db에 저장해야한다는 점
요청이 올때마다 서버는 쿠키를 받고, 세션 id를 보고, 세션 id와 일치하는 유저를 찾음. 즉 요청이 올때마다 db를 찾아야 함
✅ jwt가 등장함 (token 형식)
jwt
- 세션 db를 가질 필요 x
서버는 유저 인증한다고 많은 일을 하지 않아도 됨 - 로그인하려면 서버에 signed info / token을 서버에 보내야 함
서버는 토큰을 받으면 해당 사인이 유효한지 체크한 후, 유저로 인증 - jwt에서 서버는 유저를 인증하는데 필요한 정보를 '토큰'에 저장.
해당 토큰을 브라우저 주고.
(다음에) 페이지 요청시 서버는 해당 토큰이 유효한지만 검증하면 됨 (db를 거칠 필요가 없음)
<-> session은 세션에 대한 모든 정보가 세션 db에 있어서 세션 id를 줬어야 했다는 점 - jwt는 암호화되지 않음 (암호화되었다면 아무도 읽을 수 없고, 이해할 수 없음)
jwt는 누구나 열어서 해당 컨텐츠를 볼 수 있음 (그래서 중요한 내용은 jwt에 두면 안 됨) - (단점) jwt를 사용하면 생성된 토큰을 추적하지 않음. (session처럼 db에 저장하지 않기 때문에 현재 인스타그램에 로그인한 계정이 몇 개인지, 넷플릭스 한 아이디에 몇 명의 유저가 쓰고 있는건지 등을 확인 불가)
- 서버가 아는 것은 오직 토큰이 유효한지 여부
-> 유저 계정을 잘 관리해야 하는 경우 session을 활용
캐시
- 사용 빈도가 높은 데이터를 고속으로 엑세스 할 수 있는 위치에 두는 것
- ‘바로 사용할 테니까 일단 여기에 두자’
- 단점) 고속으로 엑세스 할 수는 있지만 데이터를 잃을 위험이 있어서 캐시 데이터가 손실돼도 괜찮은 경우에 주로 사용
캐시 vs 쿠키
-> 캐시 는 로딩 시간을 줄이기 위해 브라우저 (클라이언트 측)에 웹 페이지 자원을 저장하는 데 사용되는 반면, 쿠키 는 사용자 기본 설정을 추적하기 위해 브라우징 세션을 저장하는 데 사용
출처 : https://youtu.be/tosLBcAX1vk, https://youtu.be/1QiOXWEbqYQ
캐시 vs 쿠키 : https://ko.gadget-info.com/difference-between-cache
캐시 출처 : 책 [it인프라구조]