Django Rest Framework에는 3가지 built-in 인증 옵션이 존재한다. 오늘은 어떻게 API 인증이 동작하고, 각 방법의 장단점에 대해 이야기 해본다.
가장 쉬운 HTTP 프로토콜을 통한 인증 방법은 "Basic" Authentication 이다. 클라이언트가 HTTP 요청을 할 때, 반드시 인증 credential을 보내야한다.
위의 단계를 거치고 나면, 클라이언트는 앞으로 모든 요청에 Authorization HTTP header credential 을 보내야 한다.
Authorization credential은 base64로 인코딩된 <username>:<password>
의 형태로 보내진다. (wsv:password123 => d3N2OnBhc3N3b3JkMTIz)
이 방법의 장점은 간단하다는 점이다. 하지만 단점이 많다. 첫째로, 서버는 모든 요청에 대해 유저네임과 패스워드를 확인해야한다. 비효율적이다. "유저가 인증됨"과 같은 어떤 토큰을 보내주는 편이 더 좋다. 둘째, 유저 credential은 암호화없이 그대로 보내진다. 보안에 매우 취약하다. 때문에 Basic Authentication은 오직 HTTPS를 통해서만 사용해야 한다.
Monolithic 웹사이트들은 세션과 쿠키를 이용한 인증을 오래도록 사용했다. 클라이언트가 credential을 가지고 인증을 하면, 쿠키에 저장할 수 있는 세션 아이디 를 보내주었다. 세션 아이디는 모든 HTTP 요청 헤더에 붙어 전송된다.
서버는 credential을 포함한 주어진 유저의 모든 정보를 포함하는 세션 객체를 찾아서 사용한다. 이 방법은 서버와 클라이언트 모두가 기록을 하고 있어야하기 때문에 stateful 하다.
Django REST Framework 의 기본 설정은 Basic Authentication과 Session Authentication의 조합이다. 장고의 Session Authentication 시스템을 활용하고 Basic Authentication을 통해 모든 요청의 HTTP 헤더에서는 session ID를 필요로한다.
장점은 user credential이 한 번만 전송되기 떄문에 좀 더 안전하다. 서버가 user credential을 매번 확인할 필요가 없고, session ID만 맞춰보기 때문에 훨씬 빠르다.
하지만 단점도 있다. session ID는 로그인 한 브라우저 안에서만 유효하다. 여러 도메인에 사용할 수 없다. 웹과 모바일을 모두 지원하는 프론트엔드에서 문제점으로 작용한다. 그리고 여러 서버를 사용하는 대형 사이트들에게 세션 객체를 항상 최신으로 유지하는 일은 어렵다. 각 서버간의 세션 객체의 정확성을 어떻게 나타낼지 미지수이다. 마지막으로 인증이 필요하지 않아도 쿠키가 모든 요청에 포함되어 보내진다.
이러한 문제점들로 인하여, 여러 프론트 엔드를 가진 API 서비스에게 세션 기반의 인증은 좀 처럼 사용되지 않는다.
Single Page Application 이 떠오르면서 최근 가장 인기 있는 인증 방법인 Token Authentication 이 있다. 토큰 기반 인증은 stateless 하다. 가장 처음 user credential을 서버에 보내면, unique한 토큰을 생성해주고 클라이언트에게 보내준다. 이를 cookie 또는 local storage 에 저장한다. 이 토큰은 HTTP 요청 헤더에 담겨서 보내지고, 서버는 토큰을 보고 유저가 인증됨을 확인한다. 유저가 인증되었는지 기록을 확인하는 것이 아니고 그냥 토큰이 유효한지 아닌지만 확인한다.
Cookies vs. LocalStorage
Cookie는 server-side 정보를 읽기 위해 사용되어 진다. 4KB의 사이즈를 가지며 HTTP 요청마다 자동으로 함께 보내진다. LocalStorage는 client-side 정보를 담기 위해 생겨났다. 5120KB 이며, HTTP 요청마다 기본으로 보내지지 않는다. 토큰을 쿠키와 로컬 스토리지 둘 다 저장하는 것은 XSS 공격에 취약하다. 현재 가장 좋은 방법은httpOnly
와Secure
플래그를 사용하여 토큰을 쿠키에 저장하는 것 이다.
토큰 기반 인증에는 여러가지 이점이 있다. 토큰이 클라이언트 쪽에 저장되기 때문에, 세션 객체를 최신으로 유지하기 위해 서버를 늘리는 일은 더 이상 문제되지 않는다. 그리고 토큰은 여러 프론트엔드와 공유될 수 있다. 토큰은 웹사이트나 모바일 앱에서나 똑같은 유저를 대표한다. session ID는 공유되지 못하는 것과 대조적이다.
잠재적인 단점은 토큰이 거대해질 수도 있다는 것이다. 하나의 토큰은 한명의 모든 유저 정보를 담고 있다. 토큰은 모든 요청에 담겨지기 때문에, 이것의 사이즈를 관리하는 것은 퍼포먼스 이슈가 될 수 있다.
토큰을 구현하는 방법은 여러가지가 있다. Django REST framework의 built-in TokenAuthentication은 기본에 충실하도록 설계되었다. 그렇기 때문에 토큰의 만료시간을 지원하지 않는다. 유저마다 하나의 토큰만 생성하기 때문에, 유저는 웹사이트와 모바일 앱에서 동일한 토큰을 사용하게 된다. 해당 유저의 정보가 로컬 (웹사이트와 모바일)에 저장되기 때문에, 이 두개의 클라이언트 정보를 관리하고 업데이트하는 것이 문제를 가져올 수도 있다.
JSON Web Token (JWT) 은 서드파티 패키지를 통해 Django REST Framework에 붙일 수 있는 새롭고 강화된 버전의 토큰이다. JWT는 클라이언트별 고유한 토큰일 생성하고, 토큰 만료 시간을 포함시킬 수 있는 능력이 있다. 서버에서 직접 생성되거나, AuthO 같은 서드파티 서비스에서 생성된다. 또한 암호화가 가능하여 단순 HTTP 연결에서도 좀 더 안전하게 사용가능 하다.
JWT를 써야한다 ㅋㅋ 🤣🤣🤣🤣🤣🤣