본문 바로가기
Web/Django

[DRF] 5. Authentication / Permission

by 조엘 2021. 1. 15.

안녕하세요! 파피몬입니다! 🎊

백엔드 API 서버가 어떻게 구축되는지 조금 알 필요가 있다고 생각이 들어서 Django Rest Framework(이하 DRF)를 공부하고 있습니다. 아직 개발자를 지망하는 학생입니다! 틀린 부분이 있으면 댓글로 알려주시면 정말 감사하겠습니다!!

 

 

*** 개요 ***

이번엔 Authentication과 Permission에 대해 알아본다. 둘 모두 안전한 서버 관리를 위해 필수적인 요소들이다.

또한, 둘은 View 호출 시 가장 먼저 체크되는 속성들이다. 하나씩 살펴보자!

 

 

*** Authentication ***

Authentication은 서비스 이용에 있어서 클라이언트 자신이 권한이 있는지를 서버에 알려주는 과정이다.

 

인증에 알맞은 형식으로 request가 들어온다면, 200대의 정상적 HTTP 상태 코드 응답을 하게 된다.

그렇지 않은 경우, 서버는 401 Unauthorized, 403 Permission Denied 중 하나의 응답을 하게 된다. 

 

Authenticated 과정을 거치면서 request.user 변수와 request.auth 변수가 설정이 된다. 

인증에 성공한 경우, request.user는 User 객체로 설정되고 request.auth는 인증 방식에 따라 다르게 설정된다. 

인증에 실패한 경우, request.user는 django.contrib.auth.models.AnonymousUser, request.auth는 None으로 설정된다. 

 

본 포스팅에서는 3가지 BasicAuthentication, SessionAuthentication, TokenAuthentication 인증 방식을 다룬다. 

구현 방식은 획일적이라 먼저 소개하고, 각 Authentication의 이론적인 부분에 대해서 조금 공부해보자. 

 

앞선 포스팅에서 살펴본 Pagination처럼, Authentication 역시 전역 설정단일 모델 설정이 각각 가능하다.

TokenAuthentication은 유저의 정보에 일대일 대응이 되는 토큰을 발행해주기 때문에 따로 모델을 가지고 있다. 따라서 TokenAuthentication을 사용 시 settings.py에 rest_framework.authtoken을 app에 등록해주고 migrate까지 진행해 주어야 한다. 

 

전역 설정은 간단하다. 장고 프로젝트의 settings.py에서 REST_FRAMEWORK 딕셔너리에 다음과 같이 추가하면 된다. 

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
    ],
}

 

단일 모델에서 Authentication을 적용하고자 한다면, 다음과 같이 authentication_classes 변수에 사용할 인증 방법을 리스트 안에 적어서 설정해 주면 된다. 

# views.py
from rest_framework.authentication import BasicAuthentication

class UserPostViewSet(viewsets.ModelViewSet):
    authentication_classes = [BasicAuthentication]
    ...

 

BasicAuthentication

BasicAuthentication은 HTTP 자체 기본 인증에 기반한 인증 방식이다. 사용자의 ID와 PW가 base64 인코딩을 거쳐 HTTP 헤더로 넘어오게 된다. 안타깝게도, 해당 인코딩 방식은 쉽게 디코딩될 수 있어서 보안이 취약한 인증 방식이다. 따라서 해당 인증 방식은 필히 HTTPS와 함께 써야 한다고 공식 문서에서 얘기한다. 

 

HTTPS와 함께 쓰면 BasicAuthentication이 안전해지니 많이 쓰일까? 간단하고 좋은데? 그렇지 않다. 해당(참고: security.stackexchange.com/questions/988/is-basic-auth-secure-if-done-over-https) 글을 보면, 왜 BasicAuthentication을 잘 안 쓰는지 알 수 있다. 

 

우선 BasicAuthentication은 사용자로부터 하여금 끊임없이 비밀번호를 매 요청마다 보내게 한다. 따라서 비밀번호는 웹 브라우저에 캐싱이 될 것이고, 이는 CSRF 공격과 같은 보안 위험에도 노출될 가능성이 높다. 

 

