본문 바로가기

Programming👩🏻‍💻/Web Basic

Json Web Token

웹사이트에 수많은 사용자가 접속을 하는데 그 중 회원/비회원 여부에 따라 로그인 인증 결과에 따라 접근 권한을 부여를 할지 결정해서 응답을 해줘야한다.

특정 사이트에서 어떤 활동이든 사용자의 로그인 여부가 매번 확인해야된다.

 

Q) 브라우저에 사용자 로그인 정보를 저장한다면?

A) DB에 저장된 사용된 계정의 해시값 등을 꺼내온 정보가 사용자의 암호를 복잡한 알고리즘으로 계산한 값과 일치하는지 확인하는 과정이 필요하여 사이트에서 모든 활동에 대해 실행하기 굉장히 부담되는 작업이다.

시간+자원 손해/매번 아이디,패스워드 정보가 이동되면 보안상 취약

 

이러한 취약성을 보안하기 위해 전통적으로 사용된 '세션'

 

세션방식은 사용자가 로그인에 성공하면 서버는 '세션 표딱지'란 걸 출력한다.(a.k.a 입장표)

사용자 브라우저 측에 반쪽 찢은 세션표,

다른 반쪽은 자기 책상(메모리),서랍(하드디스크),데이터베이스(창고)에 적합한 곳에 둔다.

 

브라우저가 이 표를 Session ID란 이름의 쿠키[각주:1]로 저장하고

해당 사이트에 요청을 보낼때마다 이 표를 같이 실어보낸다.

 

이 세션 아이디가 사용자부터 실려오면 서버는 나머지 반쪽의 세션 표를 찾으면, authorazation(어서와제이션~)인것.

 

세션 아이디를 사용해서 어떤 사용자가 서버에 로그인 된것이 지속되는 상태를 '세션'이라고 한다.

즉 사용자, 서버에서 나눈 세션이 어디에 둔 것을 기억하고 있는게 세션.

 

이 방식에도 허점이 있다.

표딱지 수는 곧 사이트를 이용하는 사용자들 수인데, 동시다발로 접속자가 많아지면 메모리 부족 현상이 온다.

그리고 서버에 문제가 생겨서 꺼져버리면 휘발성으로 죄다 날라가 주옥됨.

에러가 발생해서 죄다 엎어버리면 = 서버 재부팅해야될 상황이 오면 메모리 리셋..

 

그렇게 되면 사용자들은 죄다 로그인이 튕겨서 재접속해야됨.

비휘발성인 하드에다가 세션을 두게되면? 메모리에서 바로바로 호출하는 것보다 느려서 문제..

 

서비스가 어느정도 규모가 있어서 서버를 여러대 두고 사이트를 운영할 때는 여러 서버가 가동중인데 클라이언트 요청이 들어오면 해당 요청으로 여러 서버들 사이에 분산을 해서 로드발란싱을 해주는데,,

 

만약 로그인은 1번 서버에 연결돼서 하고, 이메일 페이지로 가는 요청은 3번 서버로 분산되면

1번엔 세션이 있는데 3번엔 없으면 세션 유지가 또 안되는 것.. 환장

 

그렇다고 요청이 각자 할당된 서버로만 보내지는 것도 번거로운 일..

다른 해결책으로는 표딱지(세션)을 공용 창고(DB 서버)에 넣어두거나 레이디스, MenCached 같은 메모리형 데이터베이스 서버(a.k.a. 길다란 공용 책상)을 따로 둬서 세션을 관리.....근데 이것도 책상이 엎어지면 상황이 달라지지 않음

 

