Django 不会为 csrftoken cookie 设置 HttpOnly

Django won't set HttpOnly for csrftoken cookie

在我的 Django 中 settings.py 我有

SESSION_COOKIE_HTTPONLY = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 15768000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SESSION_COOKIE_AGE = 2 * 24 * 3600

不过https://detectify.com has found that this flag isn't set for csrftoken cookie. I checked what Chrome tells about the cookie, and if I understand correctly, the empty HTTP column confirms that the two cookies are not HTTP-only:

此外,如果我在 chrome 的控制台中执行 document.cookie,则会显示 csrftoken 值。

我想知道为什么会这样。我在 uwsgi 和 nginx 上有 Django 运行。 nginx配置如下,问题站点为https://rodichi.net:

server {
    listen 443 ssl http2 default_server;
    server_name rodichi.net;

    ssl_certificate /etc/letsencrypt/live/rodichi.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/rodichi.net/privkey.pem;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    ssl_protocols TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    charset     utf-8;

    ... # location settings follow here

```

您仅将其配置为将 CSRF 令牌设置为安全(即仅通过 https 请求发送)而不是 HttpOnly(即对 Javascript 不可用)。

查看 Django 文档您还需要设置 CSRF_COOKIE_HTTPONLY。但是文档正确地指出:

Designating the CSRF cookie as HttpOnly doesn’t offer any practical protection because CSRF is only to protect against cross-domain attacks. If an attacker can read the cookie via JavaScript, they’re already on the same domain as far as the browser knows, so they can do anything they like anyway. (XSS is a much bigger hole than CSRF.)

Although the setting offers little practical benefit, it’s sometimes required by security auditors.

这也取决于您是如何实施 CSRF 的。表单基本上有两种方法:

  1. 为每个表单设置一个隐藏的CSRF字段,并使该字段在每次加载表单时生成一个唯一的值。因此,如果提交的表单包含有效代码,那么您就知道请求来自您的域。这在服务器端很复杂,因为它需要跟踪有效令牌,也意味着必须动态生成每个表单以包含随机令牌,但在客户端更容易使用标准表单请求而不是 JavaScript。对于这种保护,不需要 CSRF cookie,即使它存在也不会被使用。

  2. 另一种方法调用设置 CSRF cookie,让 Javascript 读取它并在 HTTP header(通常是 X-CSRF-TOKEN)中发送它。来自另一个域的 CSRF 请求将无法访问此 CSRF cookie,因此无法正确设置 header。由于 cookie 也会在所有请求上发送,因此服务器很容易检查 HTTP 请求中的 cookie 是否与请求中设置的 header 相匹配。这意味着请求来自可以访问 cookie 的某个地方,这意味着它来自同一个域。这意味着它不是 CSRF 攻击。这在服务器端更容易实现(因为不需要保留活动令牌列表)但在前端需要 Javascript 并且需要 CSRF 令牌 而不是 HttpOnly - 正是因为令牌应该由客户端读取 Javascript!

再次 Django documentation warns against this:

If you enable this and need to send the value of the CSRF token with an AJAX request, your JavaScript must pull the value from a hidden CSRF token form input on the page instead of from the cookie.

因此,总而言之,建议为此 cookie 设置 HttpOnly 属性。它限制了您,没有增加真正的保护,并且使 cookie 本身变得毫无意义。

您会在您网站上的渗透测试报告中突出显示它(包括 https://detectify.com by the looks of things) but should accept that as you are comfortable that this is correct. Not sure if it's possible to whitelist this cookie in https://detectify.com 所以它不会每次都发出警报?