dj-rest-auth 파헤치기-login 기초

Hyeon Soo·2024년 5월 20일

http 프로토콜을 이용한 통신은 stateless하다는 특징이 있습니다. 직역하면 상태를 저장하지 않는다는 의미인데, 이는 이전 호출과 다음호출이 서로 연관 없이 분리되어있음으로 나타납니다. 결국 특정 호출이 실패해도 다른 호출이 연쇄적으로 실패하지는 않는다는 것입니다.
이런 특성은 가용성을 크게 향상하였는데, 사용자정보도 프로토콜 수준에서 저장하지 않는 문제가 있게 됩니다. 그런데 매 호출마다 인증을 새로 진행하여 서비스를 구성하자니 불편하고, 트래픽의 낭비에다가 인증정보가 계속 날아가는 것은 보안에도 별로 좋지 않습니다.

이를 해결하려면 결국 인증한 내용을 어딘가에 저장하여 매번 새로운 인증 없이 해당 저장 정보의 확인으로 대체하는 것입니다. 즉, 프로토콜 자체는 stateless하기 때문에 필요한 state를 어딘가에는 저장해두는 것입니다.
이때 저장 주체, 주고받는 인증전용 정보가 정보를 담는지, 그렇지 않은지, 어디에 하는지의 차이가 세션 방식과 토큰 인증방식이라고 볼 수 있습니다.

세션

이 방법은 인증이 발생하여 처리될 때 무작위 문자열로 구성된 세션 아이디를 생성, 데이터베이스나 서버측 저장소에 저장하고, 이를 사용자에게 전달합니다. 그리고 사용자는 해당 세션 아이디를 다른 요청을 보낼때 같이 전달하고, 서버는 요청때 받은 세션 아이디를 저장소와 대조하여 저장되어있는지, 어떤 유저대상인지 확인하여 인증을 처리하는 방법입니다.

이때 세션아이디 자체는 아무런 의미도 없도록 구성합니다. 그래서 세션 아이디를 그 자체를 통해 뭔가 해봤자 아무 의미 없습니다.

세션의 특징은 보안성이 훨씬 높다는 점입니다. 앞서 설명한 것 처럼 세션아이디를 획득하여 다른 정보를 얻을 수도 없는데, 털려도 서버측에서 세션을 삭제해버리면 바로 아무 쓸모가 없어집니다. 세션 아이디를 통해 유의미한 유저 정보를 얻으려면 서버를 직접 털어야하는데 개인 컴퓨터의 보안수준과 서버의 보안 수준은 다르기 때문에 난이도가 크게 오릅니다.

반면에 저장소에 세션아이디를 확인하는 연산이 매번 필요하게되고, 이는 그 자체로 비용입니다. 사용자가 많아질수록 그 비용은 점점 커지기 때문에 인증만으로도 서버에 부담을 주게 됩니다. 보통 인증용 디비 및 서버의 분할을 통해 레코드를 나눠서 속도처리를 높이거나, 캐싱하거나, nosql등의 방법으로 속도 문제 자체는 완화가 가능합니다.
다만 세션은 세션 저장소에 인증 여부가 묶여있게 된다는 특징이 있어서, 세션 저장소를 분할할 경우 특정 인증이 분할된 저장소 중 어디로 향해야하는지, 다른 요청이 들어왔을 때 어떤 저장소에서 확인해야할지 전체 구성에서 명확하게 정의해야하고, 이를 처리하는 개별 로직을 따로 구성해주어야하는 불편함이 있습니다. 즉, 성능 문제를 완화하기 위해 서버의 분할 등 스케일의 수평 확장이 필요한데, 세션의 특징으로 인해 수평 확장이 쉽지 않다는 문제가 동시에 존재합니다.

토큰

로그인시 유저를 인증한 후, 유저의 정보 중 특정 정보를 바탕으로 토큰을 생성하고, 해당 토큰이 요청에 전달되면 토큰을 해석하여 유저정보를 얻어 인증을 처리하는 방식입니다. 이 방법은 토큰 자체가 유저의 정보를 들고 있어서 탈취 후 해석을 당하면 유의미한 정보가 탈취당하는 것과 같으며, 세션과 달리 서버에서 능동적으로 인증상태를 취소하기 힘듭니다.

반대급부로 서버쪽에서 저장을 할 필요 없으면서, 인증 여부 자체를 확인하기 위한 과정이 세션보다 훨씬 가볍습니다. 사용자가 크게 늘어나더라도 성능상의 부하가 훨씬 낮은 수준으로 증가합니다. 그리고 토큰에 담겨있는 정보가 개별 API사용에는 큰 문제 없을 정도로 충분하다면, 서버를 수평 확장한 후에도 개별 서버에서 알아서 인증 처리가 가능하기 때문에 세션과 달리 확장이 매우 쉽습니다.

보안의 문제는 서버에 존재하는 생성시의 알고리즘 및 키를 잘 관리하면 탈취하더라도 해석이 매우 어려워지기에 토큰 자체의 탈취로 인한 정보 유출을 막는 한편, 토큰 생성시 필요한 유저 정보를 최대한 무의미한 정보로 하는 방식이 있습니다. 이 경우 정보가 유출되더라도 해당 정보로 유저의 다른 정보를 얻으려면 서버를 해킹해야하는데 이는 어렵습니다. 더욱이 토큰은 만료시간 설정이 가능한데, 만료 시간을 따로 설정하여 영속성을 제거, 탈취하더라도 계속적인 이용이 불가능하도록 할 수 있습니다.

