{"title":"如何使用 Django 提供的 CSRF 防护功能","version":"4.2","locale":"zh-hans","docname":"howto/csrf","url":"/zh-hans/4.2/howto/csrf/","canonical":"https://djangodocs.dev/zh-hans/4.2/howto/csrf/","summary":"要在你的视图中利用 CSRF 保护，请遵循以下步骤： CSRF 中间件默认在 MIDDLEWARE 配置中被激活。如果你覆盖了这个配置，请记住 'django.middleware.csrf.CsrfViewMiddleware' 应该排在任何假设 CSRF 攻击已经被处理的视图中间件之前。…","html":"<span id=\"using-csrf\"></span><h1>如何使用 Django 提供的 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=\"/zh-hans/4.2/ref/settings/#std-setting-MIDDLEWARE\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">MIDDLEWARE</span></code></a> 配置中被激活。如果你覆盖了这个配置，请记住 <code class=\"docutils literal notranslate\"><span class=\"pre\">'django.middleware.csrf.CsrfViewMiddleware'</span></code> 应该排在任何假设 CSRF 攻击已经被处理的视图中间件之前。</p>\n<p>如果你禁用了它，这并不推荐，你可以使用 <a class=\"reference internal\" href=\"/zh-hans/4.2/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> 对你想要保护的特定视图进行保护（见下文）。</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=\"/zh-hans/4.2/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>在相应的视图函数中，确保 <a class=\"reference internal\" href=\"/zh-hans/4.2/ref/templates/api/#django.template.RequestContext\" title=\"django.template.RequestContext\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">RequestContext</span></code></a> 用于渲染响应，这样 <code class=\"docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">csrf_token</span> <span class=\"pre\">%}</span></code> 才能正常工作。如果你使用的是 <a class=\"reference internal\" href=\"/zh-hans/4.2/topics/http/shortcuts/#django.shortcuts.render\" title=\"django.shortcuts.render\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">render()</span></code></a> 函数、通用视图或 contrib 应用程序，你已经被覆盖了，因为这些都使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">RequestContext</span></code>。</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 请求中都要把 CSRF 令牌作为 POST 数据传递进来。出于这个原因，有一种替代方法：在每个 XMLHttpRequest 上，设置一个自定义的 <code class=\"docutils literal notranslate\"><span class=\"pre\">X-CSRFToken</span></code> 头（由 <a class=\"reference internal\" href=\"/zh-hans/4.2/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=\"/zh-hans/4.2/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=\"/zh-hans/4.2/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=\"/zh-hans/4.2/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=\"/zh-hans/4.2/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> 为 <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> 时获取令牌<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> cookie，如果你已经为你的视图启用了上文所述的 CSRF 保护，则会设置该 cookie。</p>\n<p>CSRF 令牌 cookie 默认命名为 <code class=\"docutils literal notranslate\"><span class=\"pre\">csrftoken</span></code>，但你可以通过 <a class=\"reference internal\" href=\"/zh-hans/4.2/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> 配置来控制 cookie 的名称。</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 class=\"reference external\" href=\"https://github.com/js-cookie/js-cookie/\">JavaScript Cookie 库</a> 代替 <code class=\"docutils literal notranslate\"><span class=\"pre\">getCookie</span></code> 来简化：</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\">Note</p>\n<p>CSRF令牌也以掩码形式存在于DOM中，但仅当在模板中明确包含 <a class=\"reference internal\" href=\"/zh-hans/4.2/ref/templates/builtins/#std-templatetag-csrf_token\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">csrf_token</span></code></a> 时才存在。Cookie 包含规范的、未掩码的令牌。 <a class=\"reference internal\" href=\"/zh-hans/4.2/ref/middleware/#django.middleware.csrf.CsrfViewMiddleware\" title=\"django.middleware.csrf.CsrfViewMiddleware\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">CsrfViewMiddleware</span></code></a> 将接受任一种令牌。但是，为了防止 <a class=\"reference external\" href=\"https://www.breachattack.com/\">BREACH</a> 攻击，建议使用掩码令牌。</p>\n</aside>\n<aside class=\"admonition admonition-warning\" role=\"note\">\n<p class=\"admonition-title\">Warning</p>\n<p>如果你的视图没有渲染包含 <a class=\"reference internal\" href=\"/zh-hans/4.2/ref/templates/builtins/#std-templatetag-csrf_token\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">csrf_token</span></code></a> 模板标签的模板，Django 可能不会设置 CSRF 令牌 cookie。这种情况常见于表单被动态添加到页面的情况。针对这种情况，Django 提供了一个视图装饰器来强制设置 cookie： <code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">sure_csrf_cookie()</span></code>。</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=\"/zh-hans/4.2/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=\"/zh-hans/4.2/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> 为 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> 时获取令牌<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=\"/zh-hans/4.2/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=\"/zh-hans/4.2/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>，你必须在你的 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>Django 的 <a class=\"reference internal\" href=\"/zh-hans/4.2/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> 模板后端在所有模板的上下文中添加了 <code class=\"docutils literal notranslate\"><span class=\"pre\">{{</span> <span class=\"pre\">csrf_input</span> <span class=\"pre\">}}</span></code>，相当于 Django 模板语言中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">csrf_token</span> <span class=\"pre\">%}</span></code>。例如：</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>不要将 <code class=\"docutils literal notranslate\"><span class=\"pre\">CsrfViewMiddleware</span></code> 作为全局保护添加，而是可以在需要保护的特定视图上使用具有完全相同功能的 <a class=\"reference internal\" href=\"/zh-hans/4.2/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> 装饰器。它必须 <strong>同时</strong> 用于在输出中插入 CSRF 令牌的视图和接受 POST 表单数据的视图。 (这些通常是相同的视图函数，但不总是)。</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>默认情况下，如果传入的请求未能通过由中间件 <code class=\"docutils literal notranslate\"><span class=\"pre\">CsrfViewMiddleware</span></code> 执行的检查，用户会收到“403 Forbidden”响应。 这通常只应该出现在真正的跨站点请求伪造时，或者由于编程错误，CSRF 令牌没有被包含在 POST 表单中。</p>\n<p>然而，错误页面不是很友好，所以你可能想提供自己的视图来处理这种情况。 要做到这一点，请在`settings.py`中的配置`CSRF_FAILURE_VIEW` 来指定视图。</p>\n<p>CSRF 失败会被记录为警告到 <a class=\"reference internal\" href=\"/zh-hans/4.2/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=\"/zh-hans/4.2/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></code> 将添加一个 cookie 和一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">Vary:</span> <span class=\"pre\">Cookie</span></code> 头到响应中。这意味着，如果按照指示使用，中间件将与缓存中间件很好地配合（<code class=\"docutils literal notranslate\"><span class=\"pre\">UpdateCacheMiddleware</span></code> 先于所有其他中间件）。</p>\n<p>但是，如果你在单个视图上使用缓存装饰器，CSRF 中间件还不能设置 Vary 头或 CSRF cookie，响应将在没有任何一个的情况下被缓存。在这种情况下，在任何需要插入 CSRF 令牌的视图上，你应该先使用 <a class=\"reference internal\" href=\"/zh-hans/4.2/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>\n    <span class=\"o\">...</span>\n</code></pre></div>\n<p>如果你使用的是基于类的视图，你可以参考 <a class=\"reference internal\" href=\"/zh-hans/4.2/topics/class-based-views/intro/#id1\"><span class=\"std std-ref\">装饰基于类的视图</span></a>。</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><a href=\"#id1\"><span class=\"problematic\" id=\"id2\">``</span></a>CsrfViewMiddleware``通常会对测试视图函数造成很大的阻碍，因为每个POST请求都需要发送CSRF令牌。因此，Django用于测试的HTTP客户端已经修改了，为请求设置了一个标志，放松了中间件和` ' csrf_protect ' <a href=\"#id3\"><span class=\"problematic\" id=\"id4\">`</span></a>装饰器，使它们不再拒绝请求。在其他方面(例如发送cookie等)，它们的行为是相同的。</p>\n<p>如果出于某种原因，你* <em>希望</em> <a href=\"#id1\"><span class=\"problematic\" id=\"id2\">*</span></a>测试客户端执行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>解决办法：与其禁用中间件并对所有需要的视图应用 <code class=\"docutils literal notranslate\"><span class=\"pre\">csrf_protect</span></code>，不如启用中间件并使用 <code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">csrf_exempt()</span></code>。</p>\n</section>\n<section id=\"setting-the-token-when-csrfviewmiddleware-process-view-is-not-used\">\n<h3>当`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>有些情况下，<code class=\"docutils literal notranslate\"><span class=\"pre\">CsrfViewMiddleware.process_view</span></code> 可能在你的视图运行之前没有运行——例如 404 和 500 处理程序——但你仍然需要表单中的 CSRF 令牌。</p>\n<p>解决方法：使用 <code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">requests_csrf_token()</span></code>。</p>\n</section>\n<section id=\"including-the-csrf-token-in-an-unprotected-view\">\n<h3>在未保护的视图中包含 CSRF 令牌。<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>可能有一些视图是不受保护的，已经被 <code class=\"docutils literal notranslate\"><span class=\"pre\">csrf_exempt</span></code> 豁免，但仍然需要包括 CSRF 令牌。</p>\n<p>解决方法：使用 <a class=\"reference internal\" href=\"/zh-hans/4.2/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> 后面跟着 <a class=\"reference internal\" href=\"/zh-hans/4.2/ref/csrf/#django.views.decorators.csrf.requires_csrf_token\" title=\"django.views.decorators.csrf.requires_csrf_token\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">requires_csrf_token()</span></code></a>。（即 <code class=\"docutils literal notranslate\"><span class=\"pre\">requires_csrf_token</span></code> 应该是最里面的装饰器)。</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>解决方法：用 <code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">csrf_exempt()</span></code> 表示整个视图函数，用 <code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">csrf_protect()</span></code> 表示其中需要保护的路径。例如：</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 请求，而该页面并没有一个带有 <a class=\"reference internal\" href=\"/zh-hans/4.2/ref/templates/builtins/#std-templatetag-csrf_token\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">csrf_token</span></code></a> 的 HTML 表单，这将导致所需的 CSRF cookie 被发送。</p>\n<p>解决方法：在发送页面的视图上使用 <code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">sure_csrf_cookie()</span></code>。</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></code>，所以 contrib 应用程序中的所有相关视图都使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">csrf_protect</span></code> 装饰器来确保这些应用程序针对 CSRF 的安全性。 建议需要相同保证的其他可重用应用程序的开发人员也在其视图上使用 <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 和 CSRF_COOKIE_HTTPONLY 为 False 时获取令牌","anchor":"acquiring-the-token-if-csrf-use-sessions-and-csrf-cookie-httponly-are-false","children":[]},{"title":"当 CSRF_USE_SESSIONS 或 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 令牌。","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":"操作指南","url":"/zh-hans/4.2/howto/"}],"prev":{"docname":"howto/auth-remote-user","title":"使用 REMOTE_USER 进行身份验证","url":"/zh-hans/4.2/howto/auth-remote-user/"},"next":{"docname":"howto/custom-management-commands","title":"编写自定义 django-admin 命令","url":"/zh-hans/4.2/howto/custom-management-commands/"},"formats":{"html":"/zh-hans/4.2/howto/csrf/","markdown":"/zh-hans/4.2/howto/csrf.md","json":"/zh-hans/4.2/howto/csrf.json"},"source":"https://github.com/django/django/blob/stable/4.2.x/docs/howto/csrf.txt","official":"https://docs.djangoproject.com/zh-hans/4.2/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"]}