반응형

1. 개요

 JWT는 인증, 인가에 사용하는 Json 형식의 토큰이고, 인코딩되어있다 정도로만 알고있었으나 이번 멘토링 과제에 적용하기 위해 공부해보니 자세해서 정리된 글들이 많이 보였다. 열심히 구글링하여 이해한 내용들과 필자가 궁금한 점들을 정리해보았다.

 

 


2. JWT란?

 JWT는 Json Web Token의 약자로 '웹에서 사용되는 Json 형식의 토큰'이다. 토큰에는 사용자의 권한 및 기본 정보, 서명 알고리즘 등이 포함되어 있다. 개인정보는 저장하지 않는데, 이유는 정보성 데이터가 저장되는 Payload는 쉽게 조회할 수 있기 때문이다.

 JWT는 서버에 저장되지 않고 클라이언트에서 저장하기 때문에 서버의 메모리 부담을 덜 수 있다. 이처럼 서버에 상태값을 저장하지 않는 것을 무상태(Stateless)라 하며 JWT는 이 무상태 성질을 갖는다.

 

JWT 사용 프로세스는 다음과 같다.

 1. 로그인을 성공했을 때 JWT를 생성하여 클라이언트에게 응답해준다.

 2. 클라이언트는 요청마다 Authrization 헤더에 Bearer 타입으로 JWT 값을 넣어 서버로 보낸다.

 3. 서버는 JWT 값을 검증하여 요청 처리 여부를 결정한다.

 


3. JWT 구조

 JWT는 Header, Payload, Signature로 구성되며, 각 부분은 온점(.)으로 구분된다.

 Header에는 토큰의 유형과 서명 알고리즘, Payload에는 권한 및 기본 정보, Signature에는 Header와 Payload를 Base64로 인코딩 한 후 Header에 명시된 해시 함수를 적용하고, 개인키로 서명한 값(이를 전자서명이라고 한다)이 담겨있다. Signature를 통해 토큰에 대한 위변조 여부를 체크할 수 있다.

 

 아래 이미지는 JWT 에 대한 인코딩, 디코딩 데이터를 확인할 수 있는 jwt.io 라는 사이트에서 제공하는 예제를 캡쳐한 것이다. 빨간색 부분이 Header, 보라색 부분이 Payload, 하늘색 부분이 Signature에 해당한다. 

jwt.io

 

 


4. 토큰은 암호화 된거 아냐?

 앞서 Payload는 쉽게 조회할 수 있어 개인정보를 저장하지 않는다고 했었는데, 위 사진을 보면 '어딜봐서 쉽게 조회할 수 있다는거지? 딱 봐도 암호화 되어있는것 같은데?' 라고 생각할 수 있다. 하지만 Header와 Payload는 Base64 URL-safe Encode 형식으로 인코딩되어있을 뿐이고 Signature 만 암호화 되어있다.

 아래는 실제 Header와 Payload를 base64 디코딩 사이트에서 디코딩한 결과이며, 토큰 값은 인코딩 값이라는 것을 알 수 있다. (인/디코딩 사이트 https://www.convertstring.com/ko/EncodeDecode/Base64Decode)

구분 토큰 값 디코딩 값
Header eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 {"alg":"HS256","typ":"JWT"}
Payload eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG5Eb2UiLCJpYXQiOjE1MTYyMzkwMjJ9 {"sub":"1234567890","name":"JohnDoe","iat":1516239022}

 

 


 

5. 토큰의 인가 정보를 바꾸면?

 지금은 Payload에 sub, name, iat와 같은 값들이 들어있으나 일반적으로 "role":"admin" 혹은  "role":"user"와 같은 권한 정보도 포함시킨다. 그런데 여기서 다음과 같은 궁금증이 생겼다. 권한정보를 임의로 바꾼다면 admin 권한도 갖을 수 있지 않을까?

 클라이언트가 응답 받은 JWT의 Payload 값을 디코딩한 후 role에 대한 value값을 user 에서 admin으로 바꾼 후 다시 인코딩하여 서버에게 전달하면 admin 권한을 갖는 것과 동일하게 처리될지 궁금했다. 어차피 서버에서도 인증 정보를 따로 저장하지 않기 때문이다.

결론은 그딴 상술은 통하지 않는다 였다.

 JWT의 세번째 항목인 Signature가 토큰의 위변조를 체크하기 때문이다. Signature는 Header와 Payload를 Base64로 인코딩 한 후 Header에 명시된 해시 함수를 적용한 값이 들어있다. 서버에서 토큰을  발급한 시점의 Header와 Payload 값에 대한 해싱 값이 Signature에 있으며, Header와 Payload 값이 위변조 됐다면 해싱 값이 일치하지 않아 위변조된 토큰임을 알아차릴 수 있다.

 

 


 

6. 클라이언트의 토큰 저장위치

JWT를 Access Token, Refresh Token으로 분리하고 refresh Token은 http Only secure 쿠키에, 액세스 토큰은 로컬변수에 저장하는 방식을 채택해야한다. 추가로 http only secure 옵션과 XSS 공격을 막는 필터를 추가해야한다. 여기서 액세스 토큰은 인가, 인증정보가 들어있는 토큰, 리프레시 토큰은 액세스 토큰을 재발급하기 위한 토큰이다.

 CSRF 공격을 막기 위해서는 쿠키에 액세스 토큰이 있어서는 안된다. 그러므로 쿠키에는 리프레시 토큰을 저장하고 액세스 토큰은 로컬변수에 저장해야한다. http only는 스크립트를 통한 쿠키 접근을 막는 옵션이고, secure는 네트워크 감청에 의한 쿠키 탈취를 막는 옵션이다. secure가 적용되어 있을 경우 https가 적용된 서버에 대해서만 통신이 가능하게 된다.

 이렇게 되면 해커가 CSRF 공격을 하더라도 쿠키에는 액세스 토큰이 없기 때문에 인증 불가 상태가 되어 요청이 차단되고, http only secure 쿠키 특성 상 리프레시 토큰 조회는 불가능하다. 액세스 토큰은 로컬 변수에 저장되어 있으나 XSS 공격을 막으므로 스크립트를 통한 접근도 불가능하다. (참고: https://pronist.dev/143)

 

 

 

 

 

반응형

+ Recent posts