aekasitt/fastapi-csrf-protect

GitHub: aekasitt/fastapi-csrf-protect

为 FastAPI 应用提供基于双重提交 Cookie 模式的无状态 CSRF 防护扩展。

Stars: 110 | Forks: 15

# FastAPI CSRF Protect [![Package version](https://img.shields.io/pypi/v/fastapi-csrf-protect)](https://pypi.org/project/fastapi-csrf-protect) [![Format](https://img.shields.io/pypi/format/fastapi-csrf-protect)](https://pypi.org/project/fastapi-csrf-protect) [![Python version](https://img.shields.io/pypi/pyversions/fastapi-csrf-protect)](https://pypi.org/project/fastapi-csrf-protect) [![License](https://img.shields.io/pypi/l/fastapi-csrf-protect)](https://pypi.org/project/fastapi-csrf-protect) [![Top](https://img.shields.io/github/languages/top/aekasitt/fastapi-csrf-protect)](.) [![Languages](https://img.shields.io/github/languages/count/aekasitt/fastapi-csrf-protect)](.) [![Size](https://img.shields.io/github/repo-size/aekasitt/fastapi-csrf-protect)](.) [![Last commit](https://img.shields.io/github/last-commit/aekasitt/fastapi-csrf-protect/master)](.) [![Documentation](https://img.shields.io/badge/pdoc-555?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAkUExURUdwTJHtkZDtkI/vj2rOYFXBQn3deZDukDuzAHDTaFrES4HhfmcEZqoAAAAHdFJOUwCAv0Df788Wv3t3AAACCElEQVQ4y42UsXKcMBCGuVC4Pc6FJuPGRzLDnK/xnAsKP0IeIU+QLhyFEHoAVlJlNxJHlwbBVY4LyeeXs4BLCnQzCTOAZj+0K+3/iyD4zwvhIIiiey/+iVRB8LnJPJBq6QA8eeBRuI8R8UFKTYbc7QGqQRIAOa++KKAD6LW4nQMFwgIB7gMlLaPQz8GVMlhxjD2AoKVcSeKl2jCAyq2K/5yBsiINRqSx+QwUcmhgFNmXOWivzO2CZsWTlyrl2U69qPmMjUi0pBqreY0bvi8BGGHzVaVKJnWHi2o5bwlhOI4JGF9yzeuTPua+5qoHEHvfDAvF398qPz70vboRl4DV8vESCC3HqSfsAAotkNf0sY1HhnR+AZTQruzrBXAHLLe/h9HDtIRwFa+v3XvL4bX8NXpvrLTDb31tImdezvO7dgipAYTOmhyYXAYptOvNuPVR99Ry24BieJlqEX8bdzhUWlBo3JQTMExZ//X7BNplmCgN4t3FFdecnUHJcHLi6ogfEurCujdlMwK38AMItd/FETp0TD2vi6nvW3AHYkje1VmcKJ6humyn46hB1ebkqPwhsYkJs8+jmLYp2Z5AgwkfnlRLPR7BsBTYUH3M7pEVRAMcT2dXbbWLM6d/SEFi6pKayVU75z2Oh3FKoKt7Lv/IkRw6M4mJXKrerP8aII6vz8MVxvsv//gNfQBIOvQNeKr0GQAAAABJRU5ErkJggg==)](https://aekasitt.github.io/fastapi-csrf-protect) [![Protect Banner](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/b58cfc0432004252.svg)](https://github.com/aekasitt/fastapi-csrf-protect/blob/master/static/protect-banner.svg) ## 功能 提供无状态跨站请求伪造(Cross-Site Request Forgery, XSRF)保护支持的 FastAPI 扩展。 旨在实现易用和轻量化,我们采用了 [双重提交 Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie) 缓解模式。 如果您之前熟悉 `flask-wtf` 库,那么这个扩展非常适合您。 该扩展的灵感来源于 `fastapi-jwt-auth` 😀 - 将 `fastapi-csrf-token` 存储在 cookie 中,或者在模板上下文中提供 ## 安装说明 开始使用此扩展最简单的方式是通过 pip ``` pip install fastapi-csrf-protect # 或者 uv add fastapi-csrf-protect ``` ## 快速入门 以下示例展示了如何将此扩展集成到 FastAPI 应用中 ### 登录表单示例 ``` from fastapi import FastAPI, Request, Depends from fastapi.responses import JSONResponse from fastapi.templating import Jinja2Templates from fastapi_csrf_protect import CsrfProtect from fastapi_csrf_protect.exceptions import CsrfProtectError from pydantic_settings import BaseSettings app = FastAPI() templates = Jinja2Templates(directory="templates") class CsrfSettings(BaseSettings): secret_key: str = "asecrettoeverybody" cookie_samesite: str = "none" @CsrfProtect.load_config def get_csrf_config(): return CsrfSettings() @app.get("/login") def form(request: Request, csrf_protect: CsrfProtect = Depends()): """ Returns form template. """ csrf_token, signed_token = csrf_protect.generate_csrf_tokens() response = templates.TemplateResponse( "form.html", {"request": request, "csrf_token": csrf_token} ) csrf_protect.set_csrf_cookie(signed_token, response) return response @app.post("/login", response_class=JSONResponse) async def create_post(request: Request, csrf_protect: CsrfProtect = Depends()): """ Creates a new Post """ await csrf_protect.validate_csrf(request) response: JSONResponse = JSONResponse(status_code=200, content={"detail": "OK"}) csrf_protect.unset_csrf_cookie(response) # prevent token reuse return response @app.exception_handler(CsrfProtectError) def csrf_protect_exception_handler(request: Request, exc: CsrfProtectError): return JSONResponse(status_code=exc.status_code, content={"detail": exc.message}) ``` ### 如何在客户端代码中发送 CSRF token #### HTML 表单(服务端渲染) ```
``` #### AJAX (JavaScript) ``` fetch("/items/123", { method: "DELETE", headers: { "X-CSRFToken": getCookie("csrftoken") }, credentials: "include" }); ``` ### 📌 灵活模式 (fastapi_csrf_protect.flexible) 某些应用程序会在同一个项目中结合使用 **服务端渲染 (SSR)** 和 **API endpoint**。 例如: - 使用 Jinja2 模板渲染并使用 HTML 表单的 **SSR 页面**(CSRF token 位于**表单主体**中) - 在 **HTTP header** 中传递 CSRF token 的 **AJAX / API 调用**(例如 DELETE、PUT、PATCH) 主要的 fastapi-csrf-protect 包具有**强约定性**,并要求 CSRF token **仅存在于一个位置**(header 或 body)。 对于混合应用来说,这可能不太方便。 **灵活子包 (flexible sub-package)** 提供了 CsrfProtect 的直接替代方案,它**始终接受来自 header 或表单主体的 CSRF token**,优先级如下: - **Header**: X-CSRFToken - **Body**: token_key (form-data) ### 何时使用灵活模式 在以下情况下使用 fastapi_csrf_protect.flexible: - 您在同一个项目中同时拥有 SSR 页面和 API endpoint。 - 某些请求(如 DELETE)无法发送 body,但仍需要 CSRF 验证。 - 您希望避免维护两套不同的 CSRF 配置。 如果您的应用仅使用**一种**方法发送 CSRF token,请继续使用**核心包**以获得更严格的策略。 ### 前置条件 * [git](https://git-scm.com/) - --fast-version-control * [python](https://www.python.org) 3.10 及以上版本 - 高级通用编程语言 * [uv](https://docs.astral.sh/uv) - 极速的 Python 包和项目管理器,使用 Rust 编写 以下指南将介绍如何使用 `git` 作为分布式版本控制系统,以及 `uv` 作为 Python 包和版本管理器来设置本地工作环境。 如果您尚未安装 `git`,请运行以下命令。
使用 Homebrew 安装 (Darwin) ``` brew install git ```
通过二进制安装程序安装 (Linux) * 基于 Debian 的包管理 ``` sudo apt install git-all ``` * 基于 Fedora 的包管理 ``` sudo dnf install git-all ```
如果您尚未安装 `uv`,请运行以下命令。
使用 Homebrew 安装 (Darwin) ``` brew install uv ```
使用独立安装程序安装 (Darwin 和 Linux) ``` curl -LsSf https://astral.sh/uv/install.sh | sh ```
一旦安装了 `git` 分布式版本控制系统,您就可以 克隆当前代码库,并为该项目安装 3.10 以上版本的 Python。 以下命令可帮助您设置并激活 Python 虚拟环境,随后 `uv` 便可以从 `pyproject.toml` 文件中定义的 `PyPI` 开源注册表下载项目依赖项。
设置环境并同步项目依赖 ``` git clone git@github.com:aekasitt/fastapi-csrf-protect.git cd fastapi-csrf-protect uv venv --python 3.10.18 source .venv/bin/activate uv sync --dev ```
### 路线图 * 修复 CI/CD 和 GitHub Pages 集成 * 在设置和运行测试时添加代码示例 * 更正 `README.md` 中的前端示例 * 添加 `flexible` 示例 * 重写 `README.md` 中的 `Flexible Mode` 部分 * 测试 `granian[uvloop]` 和 `granian[rloop]` ## 更新日志 * **0.3.1** 采用 [双重提交 Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie) :construction: **重大变更** 0.3.0 -> 0.3.1: `generate_csrf` 已标记为弃用 * **0.3.2** 添加 `token_location` 配置(可选 `body` 或 `header`);不设置则防止 token 重用 :construction: **重大变更** 0.3.1 -> 0.3.2: `validate_csrf` 现在是异步的 * **0.3.5** 引入了与 Pydantic V2 相关的 bug,已在 0.3.6 版本中修复;影响 `cookie_samesite` * **0.3.6** 修复了前一版本引入的 `cookie_samesite` 验证 bug * **1.0.0** 移除已弃用的 `generate_csrf`,请使用返回元组的 `generate_csrf_tokens` * **1.0.1** 修复了在使用 cookie `Secure` 和/或 `SameSite=None` 配置库时的 cookie 取消设置问题 * **1.0.2** 改进了 `LoadConfig` 的布尔值处理 * **1.0.3** 尝试集成 `mypyc` 编译的实验失败,原因是依赖注入模式 * **1.0.4** 添加了在省略 `token_location` 时的灵活模式以及多位置检查 :construction: **发布失败** 1.0.3 -> 1.0.4: 带着半成品代码发布;立即从 PyPI 撤回了该版本 * **1.0.5** 移除失败实验中遗留的 `@dataclass`;澄清测试下的失败原因 * **1.0.6** 修复了通过 form data 提交 token 时出现的 `Stream consumed` 问题,`isinstance` 会消耗主体 * **1.0.7** 修复了在 `Flexible Mode` 下添加预验证内容的问题;添加测试选择标志 ### 运行示例 要运行提供的示例,您首先必须安装额外的依赖项 [granian](https://github.com/emmett-framework/granian) 和 [minijinja](https://github.com/mitsuhiko/minijinja/tree/main/minijinja-py) 或者,在终端运行以下命令来完成安装 ``` uv sync --group=examples ``` 运行使用表单提交的示例 ``` granian --interface asgi examples.body:app ``` 运行通过 JavaScript 使用 header 的示例 ``` granian --interface asgi examples.header:app ``` ## 许可证 本项目基于 MIT 许可证条款授权。
标签:AV绕过, CSRF防护, FastAPI, Python, Syscall, Web开发, 中间件, 无后门, 网络安全, 逆向工具, 隐私保护