Json Web Token

인증을 위한 토큰 가운데 아예 웹 표준을 따르기 때문에 가장 보편적으로 쓰이는 타입의 토큰입니다. 그 자체로 특정 인증내용을 담고 있으면서, 전자서명을 통해 토큰의 변조를 탐지하고 있습니다. 크게 header, payload, signature의 세 부분으로 구성되며, 각 부분은 . 으로 구분하고 있습니다.

  1. Header
    일반적으로 이 부분에는 토큰을 생성할 때 사용한 암호화 알고리즘과 토큰의 타입이 명시되어 있습니다. 이 내용들은 개별 네트워크, 시스템, 소프트웨어들이 동일하게 이해할 수 있도록 base64 방식을 통해 문자열 형태로 인코딩되어서 제공됩니다.

  2. Payload
    이 부분은 실질적인 정보들이 들어있습니다. 인증을 위한 정보, 발행시간, 만료기간 등이 담겨있고, 마찬가지로 base64형태로 인코딩해서 사용합니다. base64 방식은 형태를 바꿔주는 것이지 암호화를 해주는 방식이 아니기 때문에, 인증을 위한 정보가 담겨있다고는 하지만 그것은 서버측에서 인증을 확인하기 위해 필요한 최소한의 정보만이 들어가야 합니다. 예를 들어, 서버측의 user id같은 것만이 들어가야하고, password나 그외 노출시 치명적인 것들은 들어가선 안 됩니다.

  3. Signature
    앞서 살펴본 토큰의 두 파트는 암호화하지 않기 때문에, 토큰의 위조, 변조 여부를 파악하기 위해서는 서명부분을 필수로 생성해야합니다. 이 서명은 기본적으로 비밀키 암호화 알고리즘 방식으로 이루어져있으며, 전달받은 header, payload의 내용이 서버에서 발행한 내용과 일치하는지의 여부를 판단하는데 쓰입니다.

최초 인증시 서버에서 header와 payload를 base64로 인코딩하여 생성한 다음, 인코딩된 전체 문자열을 서버 내부의 비밀키를 이용하여 암호화 알고리즘을 적용하고 base64로 인코딩합니다. 이 서명은 서버의 비밀키를 통해서만 원래의 문자열로 복호화가 가능합니다.

그렇기 때문에, 서버에서 전달한 토큰이라면 최초의 header, payload가 어떤 형태였는지 확인이 가능하고, 이를 통해 서버가 다시 전달받은 토큰의 변조여부를 탐지할 수 있게 됩니다. 간단히 설명하면, 서명을 base64로 복호화 한 다음에, 그 결과를 다시 서버의 비밀키를 통해 복호화합니다. 그러면 서버가 최초로 생성했던 header와 payload의 정보를 그대로 얻을 수 있습니다. 이 결과값과 전달받은 header, payload가 일치하면 정상적인 토큰이며, 그렇지 않으면 변조된 토큰으로 판단, 인증을 거부하도록 서비스를 구성할 수 있습니다.

dj-rest-auth가 제공하는 인증 방식

dj-rest-auth에 특정 방식의 인증을 사용하고자 한다면, django-rest-framework의 설정을 정하고, dj-rest-auth의 설정을 정해주어야 합니다.

settings의 REST_FRAMEWORK 항목에 있는 DEFAULT_AUTHENTICATION_CLASSES에 어떤 인증 방식을 허용할지 명시해주는 일이 우선입니다. django는 이 항목에 잡힌 설정값에 따라 request header에 들어온 Authorization 값을 확인하여 인증을 처리합니다.

이 항목에 들어올 수 있는 클래스는 rest_framework.authentication.py 파일에 선언되어 있는 클래스와, 이를 상속하여 동작하는 클래스들입니다. Basic, Session, Token이 기본적으로 선언되어 있으며, 공통으로 authenticate 메서드를 가지고 있습니다. django 웹 서버에 들어온 모든 request들은 view에 따로 permission을 명시하지 않은 이상 Default로 선택된 authentication 클래스의 authenticate 메서드를 거쳐서 인증을 사전에 처리합니다. 이때 Default 클래스가 복수로 선언되어 있다면, 한쪽만 통과하여도 인증된 것으로 처리합니다. 예를 들어, BasicAuthentication, SessionAuthentication이 모두 설정되어 있다면, 헤더의 Authorization 에 Basic XXXX 으로 들어있던, 쿠키에 세션 아이디가 들어있던 둘 중 하나만 알맞게 보낸 요청이라면 인증과정을 통과할 수 있습니다.

다만, 위에서 설명한 jwt 방식의 인증을 사용하기 위해서는 rest_framework의 TokenAuthentication 클래스가 아닌, rest_framework_simplejwt 라이브러리에서 찾아볼 수 있는 JWTAuthentication 클래스를 설정해주어야 합니다. 이에 대한 내용과 dj-rest-auth의 login과 관련된 serializer, view에 대해서는 다음 글에서 다루고자 합니다.

0개의 댓글