장고 5 부터 LogoutView로의 요청은 POST 방식만 허용

장고 5부터 GET 방식의 로그아웃 요청은 LogoutView에서 거부합니다. 보통 로그아웃 처리를 a 링크를 통해 심플하게 처리했었는 데 이는 GET 방식으로 요청이 전달됩니다. 장고 5부터는 POST 방식만 허용되며, GET 방식으로 요청하면 405 응답을 합니다.

장고 5.0부터 LogoutView에서는 POST 방식만 허용

위 스크린샷의 코드는 장고 4.1에서의 LogoutView 코드입니다. GET 요청일 때, "Log out via GET requests in deprecated and will be removed in Django 5.0. Use POST requests for logging out." 경고를 띄워주고 있습니다.

장고 기본앱인 django.contrib.auth 에서는 LogoutView 뷰를 통한 로그아웃을 지원해줍니다.

종전에는 아래와 같이 a 태그를 통해 로그아웃을 처리했었다면,

<a href="{% url 'logout' %}">로그아웃</a>

장고 5부터는 LogoutView에서 GET 요청을 지원하지 않고, POST 요청 만을 허용하게 됩니다. GET 요청을 보내게 된다면 상태코드 405의 Method Not Allowed (GET) 응답을 받게 됩니다.

이전의 모든 장고 버전에서도 LogoutView 에서 아래의 POST 방식을 지원하니, 지금 미리 변경해두시면 어떨까요?

<form action="{% url 'logout' %}" method="post">
    {% csrf_token %}
    <input type="submit" value="로그아웃" />
</form>

논의된 티켓 및 커밋

추가 : 2023년 2월 26일

댓글에서 JUNG JAEGYUN 님의 질문에 관련 티켓과 커밋을 찾아봤습니다.

#15619. Logout link should be protected 티켓의 설명은 아래와 같습니다.

There is a logout link in admin app. It is link, not a form. Therefore it is not CSRF-protected. Probably it is not so important to protect logout from CSRF attack, because this fact cannot be used to do anything harmful. So this is just a request for purity. Another reason is that GET request should never change invernal state of the system.

첫 번째 이유는 로그아웃 링크가 링크 형태로 되어 있어서 CSRF(Cross-Site Request Forgery) 공격으로부터 보호되지 않는다는 것입니다. 일반적으로 CSRF 공격은 악의적인 웹사이트에서 사용자가 로그인한 상태에서 다른 웹사이트로 요청을 보내는 것으로 이루어집니다. 이 경우, 사용자는 악성 웹사이트에서 로그인한 상태로 요청을 보내게 되어 인증 정보가 탈취될 수 있습니다. 하지만, 로그아웃은 사용자를 로그아웃 상태로 만들기 때문에 CSRF 공격에 사용될 수 없으므로 보호하지 않아도 괜찮은 경우가 많습니다.

두 번째 이유는 GET 요청은 시스템의 상태를 변경하지 않아야 한다는 것입니다. GET 요청은 조회 목적으로서 서버의 상태를 변경하거나 부작용(Side Effect)을 일으키면 안됩니다. 따라서 로그아웃과 같은 상태 변경 작업은 보통 POST 또는 DELETE 요청으로 처리하는 것이 낫습니다.

관련 커밋 : Refs #15619 -- Removed support for logging out via GET requests.

  • 마지막 편집일시 : 2023년 4월 6일 4:08 오후
  • 최초 생성일시 : 2023년 2월 25일 10:42 오전
🌟 본 포스팅이 도움이 되셨다면 댓글 하나 남겨주시고, 널리 공유도 부탁드립니다. 🌟

댓글

JUNG JAEGYUN's avatar
JUNG JAEGYUN 2023년 2월 26일 12:47 오후 (KST)

장고에서 이렇게 변경하기로 한 이유가 궁굼하네요!

이진석's avatar
이진석 2023년 2월 26일 1:28 오후 (KST)

위 본문에 관련 티켓 내용을 추가해두었습니다. ;-)

ILCO's avatar
ILCO 2023년 2월 26일 5:03 오후 (KST)

신기하게 방금까지 로그아웃 뷰를 연습하던 중이었습니다^^;

{% if user.is_authenticated %}
                    <a href="{% url 'common:logout' %}" class="nav-link">{{ user.username }}(로그아웃)</a>
                {% else %}
                    <a href="{% url "common:login" %}" class="nav-link">로그인</a>
                {% endif %}

이런 사소해 보이는 부분도 CSRF 취약점이 될 수 있군요!ㅎ

이진석's avatar
이진석 2023년 2월 26일 6:04 오후 (KST)

딱~! 명진님을 위한 포스팅이었군요~! ㅎㅎ :D

논외로 로그인/로그아웃 링크에에 next 인자로 로그인/로그아웃 후에 이동할 주소를 지정할 수 있는 데요. 현 페이지 주소(request.path)를 지정하시면, 로그인/로그아웃 후에 현재 페이지로 이동합니다.