Django-Rest-Framework(DRF)로 JWT 기반 Authentication 세팅하기(with simplejwt) — blacklist 기법으로 보안 강화하기(3)

Chanjong Park
Chan’s Programming Diary
7 min readApr 21, 2021

저번 포스트에서는 dj_rest_auth와 simplejwt를 사용해서 회원가입, 로그인 동작을 구현해 보고, 직접 간단한 API를 작성해 접근 시에 토큰의 사용 유무를 확인해 보았다.

앞서 작성했듯이 refresh token의 유효기간 내에서 access token의 재발급은 제한이 없기 때문에, 보안적인 이슈가 필연적으로 발생할 수 밖에 없다. 한번 발급된 토큰은 임의로 삭제할 수 없기 때문에 refresh token이 중간에 탈취되기라도 한다면 개인정보 문제가 된다.

이러한 방법을 방지하고자 나온 방법이 Blacklist 기법이다. 보통 악영향을 미치는 사람 또는 기업을 대상으로 리스트화 해놓는 것을 칭하는 단어지만, 여기서는 유저의 로그아웃을 통해 더이상 필요없는 토큰이나 악의적으로 탈취된 token을 서버에서 사용할 수 없도록 관리를 해주는 방법 중 하나이다.

그래서 이번 포스팅에서는 Blacklist를 적용해 실습을 한번 해보겠다. 여기서 공식 문서에 나와있는 걸 볼 수 있다.

settings.py

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
#myapp
'accounts',
#django-rest-auth
'rest_framework',
'rest_framework_simplejwt.token_blacklist',
'dj_rest_auth',
'dj_rest_auth.registration',#django-allauth
'allauth',
'allauth.account',
'allauth.socialaccount',
]

blacklist 테이블을 만들고 기능을 활성화 시키기 위해 처음에 rest_framework_simplejwt.token_blacklist를 추가해 주었다.

실제 DB에서는 이 두 테이블 들을 확인할 수 있다.
from datetime import timedeltaSIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=2),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
}

그리고 또 위 설정들을 해주었다. 저번 포스트에서 access token 재발급을 해보았는데, 그 기능을 하는 viewset이 TokenRefreshView 다. 아래 두 개의 설정은 TokenRefreshView 을 위한 설정이다.

문서를 보면

ROTATE_REFRESH_TOKENS

When set to True, if a refresh token is submitted to the TokenRefreshView, a new refresh token will be returned along with the new access token. This new refresh token will be supplied via a “refresh” key in the JSON response. New refresh tokens will have a expiration time which is determined by adding the timedelta in the REFRESH_TOKEN_LIFETIME setting to the current time when the request is made. If the blacklist app is in use and the BLACKLIST_AFTER_ROTATION setting is set to True, refresh tokens submitted to the refresh view will be added to the blacklist.

이렇게 설명되어 있다. 저번 포스트에서 refresh token을 통한 토큰 재발급을 해봤는데, 위에서 말하는 TokenRefreshView 가 재발급하는 url에 매핑되어있는 viewset이다.

요약하자면 True로 설정 시, TokenRefreshView를 통한 재발급이 실행될 때 새로운 refresh token, access token을 재발급하고 JSON으로 응답을 한다. 또한 BLACKLIST_AFTER_ROTATION 가 True로 설정될 경우, 기존에 있는 refresh token은 blacklist된다. 필자는 False로 설정하고 access token만 받도록 했지만, True로 한다면 기존에 있던 Token은 blacklist해주는 것을 추천한다.

Logout

이제 마지막으로 JWT 환경에서 로그아웃을 구현해보자. 세션 환경의 로그아웃은 세션 초기화를 하면 간단하지만, 앞에서 말했듯이 한번 발급된 토큰은 임의적으로 삭제가 불가능하기 때문에 서버에서 직접 차단해야한다. allauth에서 로그아웃이 구현되어 있으니 사용해보자.

Postman으로 로그인 및 Response

주의해야 할 점은, Postman으로 API 이용 시, CSRF 오류를 해결하기 위해 Header 파일에 X-CSRFToken을 따로 추가해줘야한다. 위에 Cookie에 csrftoken=~ 을 복붙하면 된다.

로그인해서 받은 refresh token을 Body에 넣어 요청하고, 성공하게 되면

이렇게 메세지가 온다. 그리고 DB를 확인해보면 토큰이 blacklist된 것을 확인 할 수 있다.

총 세 글에 걸쳐 DRF 환경에서 JWT 인증의 기본 로직을 실습해 보았다. 글 작성간의 텀이 길어서 글의 흐름이 이상할 지도 모르니 양해 바란다. DRf에서 Blacklist를 실습하는 글이 한국어로는 거의 없기 때문에, 혹시 구현을 필요로 하게 된다면 간단한 실습 정도는 될 것이라고 생각한다.

소스 코드

django_auth_simplejwt

참고 사이트

How to blacklist JSON Web Tokens in Django?

JWT(Json Web Token)이란?

장고(django)에 JWT 사용하기

참고 문서

dj-rest-auth

django-allauth

Simple JWT

--

--