跳到内容

严格的 Content-Type 检查

默认情况下,FastAPI 对 JSON 请求体使用严格的 Content-Type 标头检查,这意味着 JSON 请求必须包含有效的 Content-Type 标头(例如 application/json),以便将请求体解析为 JSON。

CSRF 风险

这种默认行为在非常特定的场景下为跨站请求伪造 (CSRF) 攻击提供了一类保护。

这些攻击利用了浏览器允许脚本在以下情况下发送请求且不执行任何 CORS 预检检查的事实:

  • 没有 Content-Type 标头(例如使用带有 Blob 请求体的 fetch()
  • 并且不发送任何身份验证凭据。

这种类型的攻击主要在以下情况中相关:

  • 应用程序在本地(例如在 localhost 上)或内部网络中运行
  • 并且应用程序没有任何身份验证,它默认信任来自同一网络的所有请求。

攻击示例

假设你构建了一种运行本地 AI 代理的方法。

它提供了一个 API,地址为

https://:8000/v1/agents/multivac

此外还有一个前端,地址为

https://:8000

提示

请注意,两者具有相同的主机名。

然后,通过前端,你可以让 AI 代理替你执行操作。

由于它在本地运行,而不是在开放的互联网上,你决定不设置任何身份验证,只信任对本地网络的访问。

然后,你的用户之一可能会安装并将其在本地运行。

接着他们可能会打开一个恶意网站,例如类似于

https://evilhackers.example.com

该恶意网站使用带有 Blob 请求体的 fetch() 向本地 API 发送请求,地址为

https://:8000/v1/agents/multivac

尽管恶意网站的主机名与本地应用程序不同,但浏览器不会触发 CORS 预检请求,因为:

  • 它在没有任何身份验证的情况下运行,不需要发送任何凭据。
  • 浏览器认为它发送的不是 JSON(因为缺少 Content-Type 标头)。

于是恶意网站就可以让本地 AI 代理向用户的前老板发送愤怒的消息……或者更糟。 😅

开放互联网

如果你的应用程序运行在开放的互联网上,你通常不会“信任网络”并允许任何人无需身份验证即可发送特权请求。

攻击者可以直接运行脚本向你的 API 发送请求,无需浏览器交互,因此你很可能已经保护了任何特权端点。

在这种情况下,这种攻击/风险对你不适用

这种风险和攻击主要与应用程序在本地网络上运行,且该网络是唯一的假定保护措施的情况相关。

允许不带 Content-Type 的请求

如果你需要支持不发送 Content-Type 标头的客户端,可以通过设置 strict_content_type=False 来禁用严格检查。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(strict_content_type=False)


class Item(BaseModel):
    name: str
    price: float


@app.post("/items/")
async def create_item(item: Item):
    return item

使用此设置,没有 Content-Type 标头的请求仍会被解析为 JSON,这与旧版本 FastAPI 的行为相同。

信息

此行为和配置是在 FastAPI 0.132.0 中添加的。