{"title":"장고의 CSRF 보호를 사용하는 방법","version":"5.0","locale":"ko","docname":"howto/csrf","url":"/ko/5.0/howto/csrf/","canonical":"https://djangodocs.dev/ko/5.0/howto/csrf/","summary":"당신의 뷰에서 CSRF 보호를 활용하려면 다음 단계를 따르십시오: CSRF 미들웨어는 MIDDLEWARE 설정에서 기본적으로 활성화되어 있습니다. 해당 설정을 덮어쓰는 경우에는, CSRF 공격이 처리되었다고 가정하는 모든 뷰 미들웨어 앞에 ``…","html":"<span id=\"using-csrf\"></span><h1>장고의 CSRF 보호를 사용하는 방법<a class=\"heading-anchor\" href=\"#how-to-use-django-s-csrf-protection\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>당신의 뷰에서 CSRF 보호를 활용하려면 다음 단계를 따르십시오:</p>\n<ol class=\"arabic\">\n<li><p>CSRF 미들웨어는 <a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-MIDDLEWARE\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">MIDDLEWARE</span></code></a> 설정에서 기본적으로 활성화되어 있습니다. 해당 설정을 덮어쓰는 경우에는, CSRF 공격이 처리되었다고 가정하는 모든 뷰 미들웨어 앞에 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>’django.middleware.csrf.CsrfViewMiddleware’<a href=\"#id3\"><span class=\"problematic\" id=\"id4\">``</span></a>가 와야 함을 기억하십시오.</p>\n<p>사용하지 않도록 설정한 경우(권장하지 않음), 보호하려는 특정 뷰에서 :func:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>~django.views.decorators.csrf.csrf_protect`를 사용할 수 있습니다(아래 참조).</p>\n</li>\n<li><p>POST 양식을 사용하는 모든 템플릿에서, 양식이 내부 URL용인 경우 <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;form&gt;</span></code> 요소 내부에 <a class=\"reference internal\" href=\"/ko/5.0/ref/templates/builtins/#std-templatetag-csrf_token\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">csrf_token</span></code></a> 태그를 사용하세요. 예:</p>\n<div class=\"code-block\" data-language=\"html+django\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">form</span> <span class=\"na\">method</span><span class=\"o\">=</span><span class=\"s\">&quot;post&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{%</span> <span class=\"k\">csrf_token</span> <span class=\"cp\">%}</span>\n</code></pre></div>\n<p>외부 URL을 대상으로 하는 POST 양식에 대해서는 이렇게 할 수 없습니다. CSRF 토큰이 누출되어 취약점이 발생할 수 있기 때문입니다.</p>\n</li>\n<li><p>해당 뷰 함수에서 <code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">render</span></code> 함수, 제네릭 뷰, 또는 contrib apps를 사용하는 경우, 이들은 모두 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>RequestContext``를 사용하므로 확인할 필요가 업습니다.</p></li>\n</ol>\n<section id=\"using-csrf-protection-with-ajax\">\n<span id=\"csrf-ajax\"></span><h2>AJAX와 함께 CSRF 보호 사용<a class=\"heading-anchor\" href=\"#using-csrf-protection-with-ajax\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>위의 방법을 AJAX POST 요청에 사용할 수 있지만 몇 가지 불편한 점이 있습니다: 모든 POST 요청에 POST 데이터로 CSRF 토큰을 전달해야 합니다. 이러한 불편함 때문에, 다른 대체 방법이 있습니다: 각 XMLHttpRequest에서 사용자 지정 <code class=\"docutils literal notranslate\"><span class=\"pre\">X-CSRFToken</span></code> 헤더(<a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-CSRF_HEADER_NAME\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">CSRF_HEADER_NAME</span></code></a> 설정에 지정된 대로)를 CSRF 토큰 값으로 설정합니다. 많은 JavaScript 프레임워크가 모든 요청에 대해 헤더를 설정할 수 있도록 하는 후크를 제공하기 때문에 이 방식이 종종 더 쉽습니다.</p>\n<p>먼저, CSRF 토큰을 받아야 합니다. 그 방법은 <a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-CSRF_USE_SESSIONS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">CSRF_USE_SESSIONS</span></code></a> 및 <a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-CSRF_COOKIE_HTTPONLY\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">CSRF_COOKIE_HTTPONLY</span></code></a> 설정이 활성화되어 있는지 여부에 따라 다릅니다.</p>\n<section id=\"acquiring-the-token-if-csrf-use-sessions-and-csrf-cookie-httponly-are-false\">\n<span id=\"acquiring-csrf-token-from-cookie\"></span><h3><a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-CSRF_USE_SESSIONS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">CSRF_USE_SESSIONS</span></code></a> 및 :setting:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>CSRF_COOKIE_HTTPONLY`가 <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">``</span></a>False``인 경우 토큰 획득<a class=\"heading-anchor\" href=\"#acquiring-the-token-if-csrf-use-sessions-and-csrf-cookie-httponly-are-false\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>토큰의 권장 원본은 <code class=\"docutils literal notranslate\"><span class=\"pre\">csrftoken</span></code> 쿠키이며, 이는 위에서 설명한 대로 뷰에 대해 CSRF 보호를 활성화한 경우 자동으로 설정됩니다.</p>\n<p>CSRF 토큰 쿠키의 이름은 기본적으로 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>csrftoken``이지만 <a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-CSRF_COOKIE_NAME\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">CSRF_COOKIE_NAME</span></code></a> 설정을 통해 쿠키 이름을 설정할 수 있습니다.</p>\n<p>다음과 같이 토큰을 획득할 수 있습니다:</p>\n<div class=\"code-block\" data-language=\"javascript\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">JavaScript</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"JavaScript code\"><code><span class=\"kd\">function</span><span class=\"w\"> </span><span class=\"nx\">getCookie</span><span class=\"p\">(</span><span class=\"nx\">name</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\">    </span><span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"nx\">cookieValue</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"kc\">null</span><span class=\"p\">;</span>\n<span class=\"w\">    </span><span class=\"k\">if</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">cookie</span><span class=\"w\"> </span><span class=\"o\">&amp;&amp;</span><span class=\"w\"> </span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">cookie</span><span class=\"w\"> </span><span class=\"o\">!==</span><span class=\"w\"> </span><span class=\"s1\">&#39;&#39;</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\">        </span><span class=\"kd\">const</span><span class=\"w\"> </span><span class=\"nx\">cookies</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">cookie</span><span class=\"p\">.</span><span class=\"nx\">split</span><span class=\"p\">(</span><span class=\"s1\">&#39;;&#39;</span><span class=\"p\">);</span>\n<span class=\"w\">        </span><span class=\"k\">for</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"kd\">let</span><span class=\"w\"> </span><span class=\"nx\">i</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"mf\">0</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"nx\">i</span><span class=\"w\"> </span><span class=\"o\">&lt;</span><span class=\"w\"> </span><span class=\"nx\">cookies</span><span class=\"p\">.</span><span class=\"nx\">length</span><span class=\"p\">;</span><span class=\"w\"> </span><span class=\"nx\">i</span><span class=\"o\">++</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\">            </span><span class=\"kd\">const</span><span class=\"w\"> </span><span class=\"nx\">cookie</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nx\">cookies</span><span class=\"p\">[</span><span class=\"nx\">i</span><span class=\"p\">].</span><span class=\"nx\">trim</span><span class=\"p\">();</span>\n<span class=\"w\">            </span><span class=\"c1\">// Does this cookie string begin with the name we want?</span>\n<span class=\"w\">            </span><span class=\"k\">if</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"nx\">cookie</span><span class=\"p\">.</span><span class=\"nx\">substring</span><span class=\"p\">(</span><span class=\"mf\">0</span><span class=\"p\">,</span><span class=\"w\"> </span><span class=\"nx\">name</span><span class=\"p\">.</span><span class=\"nx\">length</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"o\">===</span><span class=\"w\"> </span><span class=\"p\">(</span><span class=\"nx\">name</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"s1\">&#39;=&#39;</span><span class=\"p\">))</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\">                </span><span class=\"nx\">cookieValue</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nb\">decodeURIComponent</span><span class=\"p\">(</span><span class=\"nx\">cookie</span><span class=\"p\">.</span><span class=\"nx\">substring</span><span class=\"p\">(</span><span class=\"nx\">name</span><span class=\"p\">.</span><span class=\"nx\">length</span><span class=\"w\"> </span><span class=\"o\">+</span><span class=\"w\"> </span><span class=\"mf\">1</span><span class=\"p\">));</span>\n<span class=\"w\">                </span><span class=\"k\">break</span><span class=\"p\">;</span>\n<span class=\"w\">            </span><span class=\"p\">}</span>\n<span class=\"w\">        </span><span class=\"p\">}</span>\n<span class=\"w\">    </span><span class=\"p\">}</span>\n<span class=\"w\">    </span><span class=\"k\">return</span><span class=\"w\"> </span><span class=\"nx\">cookieValue</span><span class=\"p\">;</span>\n<span class=\"p\">}</span>\n<span class=\"kd\">const</span><span class=\"w\"> </span><span class=\"nx\">csrftoken</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nx\">getCookie</span><span class=\"p\">(</span><span class=\"s1\">&#39;csrftoken&#39;</span><span class=\"p\">);</span>\n</code></pre></div>\n<p>위의 코드는 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>getCookie``를 대체하기 위한 <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">`</span></a>JavaScript 쿠키 라이브러리 &lt;<a class=\"reference external\" href=\"https://github.com/js-cookie/js-cookie/\">https://github.com/js-cookie/js-cookie/</a>&gt;`_를 사용하여 단순화할 수 있습니다.</p>\n<div class=\"code-block\" data-language=\"javascript\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">JavaScript</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"JavaScript code\"><code><span class=\"kd\">const</span><span class=\"w\"> </span><span class=\"nx\">csrftoken</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nx\">Cookies</span><span class=\"p\">.</span><span class=\"nx\">get</span><span class=\"p\">(</span><span class=\"s1\">&#39;csrftoken&#39;</span><span class=\"p\">);</span>\n</code></pre></div>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">참고</p>\n<p>CSRF 토큰은 템플릿에서 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">:ttag:`csrf_token`을 사용하여 명시적으로 include된 경우에만 DOM에도 마스크된 형태로 존재합니다. 쿠키에는 마스크되지 않은 표준 토큰이 포함되어 있습니다. :class:`~django.middleware.csrf.CsrfViewMiddleware`는 둘 중 하나를 수락합니다. 그러나 BREACH`_</span></a> 공격으로부터 보호하기 위해서는 마스킹된 토큰을 사용하는 것이 좋습니다.</p>\n</aside>\n<aside class=\"admonition admonition-warning\" role=\"note\">\n<p class=\"admonition-title\">경고</p>\n<p>만약 뷰가 <a class=\"reference internal\" href=\"/ko/5.0/ref/templates/builtins/#std-templatetag-csrf_token\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">csrf_token</span></code></a> 템플릿 태그가 포함된 템플릿을 렌더링하지 않는 경우, 장고는 CSRF 토큰 쿠키를 설정하지 않을 수 있습니다. 이는 양식이 페이지에 동적으로 추가되는 경우에는 일반적인 설정입니다. 이 경우, 장고는 쿠키 설정을 강제하는 뷰 데코레이터를 제공합니다: <a class=\"reference internal\" href=\"/ko/5.0/ref/csrf/#django.views.decorators.csrf.ensure_csrf_cookie\" title=\"django.views.decorators.csrf.ensure_csrf_cookie\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">ensure_csrf_cookie()</span></code></a>.</p>\n</aside>\n</section>\n<section id=\"acquiring-the-token-if-csrf-use-sessions-or-csrf-cookie-httponly-is-true\">\n<span id=\"acquiring-csrf-token-from-html\"></span><h3><a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-CSRF_USE_SESSIONS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">CSRF_USE_SESSIONS</span></code></a> 또는 :setting:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>CSRF_COOKIE_HTTPONLY`가 <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">``</span></a>True``인 경우 토큰 획득<a class=\"heading-anchor\" href=\"#acquiring-the-token-if-csrf-use-sessions-or-csrf-cookie-httponly-is-true\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><a class=\"reference internal\" href=\"/ko/5.0/ref/settings/#std-setting-CSRF_USE_SESSIONS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">CSRF_USE_SESSIONS</span></code></a> 또는 :setting:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>CSRF_COOKIE_HTTPONLY`를 활성화하는 경우, HTML에 CSRF 토큰을 포함하고 JavaScript를 사용하여 DOM에서 토큰을 읽어야 합니다.</p>\n<div class=\"code-block\" data-language=\"html+django\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"cp\">{%</span> <span class=\"k\">csrf_token</span> <span class=\"cp\">%}</span>\n<span class=\"p\">&lt;</span><span class=\"nt\">script</span><span class=\"p\">&gt;</span>\n<span class=\"kd\">const</span><span class=\"w\"> </span><span class=\"nx\">csrftoken</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"nb\">document</span><span class=\"p\">.</span><span class=\"nx\">querySelector</span><span class=\"p\">(</span><span class=\"s1\">&#39;[name=csrfmiddlewaretoken]&#39;</span><span class=\"p\">).</span><span class=\"nx\">value</span><span class=\"p\">;</span>\n<span class=\"p\">&lt;/</span><span class=\"nt\">script</span><span class=\"p\">&gt;</span>\n</code></pre></div>\n</section>\n<section id=\"setting-the-token-on-the-ajax-request\">\n<h3>AJAX 요청에 대한 토큰 설정<a class=\"heading-anchor\" href=\"#setting-the-token-on-the-ajax-request\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>마지막으로, AJAX 요청에 헤더를 설정해야 합니다. <a class=\"reference external\" href=\"https://developer.mozilla.org/en-US/docs/Web/API/fetch\">fetch()</a> API 사용:</p>\n<div class=\"code-block\" data-language=\"javascript\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">JavaScript</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"JavaScript code\"><code><span class=\"kd\">const</span><span class=\"w\"> </span><span class=\"nx\">request</span><span class=\"w\"> </span><span class=\"o\">=</span><span class=\"w\"> </span><span class=\"ow\">new</span><span class=\"w\"> </span><span class=\"nx\">Request</span><span class=\"p\">(</span>\n<span class=\"w\">    </span><span class=\"cm\">/* URL */</span><span class=\"p\">,</span>\n<span class=\"w\">    </span><span class=\"p\">{</span>\n<span class=\"w\">        </span><span class=\"nx\">method</span><span class=\"o\">:</span><span class=\"w\"> </span><span class=\"s1\">&#39;POST&#39;</span><span class=\"p\">,</span>\n<span class=\"w\">        </span><span class=\"nx\">headers</span><span class=\"o\">:</span><span class=\"w\"> </span><span class=\"p\">{</span><span class=\"s1\">&#39;X-CSRFToken&#39;</span><span class=\"o\">:</span><span class=\"w\"> </span><span class=\"nx\">csrftoken</span><span class=\"p\">},</span>\n<span class=\"w\">        </span><span class=\"nx\">mode</span><span class=\"o\">:</span><span class=\"w\"> </span><span class=\"s1\">&#39;same-origin&#39;</span><span class=\"w\"> </span><span class=\"c1\">// Do not send CSRF token to another domain.</span>\n<span class=\"w\">    </span><span class=\"p\">}</span>\n<span class=\"p\">);</span>\n<span class=\"nx\">fetch</span><span class=\"p\">(</span><span class=\"nx\">request</span><span class=\"p\">).</span><span class=\"nx\">then</span><span class=\"p\">(</span><span class=\"kd\">function</span><span class=\"p\">(</span><span class=\"nx\">response</span><span class=\"p\">)</span><span class=\"w\"> </span><span class=\"p\">{</span>\n<span class=\"w\">    </span><span class=\"c1\">// ...</span>\n<span class=\"p\">});</span>\n</code></pre></div>\n</section>\n</section>\n<section id=\"using-csrf-protection-in-jinja2-templates\">\n<h2>Jinja2 템플릿에서 CSRF 보호 사용<a class=\"heading-anchor\" href=\"#using-csrf-protection-in-jinja2-templates\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>장고의 <a class=\"reference internal\" href=\"/ko/5.0/topics/templates/#django.template.backends.jinja2.Jinja2\" title=\"django.template.backends.jinja2.Jinja2\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Jinja2</span></code></a> 템플릿 백엔드는 장고의 템플릿 언어 내의 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>{% csrf_token %}``에 해당하는 모든 템플릿의 컨텍스트에 <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">``</span></a>{{ csrf_input }}``를 추가합니다. 예를 들어:</p>\n<div class=\"code-block\" data-language=\"html+jinja\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Html Jinja</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Html Jinja code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">form</span> <span class=\"na\">method</span><span class=\"o\">=</span><span class=\"s\">&quot;post&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">csrf_input</span> <span class=\"cp\">}}</span>\n</code></pre></div>\n</section>\n<section id=\"using-the-decorator-method\">\n<h2>데코레이터 메서드 사용<a class=\"heading-anchor\" href=\"#using-the-decorator-method\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p><a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>CsrfViewMiddleware``를 포괄적인 보호 기능으로 추가하는 대신 보호가 필요한 특정 뷰에 정확히 동일한 기능을 가진 <a class=\"reference internal\" href=\"/ko/5.0/ref/csrf/#django.views.decorators.csrf.csrf_protect\" title=\"django.views.decorators.csrf.csrf_protect\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">csrf_protect()</span></code></a> 데코레이터를 사용할 수 있습니다. 이는 출력에 CSRF 토큰을 삽입하는 뷰와 POST 양식 데이터를 수락하는 뷰에서 <strong>둘 다</strong> 사용해야 합니다. (이 둘은 종종 동일한 뷰 함수이지만, 항상 그런 것은 아닙니다).</p>\n<p>데코레이터를 그 자체로 사용하는 것은 <strong>권장하지 않습니다</strong>. 사용하는 것을 잊어버리면 보안 구멍이 생기기 때문입니다. 둘 다 사용하는 ‘벨트 및 버팀대’ 전략은 괜찮으며, 이 경우 최소한의 오버헤드가 발생합니다.</p>\n</section>\n<section id=\"handling-rejected-requests\">\n<span id=\"csrf-rejected-requests\"></span><h2>거부된 요청 처리<a class=\"heading-anchor\" href=\"#handling-rejected-requests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>기본적으로, 들어오는 요청이 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>CsrfViewMiddleware``에서 수행한 검사에 실패하면 ‘403 Forbidden’ 응답이 사용자에게 전송됩니다. 이것은 실제 Cross Site Request Forgery가 있거나, 프로그래밍 오류로 인해 CSRF 토큰이 POST 양식에 포함되지 않은 경우에만 전송되어야 합니다.</p>\n<p>그러나 오류 페이지는 그다지 친숙하지 않으므로 이 조건을 처리하기 위한 고유한 뷰를 제공할 수 있습니다. 이렇게 하려면, :setting:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>CSRF_FAILURE_VIEW`를 설정합니다.</p>\n<p>CSRF 실패는 <a class=\"reference internal\" href=\"/ko/5.0/ref/logging/#django-security-logger\"><span class=\"std std-ref\">django.security.csrf</span></a> 로거에 경고로 기록됩니다.</p>\n</section>\n<section id=\"using-csrf-protection-with-caching\">\n<h2>캐싱과 함께 CSRF 보호 사용<a class=\"heading-anchor\" href=\"#using-csrf-protection-with-caching\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p><a class=\"reference internal\" href=\"/ko/5.0/ref/templates/builtins/#std-templatetag-csrf_token\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">csrf_token</span></code></a> 템플릿 태그가 템플릿에서 사용되는 경우(또는 <code class=\"docutils literal notranslate\"><span class=\"pre\">get_token</span></code> 함수가 다른 방식으로 호출되는 경우), <code class=\"docutils literal notranslate\"><span class=\"pre\">CsrfViewMiddleware``는</span> <span class=\"pre\">쿠키와</span> <span class=\"pre\">``Vary:</span> <span class=\"pre\">Cookie</span></code> 헤더를 응답에 추가합니다. 이것은 미들웨어가 지시된 대로 잘 사용된다면 캐시 미들웨어와 함께 잘 작동함을 의미합니다(<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>UpdateCacheMiddleware``는 다른 모든 미들웨어보다 먼저 실행됨).</p>\n<p>그러나 개별 뷰에서 캐시 데코레이터를 사용하는 경우, CSRF 미들웨어는 아직 Vary 헤더 또는 CSRF 쿠키를 설정할 수 없으며 응답은 둘 중 하나만 캐시됩니다. 이 경우 CSRF 토큰을 삽입해야 하는 모든 뷰에서 <a class=\"reference internal\" href=\"/ko/5.0/ref/csrf/#django.views.decorators.csrf.csrf_protect\" title=\"django.views.decorators.csrf.csrf_protect\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">django.views.decorators.csrf.csrf_protect()</span></code></a> 데코레이터를 먼저 사용해야 합니다.</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.views.decorators.cache</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">cache_page</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.views.decorators.csrf</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">csrf_protect</span>\n\n\n<span class=\"nd\">@cache_page</span><span class=\"p\">(</span><span class=\"mi\">60</span> <span class=\"o\">*</span> <span class=\"mi\">15</span><span class=\"p\">)</span>\n<span class=\"nd\">@csrf_protect</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_view</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span> <span class=\"o\">...</span>\n</code></pre></div>\n<p>클래스 기반 보기를 사용하는 경우, :ref:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>클래스 기반 뷰 꾸미기&lt;decorating-class-based-views&gt;`를 참조할 수 있습니다.</p>\n</section>\n<section id=\"testing-and-csrf-protection\">\n<h2>테스트 및 CSRF 보호<a class=\"heading-anchor\" href=\"#testing-and-csrf-protection\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">CsrfViewMiddleware``는</span> <span class=\"pre\">일반적으로</span> <span class=\"pre\">모든</span> <span class=\"pre\">POST</span> <span class=\"pre\">요청과</span> <span class=\"pre\">함께</span> <span class=\"pre\">보내야</span> <span class=\"pre\">하는</span> <span class=\"pre\">CSRF</span> <span class=\"pre\">토큰이</span> <span class=\"pre\">필요하기</span> <span class=\"pre\">때문에,</span> <span class=\"pre\">뷰</span> <span class=\"pre\">기능을</span> <span class=\"pre\">테스트하는</span> <span class=\"pre\">데</span> <span class=\"pre\">큰</span> <span class=\"pre\">방해가</span> <span class=\"pre\">됩니다.</span> <span class=\"pre\">이러한</span> <span class=\"pre\">이유로</span> <span class=\"pre\">장고의</span> <span class=\"pre\">테스트용</span> <span class=\"pre\">HTTP</span> <span class=\"pre\">클라이언트는</span> <span class=\"pre\">미들웨어와</span> <span class=\"pre\">``csrf_protect</span></code> 데코레이터를 완화하는 요청에 플래그를 설정하여 더 이상 요청을 거부하지 않도록 수정되어 있습니다. 다른 모든 측면(예: 쿠키 보내기 등)에서는 동일하게 동작합니다.</p>\n<p>혹시, 테스트 클라이언트가 CSRF 확인을 실행하길 원한다면, CSRF 확인을 실행하는 객체를 생성할 수 있습니다.</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Client</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">csrf_client</span> <span class=\"o\">=</span> <span class=\"n\">Client</span><span class=\"p\">(</span><span class=\"n\">enforce_csrf_checks</span><span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n<section id=\"edge-cases\">\n<h2>경계 조건들<a class=\"heading-anchor\" href=\"#edge-cases\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>특정 뷰에는 여기에서 예상하고 있는 일반적인 패턴에 맞지 않는 비정상적인 요구 사항이 있을 수 있습니다. 이러한 상황에는 여러 유틸리티를 사용할 수 있습니다. 필요할 수 있는 시나리오는 다음 섹션에 설명되어 있습니다.</p>\n<section id=\"disabling-csrf-protection-for-just-a-few-views\">\n<h3>일부 뷰에 대한 CSRF 보호 비활성화<a class=\"heading-anchor\" href=\"#disabling-csrf-protection-for-just-a-few-views\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>대부분의 뷰에는 CSRF 보호가 필요하지만 일부는 그렇지 않습니다.</p>\n<p>해결책: 미들웨어를 비활성화하고 필요한 모든 보기에 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>csrf_protect``를 적용하는 대신, 미들웨어를 활성화하고 :func:<a href=\"#id3\"><span class=\"problematic\" id=\"id4\">`</span></a>~django.views.decorators.csrf.csrf_exempt`를 사용하세요.</p>\n</section>\n<section id=\"setting-the-token-when-csrfviewmiddleware-process-view-is-not-used\">\n<h3><a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>CsrfViewMiddleware.process_view()``를 사용하지 않을 때 토큰 설정<a class=\"heading-anchor\" href=\"#setting-the-token-when-csrfviewmiddleware-process-view-is-not-used\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>404 및 500 핸들러와 같이 뷰가 실행되기 전에 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>CsrfViewMiddleware.process_view``가 실행되지 않는 경우들이 있습니다. 하지만 여전히 양식에는 CSRF 토큰이 필요합니다.</p>\n<p>해결책: :func:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>~django.views.decorators.csrf.requires_csrf_token`을 사용하세요.</p>\n</section>\n<section id=\"including-the-csrf-token-in-an-unprotected-view\">\n<h3>보호되지 않은 뷰에 CSRF 토큰 include하기<a class=\"heading-anchor\" href=\"#including-the-csrf-token-in-an-unprotected-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>보호되지 않았거나 <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>csrf_exempt``에 의해 면제되었지만, 여전히 CSRF 토큰을 포함해야 하는 일부 뷰가 있을 수 있습니다.</p>\n<p>해결책: <a class=\"reference internal\" href=\"/ko/5.0/ref/csrf/#django.views.decorators.csrf.csrf_exempt\" title=\"django.views.decorators.csrf.csrf_exempt\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">csrf_exempt()</span></code></a> 다음에 :func:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>~django.views.decorators.csrf.requires_csrf_token`을 사용하세요. (즉, <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">``</span></a>requires_csrf_token``은 가장 안쪽 데코레이터여야 함).</p>\n</section>\n<section id=\"protecting-a-view-for-only-one-path\">\n<h3>하나의 경로에 대해서만 뷰 보호<a class=\"heading-anchor\" href=\"#protecting-a-view-for-only-one-path\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>한 가지 조건 세트에서만 CSRF 보호를 필요로 하며, 나머지 시간 동안에는 보호하지 않아야 하는 뷰.</p>\n<p>솔루션: 전체 뷰 기능에는 :func:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>~django.views.decorators.csrf.csrf_exempt`를 사용하고 보호가 필요한 경로에는 :func:<a href=\"#id3\"><span class=\"problematic\" id=\"id4\">`</span></a>~django.views.decorators.csrf.csrf_protect`를 사용합니다. 예시:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.views.decorators.csrf</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">csrf_exempt</span><span class=\"p\">,</span> <span class=\"n\">csrf_protect</span>\n\n\n<span class=\"nd\">@csrf_exempt</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_view</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n    <span class=\"nd\">@csrf_protect</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">protected_path</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">):</span>\n        <span class=\"n\">do_something</span><span class=\"p\">()</span>\n\n    <span class=\"k\">if</span> <span class=\"n\">some_condition</span><span class=\"p\">():</span>\n        <span class=\"k\">return</span> <span class=\"n\">protected_path</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">)</span>\n    <span class=\"k\">else</span><span class=\"p\">:</span>\n        <span class=\"n\">do_something_else</span><span class=\"p\">()</span>\n</code></pre></div>\n</section>\n<section id=\"protecting-a-page-that-uses-ajax-without-an-html-form\">\n<h3>HTML 양식 없이 AJAX를 사용하는 페이지 보호<a class=\"heading-anchor\" href=\"#protecting-a-page-that-uses-ajax-without-an-html-form\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>AJAX를 통해 POST 요청을 하며, 필수 CSRF 쿠키가 전송되도록 하는 :ttag:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>csrf_token`이 포함된 HTML 양식이 없는 페이지.</p>\n<p>솔루션: 페이지를 보내는 뷰에서 :func:<a href=\"#id1\"><span class=\"problematic\" id=\"id2\">`</span></a>~django.views.decorators.csrf.ensure_csrf_cookie`를 사용하세요.</p>\n</section>\n</section>\n<section id=\"csrf-protection-in-reusable-applications\">\n<h2>재사용 가능한 애플리케이션에서 CSRF 보호<a class=\"heading-anchor\" href=\"#csrf-protection-in-reusable-applications\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>개발자가 <code class=\"docutils literal notranslate\"><span class=\"pre\">CsrfViewMiddleware``를</span> <span class=\"pre\">끌</span> <span class=\"pre\">수</span> <span class=\"pre\">있기</span> <span class=\"pre\">때문에</span> <span class=\"pre\">contrib</span> <span class=\"pre\">apps와</span> <span class=\"pre\">관련된</span> <span class=\"pre\">모든</span> <span class=\"pre\">뷰들은</span> <span class=\"pre\">CSRF에</span> <span class=\"pre\">대한</span> <span class=\"pre\">이러한</span> <span class=\"pre\">애플리케이션의</span> <span class=\"pre\">보안을</span> <span class=\"pre\">보장하기</span> <span class=\"pre\">위해</span> <span class=\"pre\">``csrf_protect</span></code> 데코레이터를 사용합니다. 동일한 보장을 원하는 다른 재사용 가능한 앱의 개발자도 자신의 뷰에 <code class=\"docutils literal notranslate\"><span class=\"pre\">csrf_protect</span></code> 데코레이터를 사용하는 것이 좋습니다.</p>\n</section>","rootId":"how-to-use-django-s-csrf-protection","toc":[{"title":"AJAX와 함께 CSRF 보호 사용","anchor":"using-csrf-protection-with-ajax","children":[{"title":"CSRF_USE_SESSIONS 및 :setting:`CSRF_COOKIE_HTTPONLY`가 ``False``인 경우 토큰 획득","anchor":"acquiring-the-token-if-csrf-use-sessions-and-csrf-cookie-httponly-are-false","children":[]},{"title":"CSRF_USE_SESSIONS 또는 :setting:`CSRF_COOKIE_HTTPONLY`가 ``True``인 경우 토큰 획득","anchor":"acquiring-the-token-if-csrf-use-sessions-or-csrf-cookie-httponly-is-true","children":[]},{"title":"AJAX 요청에 대한 토큰 설정","anchor":"setting-the-token-on-the-ajax-request","children":[]}]},{"title":"Jinja2 템플릿에서 CSRF 보호 사용","anchor":"using-csrf-protection-in-jinja2-templates","children":[]},{"title":"데코레이터 메서드 사용","anchor":"using-the-decorator-method","children":[]},{"title":"거부된 요청 처리","anchor":"handling-rejected-requests","children":[]},{"title":"캐싱과 함께 CSRF 보호 사용","anchor":"using-csrf-protection-with-caching","children":[]},{"title":"테스트 및 CSRF 보호","anchor":"testing-and-csrf-protection","children":[]},{"title":"경계 조건들","anchor":"edge-cases","children":[{"title":"일부 뷰에 대한 CSRF 보호 비활성화","anchor":"disabling-csrf-protection-for-just-a-few-views","children":[]},{"title":"``CsrfViewMiddleware.process_view()``를 사용하지 않을 때 토큰 설정","anchor":"setting-the-token-when-csrfviewmiddleware-process-view-is-not-used","children":[]},{"title":"보호되지 않은 뷰에 CSRF 토큰 include하기","anchor":"including-the-csrf-token-in-an-unprotected-view","children":[]},{"title":"하나의 경로에 대해서만 뷰 보호","anchor":"protecting-a-view-for-only-one-path","children":[]},{"title":"HTML 양식 없이 AJAX를 사용하는 페이지 보호","anchor":"protecting-a-page-that-uses-ajax-without-an-html-form","children":[]}]},{"title":"재사용 가능한 애플리케이션에서 CSRF 보호","anchor":"csrf-protection-in-reusable-applications","children":[]}],"breadcrumbs":[{"docname":"howto/index","title":"“How-to” 가이드","url":"/ko/5.0/howto/"}],"prev":{"docname":"howto/auth-remote-user","title":"“REMOTE_USER”를 이용해 인증하는 방법","url":"/ko/5.0/howto/auth-remote-user/"},"next":{"docname":"howto/custom-management-commands","title":"커스텀 “django-admin” 명령을 만드는 방법","url":"/ko/5.0/howto/custom-management-commands/"},"formats":{"html":"/ko/5.0/howto/csrf/","markdown":"/ko/5.0/howto/csrf.md","json":"/ko/5.0/howto/csrf.json"},"source":"https://github.com/django/django/blob/stable/5.0.x/docs/howto/csrf.txt","official":"https://docs.djangoproject.com/ko/5.0/howto/csrf/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1"],"inLocales":["en","zh-hans","fr","ja","id","it","pt-br","ko","es","el","pl"]}