인가(Authorization)는 유저가 요청하는 request를 실행할 수 있는 권한이 있는 유저인가를 확인하는 절차이다.
예를 들어서 어떤 유저가 쇼핑몰의 카탈로그를 볼 수는 있지만, 카탈로그의 수정은 안된다는 점이다. 특정 권한이 있는 유저만 카탈로그의 수정이 가능하다.
이렇게 권한이 있는 유저인지 판별하고 인증하는 과정이 인가(Authorization) 과정이라고 한다.
access token
을 생성한 뒤 유저(브라우저)에게 준다(로그인 성공 시 브라우저에 토큰 발급). access token
에는 유저 정보를 확인할 수 있는 정보가 들어가 있어야 한다. ex)user_idaccess token
을 첨부해서 보낸다.access token
을 복호화 한다.이에 대한 해결책은
서버에서 세션 저장소를 사용하기 때문에 서버에서 추가적인 저장공간을 필요로 하고 이에 따라 부하도 커질 가능성이 높다.
서버의 확장성이 좋지 않다. A 서버에 있는 세션 저장소에 세션 정보가 있는데, 브라우저가 B 서버에 요청을 보내면 A 저장소에 있는 세션 정보를 B 에 공유해줘야하는 복잡한 작업이 필요하기 때문에 확장성이 좋지 않다.
세션/쿠키는 별도의 저장소의 관리가 필요하지만 JWT는 토큰을 발급한 후 base64 encoding 으로 암호화 한 signature 만 검증만 하면 되기 때문에 서버측에서는 유저의 세션을 유지 할 필요가 없으므로 추가 저장소가 필요 없다.
Stateless 한 서버를 만드는 데 가장 중요한 사실이다.
이에 대한 해결책은
access token
의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급하여 피해를 줄일 수 있다. Payload 정보는 따로 암호화 되지 않기 때문에 넣을 수 있는 정보가 제한적이다.
세션/쿠키 방식에 비해 JWT의 길이가 길기 때문에 인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생하게 된다.
{
"typ": "JWT",
"alg": "HS256"
}
헤더에는 타입과 알고리즘 방식을 표기한 뒤 base64 로 encoding 한다.
참고: JSON 형태의 객체가 base64 로 인코딩 되는 과정에서 공백 / 엔터들이 사라진다. 따라서, 실제로는 다음과 같은 문자열을 encoding 하게 된다.
{"alg":"HS256","typ":"JWT"}
{
"iss": "velog.io/tk_kim", # 토큰 발급자
"exp": "1485270000000", # 만료일(현재보다 뒤로 설정되어 있어야 함, NumericDate 로 표기)
"userId": "11028373727102", # 비공개 클레임 (private claim)
"username": "tk_kim" # 비공개 클레임
}
이것 또한 base64 로 encoding 한다.
이 서명은 header의 인코딩 값(base64) 과 payload 의 인코딩값을 합친 것을 주어진 비밀키로 해쉬를 하여 생성한다.
Header(base64) + Payload(base64) 를 SECRET KEY 로 HASH 한 뒤, base64 로 encoding 해주면 된다.
이제 모든 파트를 . 으로 구분하여 하나로 합치면 다음과 같은 결과가 나온다.
python 에서는 이 모든것을 쉽게 해줄 수 있는 모듈 jwt 가 존재한다.
사용법을 알아보자.
로그인 인증 (Authentication) 을 수행해서 인증정보를 확인한다.
맞으면 jwt.encode()
의
user_id 를 브라우저에 보내고 나중에 요청받을 때 user_id 로 db에 접근하여 어떤 유저인지 확인하면 된다.
SECRET_KEY 를 통해 header 와 payload 를 hash 하여 signature 를 만든다. 따라서 이 값이 절대 유출되면 안된다.
유출되면 해커가 이 값을 이용해 signature 를 복사해 낼 수 있다.
참고자료 :
1. 쉽게 알아보는 서버 인증 1편(세션/쿠키 , JWT) https://tansfil.tistory.com/58
2. [JWT] JSON Web Token 소개 및 구조 https://velopert.com/2389