Discourse 会话 Cookie(400 请求头或 Cookie 过大)

为什么 Discourse 的 Cookie 这么大?有原因吗?有没有办法减小它们的体积?

我在浏览 Discourse 管理后台时,突然从 /sidekiq/retries 收到了一个 400 Bad Request 错误:

400 Bad Request
Request Header Or Cookie Too Large

我检查了请求,果然,我的浏览器向 Discourse 服务器发送了一个巨大的 Cookie 头。为什么会这样?

Cookie: _t=403b8203003bdaf522679e0b6c17605f; rack.session=BAh7C0kiD3Nlc3Npb25faWQGOgZFVG86HVJhY2s6OlNlc3Npb246OlNlc3Np%0Ab25JZAY6D0BwdWJsaWNfaWRJIkU4NjY1Y2Y3MjU4OGYzZjk3MDUyMDdhNzhh%0ANzc4OGZlZTRhNzFkY2JjMzA4NDAyNjY3MWMwNmFhYzc2Zjg0NWIyBjsARkki%0AEF9jc3JmX3Rva2VuBjsARkkiMW9KNk83S1B5ZUVaR0I2WnBxckxISzNxbEla%0AdGRyVXNCUnc3d2JiaVorVmM9BjsARkkiFnNlY3VyZV9zZXNzaW9uX2lkBjsA%0AVEkiJWM3YWJjYTk3OTRlNTExNTllZWUyMTBkYmVkNDgzNDc4BjsARkkiCmZs%0AYXNoBjsAVHsHSSIMZGlzY2FyZAY7AFRbAEkiDGZsYXNoZXMGOwBUewZJIgxy%0AZWZlcmVyBjsAVCJPaHR0cHM6Ly9kaXNjb3Vyc2Uub3BlbnNvdXJjZWVjb2xv%0AZ3kub3JnL3UvbWFsdGZpZWxkMC9tZXNzYWdlcy9ncm91cC9hZG1pbnNJIglj%0Ac3JmBjsARkkiMVRuUHJ6TTMzUHZqcG9oditwdTRyem9HeDUxQnAwL0psci9z%0AYkFZWkpxdkU9BjsARkkiDXRyYWNraW5nBjsARnsGSSIUSFRUUF9VU0VSX0FH%0ARU5UBjsAVEkiLTk3MDJkMjYxMmJlZmM5N2U4YTIzMDVkNjU0Y2IwOThmMmQ4%0AYTI1NTUGOwBG%0A--e602081dddcd88bdb269034e7acb8c582665be0e; _forum_session=V2dWY0FGVGhsMDVtcmdmNVdicXpJVkxnVC9vUWpkeVdpSHRIYWZaVVhVZDUxcFlRdTh4bHFTQVRRcUpGR3pMRGZ1M3NGeENzUloreGdEWEtQS2Z2WDJKNFZUeXRjNXlTTTRHVzJsQzBORzVuSW9NNHg3UHhwNzdUNFlCNGVvcytkSjA1b0d3NlM3czNlTlFxMEloQmNOYzMxTm5mYW4zaWlMSkpxWXZiZDlBRFJnR3dxTkphM0ZtZmk4bGswcUdzYm94b3pkUk0zTG5sMjhqNkxYMnZqMjJPYkhzMGFLM2JWZzBCRXpFa2wyZm1HbUl3REVzd3c5MmhRMG5YMkFJV0t6Z2ZPRlI2bVpOQWJlZWJQd2pyclEvdmVmWUlsYkxyU0EzcDFaZkRpOU14SVptMk01TjZtZlNXa2VUQnFaaGZpNDlaQVBnY0RCS2ZlbVBiQWt3S2lSeHcvY0g2WUlXOTRLejh2dVhhcDFoRXVobUdRMnJvcjhtRTkxZjZCYXM1eDd2NU1rZ3duRy83VVhVSG5Ua3BIOTJoQ1orY2dlMjh2M0Fuc0lwb3p3ckVtaXhxaDkxT2E1YnEvbnBWTVlCaXd4Q2h6eTd5Ty84WUYzeUVFbE9KSXhadXZ4aUw1TmVoSEE0cW9YSHA3VTJoK3NtdktrL09qcDVxMnR5bFhhUmU1dDMzT0ZBUGxBRXBZVHB5WlNtODM2YzBsOVRkc3RpMmFFSW5COEhyRjFTY2ZCZk5VbUpYN2JzYlh6SGNGWEs2dWhQUkJnMmd4K3ZJQUFkQThwa2tOMnI3Vi9qMFo5RE5XWWxxRXFTTTNmRnJKU294aStKZFJ4NHRDTGh4WXR1Z3F5QWU3ZkMxTXBpMzcvYTd5QkRwajNjcDF6SWdFSkdqNDJlMk0vYW1mODNEdDhZSk9jbzRPRHNhZUYzNjVOWkErbVJSNG82VnhRL0FFRUtWbE1uQWFPa0JqQUFmZ21iL0YvSFIrM1dlSDNvPS0tK1pMRzNmRjA4L3c4VTkrVEllYmNQZz09--325ca2e886afaefffeb6174c99776f91fefd4292

