TT Lab
开始
学习 学习路径 课程

FastAPI — 类型就是契约

CORS 不是身份认证

在 TT Lab 中继续学习

目标

用真实的预检请求来验证源、方法和请求头的允许矩阵。

为什么重要

前端读不到响应,于是对所有源都用通配符放行。在发送 Cookie 的请求中,策略变得更复杂,而开发者又误以为只要打开 CORS,外部请求就会被拦截。本实验把浏览器的读取策略和服务器认证分开。它不会替你实现认证功能。

步骤

  1. 在 /root/work/fa-cors-policy-lab/service.py 中,origin(value) 在它是 http 或 https 的 URL,有 host,且没有 path、query、fragment 和用户信息时,返回输入字符串。其余都是 ValueError。末尾的 / 也属于 path,所以要拒绝。

先做一次准备。已有的文件不会被覆盖。

mkdir -p /root/work/fa-cors-policy-lab
test -e /root/work/fa-cors-policy-lab/service.py || cp /opt/fixtures/ten_labs/fa-cors-policy-lab/service.py /root/work/fa-cors-policy-lab/service.py
cd /root/work/fa-cors-policy-lab
  1. 在 /root/work/fa-cors-policy-lab/service.py 中,origins(values) 先用 origin 验证每一项,再按首次出现的顺序去除重复,得到新列表。

  2. 在 /root/work/fa-cors-policy-lab/service.py 中,methods(values) 只允许 GET、POST、PUT、DELETE、OPTIONS,并转成大写后去除重复。空列表或其他值都是 ValueError。

  3. 在 /root/work/fa-cors-policy-lab/service.py 中,policy(allowed, credentials) 检查 credentials 是否是 bool。如果 allowed 中有 '*',就是 ValueError,并返回 {allow_origins:origins(allowed), allow_credentials:credentials}。

  4. 在 /root/work/fa-cors-policy-lab/service.py 中,create_app(allowed, credentials=True) 是验证了 policy 并设置了 CORSMiddleware 的应用。它只允许 GET/POST,允许 Content-Type 和 X-Request-ID 请求头,并暴露(expose)X-Trace 响应头。GET /data 返回 {ok:True},以及 X-Trace='trace-1'。

  5. 在 /root/work/fa-cors-policy-lab/service.py 中,preflight_headers(source, method, requested='X-Request-ID') 是一个带有 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 三个键的字典。method 是大写。

  6. 在 /root/work/fa-cors-policy-lab/service.py 中,preflight_status(app, source, method, requested='X-Request-ID') 用 TestClient 向 /data 发送 OPTIONS 请求,并返回 HTTP 状态。其他源、DELETE、X-Secret 头都必须是 400。

  7. 在 /root/work/fa-cors-policy-lab/service.py 中,cors_observation(app, source) 发送 GET /data,并返回(状态,Access-Control-Allow-Origin 的值或 None,JSON 正文)。即使是不被允许的源,200 的正文也会被执行,但不应带有允许源的头。

参考

验证源的格式

在 /root/work/fa-cors-policy-lab/service.py 中,origin(value) 在它是 http 或 https 的 URL,有 host,且没有 path、query、fragment 和用户信息时,返回输入字符串。其余都是 ValueError。末尾的 / 也属于 path,所以要拒绝。

先做一次准备。已有的文件不会被覆盖。

mkdir -p /root/work/fa-cors-policy-lab
test -e /root/work/fa-cors-policy-lab/service.py || cp /opt/fixtures/ten_labs/fa-cors-policy-lab/service.py /root/work/fa-cors-policy-lab/service.py
cd /root/work/fa-cors-policy-lab

如果把整个 URL 都当作源允许,就可能把路径或用户信息混淆。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/01-contract.sh 确认。

去除重复的源

在 /root/work/fa-cors-policy-lab/service.py 中,origins(values) 先用 origin 验证每一项,再按首次出现的顺序去除重复,得到新列表。

白名单是精确的源列表,而不是字符串的部分匹配。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/02-contract.sh 确认。

用白名单限制方法

在 /root/work/fa-cors-policy-lab/service.py 中,methods(values) 只允许 GET、POST、PUT、DELETE、OPTIONS,并转成大写后去除重复。空列表或其他值都是 ValueError。

不要悄悄加入没有被允许的 PATCH 和任意方法。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/03-contract.sh 确认。

不同时允许凭据和通配符

在 /root/work/fa-cors-policy-lab/service.py 中,policy(allowed, credentials) 检查 credentials 是否是 bool。如果 allowed 中有 '*',就是 ValueError,并返回 {allow_origins:origins(allowed), allow_credentials:credentials}。

本实验的明确策略是,无论是否允许凭据,都不接受通配符。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/04-contract.sh 确认。

挂上真实的 CORS 中间件

在 /root/work/fa-cors-policy-lab/service.py 中,create_app(allowed, credentials=True) 是验证了 policy 并设置了 CORSMiddleware 的应用。它只允许 GET/POST,允许 Content-Type 和 X-Request-ID 请求头,并暴露(expose)X-Trace 响应头。GET /data 返回 {ok:True},以及 X-Trace='trace-1'。

如果在 preflight 和实际响应中手动分别添加头,两套策略很容易产生偏差。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/05-contract.sh 确认。

构造预检请求

在 /root/work/fa-cors-policy-lab/service.py 中,preflight_headers(source, method, requested='X-Request-ID') 是一个带有 Origin、Access-Control-Request-Method、Access-Control-Request-Headers 三个键的字典。method 是大写。

实际的请求方法是 OPTIONS,要检查的方法在另一个头里。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/06-contract.sh 确认。

计算拒绝矩阵

在 /root/work/fa-cors-policy-lab/service.py 中,preflight_status(app, source, method, requested='X-Request-ID') 用 TestClient 向 /data 发送 OPTIONS 请求,并返回 HTTP 状态。其他源、DELETE、X-Secret 头都必须是 400。

不要把三种拒绝原因混在同一个请求里,才能找出缺失的策略。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/07-contract.sh 确认。

观察 CORS 与认证的区别

在 /root/work/fa-cors-policy-lab/service.py 中,cors_observation(app, source) 发送 GET /data,并返回(状态,Access-Control-Allow-Origin 的值或 None,JSON 正文)。即使是不被允许的源,200 的正文也会被执行,但不应带有允许源的头。

curl 或服务器之间的请求不遵守浏览器的 CORS 读取限制。

保存后用 bash /opt/lab/checks/fa-cors-policy-lab/08-contract.sh 确认。