[Django Tutorial] 사용자 인증 - 세션, 인증 관리
세션 관리
로그인정보를 삭제할 때 session id 값을 브라우저의 쿠기에서 삭제만 해도 사라지고 session id값만 알고 있으면 다른 브라우저에서 세팅을 하면 동일한 세션이 복제가 됩니다.
이렇게 쿠키값이 유출이 된다면 개인정보가 누출이 되거나, 사용자의 권한을 이용해서 제삼자가 원하는 행위를 할 수 있게 됩니다. 그래서 장고는 SessionMiddleware에서는 세션 관리를 위한 다양한 옵션을 제공합니다.
세션 미들웨어
세션은 로그인한 사용자에게만 발급하는 표시입니다. 사용자에게는 이 표시의 sessionid만 알려주고 장고는 해당 sessionid를 누구에게 발급해 줬는지 세션이라는 객체에 사용자 인증정보를 저장합니다. 이 세션객체는 기본적으로 데이터베이스에 저장되도록 되어 있지만 다양한 방식으로 관리할 수 있습니다.
세션 미들웨어는 세션정보를 어디에 어떤 방식으로 저장하는지에 대한 부분만 관여합니다. 세션에 어떤 데이터가 있는지 관심이 없습니다. 세션 미들웨어는 여러 가지 세션 백엔드 중 하나를 선택해서 백엔드에게 실제 저장 기능을 위임합니다.
세션 백엔드 모듈 이름 | 기능 |
django.contrib.sessions.backends.db | 데이터베이스에 저장하는 백엔드 |
django.contrib.sessions.backends.cache | 캐시에 저장하는 백엔드 |
django.contrib.sessions.backends.cache_db | 캐시와 데이터베이스를 변행하는 백엔드 |
django.contrib.sessions.backends.file | 파일에 저장하는 백엔드 |
django.contrib.sessions.backends.signed_cookie | 쿠키에 저장하는 백엔드 |
쿠키에 세션을 저장하는 것은 사용자에게 모든 정보를 노출 하는 것이고 쿠키의 크기가 커져서 네트워크 부하에도 영향이 있기 때문에 좋지 않습니다. 세션데이터를 노출해야 하는 경우에만 사용하고 민감한 내용이 들어 있으면 안 됩니다.
가장 흔하게 사용하는 방법은 데이터베이스 백엔드와 캐시 백엔드입니다.
데이터베이스는 아주 큰 용량이 비교적 빠른 성능을 보장해서 범용적으로 사용하기 좋습니다.
캐시 백엔드는 장고의 전역캐시 백엔드에 세션데이터를 저장합니다. 캐시 백엔드가 메모리 기반이라면 캐시 서버가 리부팅될 때 모든 데이터가 초기화될 수 있으니 메모리 기반의 캐시 백엔드를 사용한다면 데이터 베이스와 병행하도록 설정해야 합니다.
세션 미들웨이는 사용자가 요청을 할 때마다 쿠키의 session is를 확인 후 세션 백엔드 세션데이터를 불러와 request.session 객체에 저장합니다. 만약 세션데이터가 유효하지 않다면 빈 세션데이터만 저장합니다.
request.session 객체를 접근할 수 있는 어디서든 객체에 추가적인 데이터를 저장하거나 수정, 삭제가 가능합니다. 사용자의 데이터를 캐시의 목적으로 저장하거나 커스텀 인증 미들웨이를 사용할 때 세션객체를 수정합니다.
안전한 세션 관리 방법
장고에서 sessionis누출과 정보 보호를 위해서 여러 가지를 제공합니다. 기본적으로 secure쿠키로 session id를 저장하는 방법과 session의 유효기간을 설정해서 유효기간이 지난 후에는 세션데이터를 무효화시키는 기능을 제공합니다.
secure쿠키는 자바스크립트로는 세션값을 불러올 수 없고 브라우저의 개발도구를 열어 확인하거나, 브라우저를 바이러스 또는 activex 등으로 메모리 값을 찾는 것 외에는 sessionid값을 알 수 없게 하는 것입니다. 즉, sessionid를 브라우저를 실제로 보고 있는 사람 또는 서버 외에는 볼 수 없게 하는 것입니다. secure쿠키 설정은 설정파일에 SESSION_COOKIE_SECURE변수에 True로 설정하면 활성화가 됩니다. 꼭 필요한 경우 외에는 변경하지 않는 것이 좋습니다.
세션의 유효기간이 설정되면 쿠기도 동일한 유효기간이 설정이 됩니다. 유효기간이 지난 쿠키는 브라우저에서 자동으로 삭제를 합니다. 만약 쿠키를 조작해서 유효기간을 변경하더라도 서버에 저장된 유효기간으로 검사를 합니다. 유효기간이 짧으면 짧을수록 보안 강도가 높아지지만 시간이 지나면 로그인이 되지 않은 상태로 인식하기 때문에 다시 로그인을 해줘야 합니다. 유효기간 설정은 설정파일에 SESSION_COOKIE_AGE값에 초 단위로 기간을 설정하면 됩니다. 기본적으로는 2주로 설정이 되어 있습니다.
다른 방법으로는 쿠키의 세션 아이디의 이름을 변경하는 것입니다. 설정파일의 SESSION_COOKIE_NAME값을 누군가 알 수 없는 이름으로 변경하면 악의적인 목적을 가진 사람에게 조금 불편함을 줄 수 있습니다. 이 방법은 보안 강도로 표현하면 아주 미세하지만 보안 목적보다는 session id라는 이름이 중복이 되거나 어쩔 수 없이 변경해야 할 경우에 사용합니다.
이 외에는 강력한 방법으로 매 요청마다 request.session 객체의 cycle_key메서드를 호출하는 겁니다. cycle_key()가 호출이 될 때마다 sessionid가 변경이 되고 변경된 값이 쿠키에 저장이 됩니다. sessionid가 노출이 되었더라도 sessionid를 악의적인 목적으로 사용하기 전에 또 다른 요청을 한다면 이전의 sessionis값은 유효하지 않은 것이 됩니다. 하지만 매 요청마다 세션 백엔드가 데이터를 다시 저장하기 때문에 서버의 가용성이 조금 떨어집니다. 서버의 성능보다 안정성을 중요시한다면 이 방법을 사용합니다.
인증 관리
인증이라는 것은 정당한 절차를 통해 접속했는지 확인하고 접속자가 누구인지를 증명하는 절차를 의미합니다. 세션이 증명서라면 인증은 증명서를 생성하는 절차를 담방하고 누군가 증명서를 들고 왔을 때 누구의 증명서인지를 확인하는 것입니다.
장고의 인증은 기본적으로 데이터베이스를 기반으로 하고 있으나 추가적인 라이브러리를 설치하거나 별도의 모듈을 구현한다면 소셜로그인 같은 외부의 api를 연동해서 인증하고 사용자의 정보를 공개된 범위 내에서 제공받을 수도 있습니다.
로그인을 하면 세션 미들웨어가 생성한 request.session 객체에 세션정보를 저장합니다. 이때 저장한 세션 정보의 내용은 세션 백엔드에 의해 자동으로 저장됩니다. 로그인의 최종목적은 사용자의 세션객체를 request.session객체에 저장하는 것입니다.
인증 미들웨어
세션 미들웨어가 sessionid를 가지고 requset.session 객체를 저장하면 인증 미들웨어는 이 세션객체의 내용을 확인하고 사용자 정보를 불러와 request.user객체에 저장합니다. 실질적으로 미들웨어가 직접 사용자의 신원을 확인하지 않고 인증 백엔드에게 위임합니다. 인증 백엔드는 사용자의 정보를 가져올 때 세션객체의 내용과 사용자 정보의 내용을 비교해 세션정보가 사용자 정보와 일치하지 않을 경우 request.user객체에 AnnonymousUser객체를 저장합니다.
장고에서는 기본적으로 세션객체에 세 가지 정보를 딕셔너리 형태로 저장합니다.
key | 데이터 |
_auth_user_id | 사용자 정보 id |
_auth_user_backend | 로그인할 때 사용한 인증 백엔드 |
_auth_user_hash | 사용자 정보 테이블에 저장된 패스워드 값의 해시값 |
인증 미들웨어는 인증 백엔드를 통해 사용자를 식별하도록 인증기능을 위임합니다. 소셜로그인 같은 외부의 인증 api를 이용하는 경우 각 api별로 백엔드가 달라질 수 있습니다.
장고는 기본적으로 데이터 베이스의 사용자 정보 모델을 기반으로 인증을 처리하는 백엔드를 제공합니다. 모델 인증 백엔드는 _auth_user_id를 가지고 사용자가 누구인지 식별하고 사용자 모델의 객체를 request.user객체에 저장합니다.
따라서 사용자에 의해 비밀번호가 변경되었을 경우 변경된 비밀번호의 해시값과 _auth_user_hash값을 비교하면 같지 않을 테니 이전 세션은 유효하지 않게 됩니다. 즉, 비밀번호를 변경하면 이전의 세션은 유효하지 않고 로그아웃 상태로 전환됩니다.
안전한 인증 방법
로그인은 위에서 설명한 것처럼 장고의 auth프레임 워크를 이용하면 된다. 특정한 케이스에 로그인 폼을 통하지 않고도 인증을 하거나 재인증하기를 원할 때 auth프레임 워크의 login함수를 사용하면 된다. 장고에서 안전하고 정확하게 인증을 한다 하더라도 장고 앱으로 데이터가 전송되기 전인 네트워크에서 이동하는 과정 중에 노출된 사용자 정보는 장고가 보호해 줄 수 없다.
예를 들어 로그인할 때 이메일과 비밀번호를 http에서는 입력한 문자를 그대로 네트워크를 통해 전송이 됩니다. 그래서 안전한 인증 기능을 제공하기 이전에 반드시 https를 제공해줘야 합니다.
인증된 사용자에게만 접속을 허용하기 뷰클래스에 LoginRequireMixin이 추가되었거나 핸들러 함수가 logn_required 데코레이터로 wrapping을 합니다. 별도의 프로세스로 사용자의 인증여부를 확인해야 하는 경우 requests.user.is_authenticated의 값을 이용하면 오류의 가능성이 줄어든다.