bigywater.tech
Flask · 127

Host Header 校验

请求里的 Host 头能信吗?Flask 3.1 的 TRUSTED_HOSTS 怎么用?SERVER_NAME 与它有什么区别?邮件里的链接该怎么生成?这篇用 test_client 与真实 gunicorn 把每种配置的结果逐个打印出来(防御向,只验证配置的效果)。

400
Host 不在 TRUSTED_HOSTS 名单里时的状态码
固定基地址
生成邮件与重置链接时最稳妥、不受请求影响的做法
x_host=1
让 ProxyFix 信任 X-Forwarded-Host 的参数,只在有可信代理时使用

默认接受任何 Host

实测 · 本机执行输出
  Host: example.com        → 200 {'host': 'example.com', 'link': 'http://example.com/info'}
  Host: other.test         → 200 {'host': 'other.test', 'link': 'http://other.test/info'}
  Host: example.com:8080   → 200 {'host': 'example.com:8080', 'link': 'http://example.com:8080/info'}
  (任何 Host 都被接受;生成密码重置链接、邮件里的绝对地址如果直接用 request.host / url_for(_external=True),地址就由请求方决定)

上一篇:文件上传类型校验;下一篇:CORS 安全边界。

TRUSTED_HOSTS

实测 · 本机执行输出
  Host: 'example.com'        → 200 {'host': 'example.com', 'link': 'http://example.com/info'}
  Host: 'example.com:8080'   → 200 {'host': 'example.com:8080', 'link': 'http://example.com:8080/info'}
  Host: 'EXAMPLE.com'        → 400 <!doctype html> <html lang=en> <title>400 Bad Request</title
  Host: 'other.test'         → 400 <!doctype html> <html lang=en> <title>400 Bad Request</title
  Host: 'www.example.org'    → 200 {'host': 'www.example.org', 'link': 'http://www.example.org/info'}
  Host: 'example.org'        → 200 {'host': 'example.org', 'link': 'http://example.org/info'}
  Host: 'evil-example.com'   → 400 <!doctype html> <html lang=en> <title>400 Bad Request</title
  Host: ''                   → 400 <!doctype html> <html lang=en> <title>400 Bad Request</title

SERVER_NAME

实测 · 本机执行输出
  SERVER_NAME=example.com,Host: example.com  → 200 {'host': 'example.com', 'link': 'http://example.com/info'}
  SERVER_NAME=example.com,Host: other.test   → 200 {'host': 'other.test', 'link': 'http://example.com/info'}
  请求之外(应用上下文里)url_for(_external=True): http://example.com/info

固定基地址

实测 · 本机执行输出
  Host: other.test → {'host_based': 'http://other.test/info', 'link': 'https://app.example.com/info'}

反向代理与 X-Forwarded-Host

实测 · 本机执行输出
  没有 ProxyFix          Host: internal:8000, X-Forwarded-Host: public.example.com → {'host': 'internal:8000', 'link': 'http://internal:8000/info'}
  ProxyFix(x_host=1)   Host: internal:8000, X-Forwarded-Host: public.example.com → {'host': 'public.example.com', 'link': 'http://public.example.com/info'}
  (只有前面确实有会覆盖这个头的可信代理时才应该启用;否则客户端自己就能伪造)

真实 gunicorn

实测 · 本机执行输出
  Host: example.com        → 200 {"host":"example.com"}
  Host: other.test         → 400 <!doctype html> <html lang=en> <title>400 Bad Request</title> <h1>Bad 
  Host: 127.0.0.1:9601     → 400 <!doctype html> <html lang=en> <title>400 Bad Request</title> <h1>Bad 

分步演示

Host Header 校验
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动
    • 没有验证:TRUSTED_HOSTS 大小写匹配的原因、IPv6 地址形式的 Host、多个 Host 头、nginx 层面的 server_name 与默认 server 拒绝未知 Host(见部署部分)、subdomain_matching 与 host_matching 的路由行为
    京ICP备2026067502号-1京公网安备11010502063487号