그래서 결론은 서버 설계는 빡친다.(??????

 

이 부담을 없애기 위해 '토큰 방식'이 출현한다.

 

JSON WEB TOKEN . 줄여서 JWT 

 

토큰 방식은 JWT를 사용하는 서비스에서 사용자가 로그인을 하면 토큰이라는 표를 출력해서 건네준다.

대신 이번엔 세션처럼 사용자,서버 반반 나눠갖는 것이 아닌 그냥 몽땅 준다.

 

사용자가 받는 토큰은 아래와 같이 마침표기준으로 3개의 데이터(header, payload, verify signature)로 조합되어 있다.

사용자 토큰

시작은 2번 페이로드로 Base64로 디코딩 하면 JSON형식으로 아래 예시 데이터가 구성되어있다.

payload 정보

이 토큰은

a. 누가 누구에게 발급했는지

b. 토큰이 유효기간

c. 토큰을 통해 공개하기 원하는 내용 등을

서비스 측에서 원하는대로 담을 수 있다.

 

이러한 토큰에 담긴 사용자 정보 등의 데이터를 Claim이라고 한다.

 

사용자가 로그인하고 나서 받은 토큰에 이 정보들이 클레임이라는 것으로 실려온다.

이후 사용자로부터 서버한테 보내진다.

사용자가 받아서 갖고 있는 토큰 자체에 이런 정보들이 들어있으면 서버 요청마다 일일이 데이터베이스에서 찾는 작업이 줄어든다.

 

Base64로 인코딩되어 있다면 사용자가 자바스크립트로 뭐든 다시 디코딩 작업으로 볼 수 있고, 악용이 될 수 있는건데?

토큰 유효기간을 한 50년 늘려놓는다던가!

관리자 여부 같은게 토큰에 담겨 있으면 이건 true로 바꿈 권한 인가를 받으면?! 리얼 지옥

 

하지만 토큰은 1헤더, 3번 서명 데이터로 인해서 보안할 수 있다.

 

1번 헤더는 디코딩하면 두가지 정보가 담겨있다.

a. 토큰 타입 (=JWT)

b. alg(알고리즘약자) 알고리즘 타입

header 데이터

1번 헤더, 2번 페이로드 그리고 '서버에 감춰놓은 비밀 값'이 셋을 암호화 알고리즘에 넣고 돌리면 3번 서명값이 나온다!

 

서버는 요청에 토큰 값이 실려들어오면 1번,2번의 값을 '서버에 감춰놓은 비밀 값''와 함께 돌려봐서 계산된 결과값이 3번 서명 값과 일치하는 결과가 나오는지 확인한다.

 

3번 서명 값과 계산 값이 일치하고 유효기간도 지나지 않았다면 사용자는 로그인 된 회원으로서 인가를 받을 수 있다.

 

서버는 사용자들의 상태를 어딘가에 따로 기억해 둘 필요가 없이 이 비밀 값만 손에 쥐고 있다면, 요청이 들어올때마다 스캔하여 사용자들을 걸러낼 수 있다!

이처럼 상태값을 갖지 않는걸 stateless, 세션은 반대로 stateful이다.

 

그럼 JWT가 짱 먹음 되지 않을까?

NOPE...

JWT를 세션처럼 stateful이 된다면 기억하는 대상의 상태를 언제든 제어할 수 있다는 의미이다..!

예를 들면 한 기기에서 로그인 가능한 서비스를 만들려는 경우 PC에서 로그인한 상태의 어떤 사용자가 핸드폰에서 또 로그인하면 PC에서는 로그아웃되도록 기존 세션을 종료할 수 있는 것..

 

세션은 서버에서 끊어버리면 되는데, JWT에서는 불가능한 일이다.

사용자에서 이미 건낸 토큰을 뺏을 수도 없고, 토큰 발급 내역이나 정보를 서버가 기록하고 있는 것도 아니다..

 

서버가 치트키를 쥐고 있기때문에 통제가 어렵다.. 편하긴한데 쩝-_-;

 

그래서 보안 방법으로 토큰을 두개 발급한다.

수명이 몇시간~ 몇 분 이하로 짧은 access토큰꽤 길게, 보통 2주 정도로 잡혀 있는 refresh 토큰이다.

 

두 토큰을 발급하고 사용자에게 보내고 나서 refresh 토큰은 상응값을 데이터베이스에도 저장한다.

 

클라이언트는 access토큰의 수명이 다하면 refresh 토큰을 보낸다.

서버는 그걸 데이터베이스에 저장된 값과 대조해보고, 맞다면 새 access토큰을 발급해준다.

 

인가를 받을 때 쓰는 수명이 짧은 토큰이 access토큰,

엑세스 토큰을 재발급 받을 때 쓰는 것이 refresh 토큰이다.

 

그렇게 되면 엑세스 토큰이 탈취 당해도 유효기간을 그리 길지 않다.

누구를 강제 로그아웃 시키려면 리프레시 토큰을 갖다가 DB에서 지워버려가지고 토큰 갱신이 안 되게 하면 되는 것~!

 

그치만 토큰이 살아 있는 동안에는 암것도 대처할 수 없는게 큰 약점이긴하다.

 

토큰 하나로만 쓰는것보단 낫겠지만 이것도 완벽한 해결책은 아님....고구미

 

 

참고자료 얄코코딩 https://www.yalco.kr/48_jwt/

  1. 브라우저에 저장되는 정보 [본문으로]

'Programming👩🏻‍💻 > Web Basic' 카테고리의 다른 글

2 JavaScript  (0) 2021.04.27
JavaScript 1  (0) 2021.04.26