上面这个 Discourse Cookie 字符串长达 1,962 个字符!

作为对比,发送到我 Mediawiki 站点的 Cookie 头只有 122 个字符,其中包括我的用户名、用户 ID 和会话 ID。

Mediawiki 的会话 ID 长度为 32 个字符,但 Discourse 似乎有两个会话 ID:rack.session 长达 813 个字符,而 _forum_session 长达 1,075 个字符。请帮我理解为什么一个简单的 uid 需要这么长。我可以配置 Discourse 使用更短的会话 uid 吗?这样的请求合理吗?

这个字符串里存储了什么?有没有可能让它变小?

具体来说,Discourse 软件中的 rack.session 组件是用于什么的?_forum_session 又是用于什么的?

当然,我可以直接调高 nginx 的配置限制,但除非有强烈需求,否则我希望保持这些限制在合理较低的水平。

我支持审查我们的会话 cookie,也许可以默认改用我们基于 Redis 的会话机制,它会自动过期,而且无论如何都更好。@david 对此有什么想法吗?

对于使用我们官方安装程序的用户来说,这应该不是问题,只有那些配置不当的代理用户才会遇到这种情况,对吧?

此外,HTTP/2 头部压缩使得这些头部在整个用户访问期间只需上传一次。

这取决于具体情况。重度依赖会话(session)的插件可能会受到影响。此外,这也关乎安全卫生:将此类敏感信息保留在服务器端,而不是加密后存储在客户端,会更加稳妥。

哎呀,这项调整是出于安全考虑而有意为之的,并非“配置不当”的代理。降低以下 nginx 指令的值通常是 nginx 加固(以解决可用性、速率限制、DDoS 等问题)的常规操作:

limit_conn_zone
limit_conn
limit_req_zone
client_body_timeout
client_header_timeout
ssl_*
add_header # SAMEORIGIN/XSS-Protection/CORS/CSP
client_body_buffer_size
client_header_buffer_size
large_client_header_buffers
client_max_body_size

我们在 nginx 配置中使用的设置在所有现有站点上都能正常运行。这里的问题似乎在于 Discourse 将不必要的客户端数据存储在 cookie 中。依我之见,cookie 中应仅存储少数几个唯一标识符,以便服务器在服务器端访问相关数据。

谢谢 Sam。你所说的“默认切换到使用 Redis”,是指目前可以通过配置更改将会话 cookie 中存储的数据迁移到 Redis 吗?还是说将会话 cookie 中的数据移出并存储到服务器端,必然需要修改代码?

不行,这需要修改代码。

为什么 10k 的头部数据会成为问题?它们是被压缩的。如果 10k 的头部数据有问题,那为什么允许 10k 的 HTML 负载呢?

注意:为了让 Discourse 正常运行,我还不得不覆盖我加固后的 nginx 配置中的 large_client_header_buffers 指令。

具体来说,我在所有其他站点的 nginx 配置中都使用 large_client_header_buffers 2 1k。但这会导致 /admin/reports/bulk?XYZ 返回 414 Request-URI Too Large 错误——其中 XYZ 实际上长达 1,019 个字符!

通过在 Discourse 的 nginx 配置 server{} 块中设置 large_client_header_buffers 4 8k; 解决了此问题,该设置覆盖了全局指令,并将该指令恢复为 Discourse 的默认值。

为了在流行的加固 Web 服务器、网络防火墙和 Web 应用防火墙中实现 Discourse 安装更好的互操作性,我敦促 Discourse 开发者考虑对这类长查询字符串使用 POST 方法。

这是一个非常非常狭窄的使用场景,专门针对管理员。

对于非后端工程师来说,这个问题的解决方法是什么?

过去一个月,我们在 Webflow 论坛 上收到了大量关于此错误的报告。建议访客清除他们的 cookie/缓存或使用隐身模式可以解决问题,但并非理想方案。

对于修复我们 Discourse 实例的这个问题,任何建议都将不胜感激。

您能提供一个例子吗?OP中的这个问题是关于自托管 Discourse 的,所以我不太明白它怎么会影响您链接的那种托管实例。