首页 > 解决方案 > Django CSRF“Referer Malformed”……但事实并非如此

问题描述

我正在尝试测试在开发模式下运行良好的 Django 设置的部署配置。

我通过ssl_preread负载均衡器上的 Nginx 模块进行基于名称的路由,并且 SSL 终止于服务器本身上的另一个 Nginx 实例,其中请求通过套接字代理到 uwsgi。

server {
    server_name dev.domain.net;
    listen 80 proxy_protocol;
    listen [::]:80 proxy_protocol;
        location / {
            return 301 https://$host$request_uri;
        }
}
server {
    server_name dev.domain.net;
    listen 443 ssl;
    listen [::]:443 ssl;
    location / {
        include uwsgi_params;
        uwsgi_pass unix:/run/uwsgi/website.sock;
    }
    location /favicon.ico {
        access_log off; log_not_found off;
    }
}

我将 uwsgi 设置为 log%(host)并且%(referer),它们在日志中匹配。

在我的 uwsgi_params 中,我正在传递$host$referer因为我使用的是基于名称的路由,所以我选择了$server_name触发 Nginx 响应的变量......

uwsgi_param  HTTP_REFERER       $server_name;
uwsgi_param  HTTP_HOST          $host;

向这些添加(或删除)协议和端口没有任何区别。可以预见地将它们带走会产生 DjangoALLOWED_HOSTS调试错误。

我已经确认我的ALLOWED_HOSTS包含$host. 我尝试添加CSRF_TRUSTED_ORIGINS相同的$host变量。我试过设置CSRF_COOKIE_DOMAIN相同的$host变量。根据文档建议,我已CSRF_COOKIE_SECURE设置为 True。

无论使用上述设置的哪种组合,我都会得到:

Referer checking failed - Referer is malformed.在所有 POST 请求上。

标签: djangonginxcsrfuwsgireferer

解决方案


简短的回答:不要使用 uwsgi unix 套接字,而是使用http-socket代理请求并将代理请求通过未加密的 http 发送到 localhost(在 uwsgi ini 文件中):

http-socket = 127.0.0.1:8001

在 nginx 中,去掉 uwsgi 代理参数,只需proxy_pass启用proxy_protocol标头即可:

server {
    server_name dev.domain.net;
    listen 443 ssl proxy_protocol;
    listen [::]:443 ssl proxy_protocol;
    location / {
        proxy_pass http://127.0.0.1:8001;
    }
    location /favicon.ico {
        access_log off; log_not_found off;
    }
}

此时,您可以启用 Django 文档中所有推荐的部署设置,明确声明您的ALLOWED_HOSTS并且一切正常。

这是一系列非常愚蠢的圈套,没有明显的正确答案,特别是考虑到引用者是很容易伪造的客户端标头。

更好的答案是 Django 需要在其 CSRF 机制中摆脱客户端引用检查,这是毫无意义的,没有意义......


推荐阅读