그러면 우리가 사용하는 웹 사이트는 어떻게 우리의 정보를 보호해 주고 있을까? 간단한 동영상을 보고 가자. 

 

 

또한 해당 글이 이해에 큰 도움을 주었다. (참고: velopert.com/2350)

 

SessionAuthentication

SessionAuthentication은 사용자의 ID, PW가 인증되면 서버 DB에 해당 사용자의 session을 저장한다. 서버는 응답으로 클라이언트에게 session ID를 넘겨주게 되고, 이는 브라우저의 쿠키에 저장이 된다. 이후 클라이언트는 서버와 session ID로 인증을 진행하게 된다. 

 

DRF 공식 문서에 따르면, 해당 방식은 AJAX 방식의 통신, 즉 비동기적으로 브라우저와 서버가 데이터를 주고받을 때 유리하다고 한다. 하지만 이 역시 session ID가 노출이 된다면 CSRF 등의 위험에 노출이 되니, 추가적인 보안 조치가 이루어져야 한다. (그래서 주구장창 Django 기본 강의에서 Form을 저장할 때 {% csrf_token %}을 쓴 거였군!)

 

TokenAuthentication

TokenAuthentication은 사용자의 ID, PW와 일대일 매칭 되는 고유 key를 생성하고 발급한다. 이를 토큰이라 부르고 DB에 저장한다. 사용자는 이제 토큰을 통해 서버에 접속할 수 있다. 

 

DRF 공식 문서에 따르면, 해당 방식은 native desktop, mobile client와 같은 client-server 환경에서 쓰이기 좋다고 한다. 나도 모바일 컴퓨팅 프로젝트를 할 때 Tmap API를 연동해서 안드로이드 앱을 만든 적이 있었는데, 이런 방식으로 나를 인증했겠구나 싶었다. 

 

다만 유튜브 영상을 보면 JWT의 개념이 등장하는데, 이는 DRF의 TokenAuthentication 과는 다르게 동작한다. 해당 방식은 stateless 한 방식으로 서버에 유저의 인증 정보를 저장하지 않는다. DB에 해당 인증 정보를 저장하지 않아도 되니, 확장성이 더 좋아진다고 한다. (아직 설명할 단계까지는 못돼서ㅜㅜ velopert.com/2350 참고하시면 더 좋을 듯합니다!)

 

 

*** Permission ***

Permission은 서비스를 어느 정도로 이용할 수 있는지, 유저에 따라 다른 권한을 부여한다.

 

DRF는 다음과 같은 Permission class를 제공한다. 

  - AllowAny: 인증된 요청이던, 비인증 요청이던 모두 View 호출을 허용하겠다.

  - IsAuthenticated: 인증된 요청(등록된 사용자)에 대해서만 View 호출을 허용하겠다. 

  - IsAdminUser: Staff User의 요청만 View 호출을 허용하겠다. 

  - IsAuthenticatedOrReadOnly: 인증된 요청은 View 호출을 허용하고, 비 인증된 요청은 안전한 http method인 get/head/option 등 만 허용하겠다. 

 

Permission 역시 전역 설정 단일 모델 설정이 각각 가능하다. 

 

전역 설정은 간단하다. 장고 프로젝트의 settings.py에서 REST_FRAMEWORK 딕셔너리에 다음과 같이 추가하면 된다.

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

 

단일 모델에서 Permission을 적용하고자 한다면, 다음과 같이 permission_classes 변수에 사용할 인증 방법을 리스트 안에 적어서 설정해 주면 된다. 

# views.py
from rest_framework.permissions import IsAdminUser

class UserPostViewSet(viewsets.ModelViewSet):
    permission_classes = [IsAdminUser]

 

 

반응형

'Web > Django' 카테고리의 다른 글

[DRF] 4. Pagination / Filtering & Search  (0) 2021.01.15
[DRF] 3. ViewSet / Router  (0) 2021.01.14
[DRF] 2. APIView / Mixins / Generic CBV  (0) 2021.01.13
[DRF] 1. 개요 / REST / JSON / Serializer  (0) 2021.01.13
[Django] 자소설 닷컴 총 복습!  (0) 2020.08.29

댓글