🔐 跨域(CORS)与 CSRF:你以为的安全,真的安全吗?
在现代 Web 开发中,前后端分离已成为主流架构,随之而来的跨域问题也让开发者们不得不与 CORS(跨源资源共享,Cross-Origin Resource Sharing) 打交道。
很多开发者配置 CORS 的初衷很简单:让前端网站能正常访问后端 API 数据。 但如果你因此认为:"只要配置了 CORS,我的接口就安全了",那你就低估了 Web 安全的复杂性。
本文将带你深入探讨:
- 什么是跨域?为什么浏览器要限制它?
- CORS 是如何"允许跨域"的?它真的能保护我的后端数据吗?
- 如果我允许所有来源访问(
CORS_ALLOW_ALL_ORIGINS = True
),会发生什么? - 为什么即使有 CORS,恶意网站仍可能通过 Nginx 代理等方式调用我的接口?
- 什么是 CSRF?为什么它才是真正防御恶意请求的关键?
- 为什么 Django 默认有 CSRF 防护,而 FastAPI 没有?
- 如何在 FastAPI 中实现类似 Django 的 CSRF 保护?
一、什么是跨域?为什么浏览器要限制它?
跨域问题来源于浏览器的核心安全机制 ------ 同源策略(Same-Origin Policy, SOP)。
什么是同源?
如果两个 URL 的 协议(http/https)、域名(例如 example.com)、端口(如 80 或 443) 完全一致,那么它们就是同源的。反之,则是不同源。
例如:
http://localhost:3000
与http://api.example.com
是不同源的;https://your-site.com
与https://your-site.com:8080
也是不同源的(端口不同)。
浏览器的默认行为:
当你的前端页面(比如运行在 http://localhost:3000
)尝试通过 AJAX / Fetch 请求访问不同源的后端 API(比如 http://api.example.com
)时,浏览器默认会阻止前端 JavaScript 读取响应内容,尽管请求本身会正常发送到服务器。
✅ 请求会发到后端,但 ✅ 浏览器会根据响应头决定是否把数据交给前端 JS。
这就是同源策略,它的目的是防止恶意网站窃取用户数据。
二、CORS 是什么?后端如何"允许跨域"?
为了解决跨域问题,让浏览器"放行"某些外部域名的请求,后端可以通过设置 HTTP 响应头来声明:"我允许哪些网站跨域访问我"。
这就是 CORS(Cross-Origin Resource Sharing,跨源资源共享)。
举个例子(Django 中常见配置):
使用 django-cors-headers
库,你可以通过如下响应头告诉浏览器:
arduino
Access-Control-Allow-Origin: https://your-frontend.com
意思是:只有来自 https://your-frontend.com
的请求,我才会允许浏览器把响应数据交给它的 JavaScript。
三、后端开启 CORS,是否有安全风险?
是的,有风险,特别是配置不当的时候。
常见高风险配置:
Access-Control-Allow-Origin: *
(允许任意网站访问)Access-Control-Allow-Credentials: true
(允许携带 cookies) 与*
同时使用(浏览器禁止!)- 没有对敏感接口做 CSRF 保护
潜在风险包括:
- 敏感数据泄露:如果你的 API 返回用户资料等敏感信息,且允许任意跨域访问,那么任何网站的前端 JS 都可能拿到这些数据。
- CSRF 攻击风险上升:如果接口支持修改操作(如 POST/PUT/DELETE),并且允许跨域携带 cookies,但没有 CSRF Token 校验,攻击者可能诱导用户发起恶意请求。
- 恶意网站滥用 API:即使没有直接窃取数据,攻击者也可以通过合法跨域请求,频繁调用你的接口,造成资源浪费或业务逻辑被滥用。
四、如果我配置了 CORS_ALLOW_ALL_ORIGINS = True,是不是意味着所有网站都能调用我的接口?
✅ 是的,从浏览器角度来看:如果后端允许所有来源(比如通过 Access-Control-Allow-Origin: *
或 Django 中设置 CORS_ALLOW_ALL_ORIGINS = True
),那么任何网站(如 https://evil.com
)的前端 JS 都可以向你的 API 发送请求,并获取响应。
⚠️ 但这仅仅是浏览器不阻止,不代表你的接口是安全的!
五、那为什么通过 Nginx 反向代理,恶意网站仍然能调用我的接口,甚至带上我的 Cookies?
关键点:CORS 是浏览器机制,不是网络层防护!
- 即使你没有配置 CORS,或者配置了允许所有来源,恶意网站仍然可以通过服务端代理(如 Nginx)、Postman、curl、自己的程序,直接访问你的后端接口。
- 如果用户已经登录,浏览器或代理请求可能会自动携带 Cookies,导致用户身份被冒用。
🔐 这就是为什么:跨域限制只能防止浏览器中的恶意 JS 偷数据,但挡不住真正有技术能力的攻击者直接调用你的接口。
六、那 CSRF(跨站请求伪造)防护就很重要了,对吧?
✅ 完全正确!
什么是 CSRF 攻击?
CSRF(跨站请求伪造)是指:攻击者诱导用户访问恶意网站,该网站通过浏览器自动携带用户的登录凭证(如 Cookies),向目标网站(比如你的银行 API)发起恶意请求,比如转账、修改密码等。
为什么 Django 默认有 CSRF 保护,而 FastAPI 没有?
- Django 偏向服务端渲染 + Cookie Session 模式,容易受到 CSRF 攻击,所以默认启用了 CSRF Token 校验。
- FastAPI 更多用于前后端分离 + Token(如 JWT)认证 ,Token 一般放在 HTTP Header(如
Authorization: Bearer xxx
)中,浏览器不会自动携带,因此 CSRF 攻击几乎无效,所以 FastAPI 默认不提供 CSRF 保护。
但这并不意味着 FastAPI 不安全,而是它所应对的使用场景天然降低了 CSRF 风险。
七、如果我在 FastAPI 中使用了 Cookie 认证,如何实现 CSRF 保护?
如果你在 FastAPI 中使用了 基于 Cookie 的身份认证(比如传统 Session、Token 存 Cookie) ,那么你 必须手动实现 CSRF 保护,否则将非常危险。
方法一:使用第三方库 fastapi-csrf-protect
这是目前较为成熟的解决方案,使用方式类似 Django 的 CSRF 中间件。
安装:
pip install fastapi-csrf-protection
基本使用:
python
from fastapi import FastAPI, Depends, Request
from fastapi_csrf_protect import CsrfProtect
from fastapi_csrf_protect.exceptions import CsrfProtectError
app = FastAPI()
csrf_protect = CsrfProtect()
@app.on_event("startup")
async def startup():
await csrf_protect.init()
@app.exception_handler(CsrfProtectError)
def csrf_protect_exception_handler(request: Request, exc: CsrfProtectError):
return JSONResponse(status_code=403, content={"detail": "CSRF token missing or invalid"})
@app.post("/protected")
async def protected_route(request: Request, csrf_token: str = Depends(csrf_protect)):
return {"message": "CSRF 校验通过!"}
前端需要:
- 从 Cookie 或页面中获取 CSRF Token;
- 在请求头(如
X-CSRF-Token
)中将其发送给后端。
方法二:手动实现(基于 Cookie + Header)
- 后端在用户登录后生成 CSRF Token,存入 Cookie 或 Session;
- 前端从 Cookie 读取 Token,然后在每次敏感请求的 Header(如
X-CSRF-Token
)中携带; - 后端校验 Header 中的 Token 是否与 Cookie 中的一致,不一致则拒绝请求。
八、总结:跨域 ≠ 安全,CORS 只是第一道防线
机制 | 作用 | 是否能防恶意调用 | 是否默认安全 | 是否需要额外防护 |
---|---|---|---|---|
CORS | 控制哪些外部网站的前端 JS 可访问你的 API | ❌ 不能防服务端/代理调用 | ⚠️ 弱防护 | ✅ 必须合理配置 |
CSRF Token | 防止恶意网站伪造用户身份发起请求 | ✅ 能有效防御 | ✅(Django 默认有) | ✅(FastAPI 需手动) |
身份认证(JWT / Session) | 确认用户身份合法性 | ✅ | ✅ | ✅ |
网络层防护(Nginx / 防火墙) | 限制访问来源 IP / 接口暴露面 | ✅ | ⚠️ 依赖配置 | ✅ 推荐 |
✅ 最佳实践建议
- 不要依赖 CORS 作为唯一安全机制。
- 敏感接口一定要使用身份认证(如 JWT、Session) + 权限控制。
- 如果你的接口依赖 Cookie,请务必加上 CSRF Token 校验。
- 在 FastAPI 中使用 Cookie 认证时,推荐使用
fastapi-csrf-protect
或手动实现 CSRF 校验。 - 永远不要使用
Access-Control-Allow-Origin: *
处理需要身份验证的接口。 - 使用 HTTPS、接口限流、操作日志等多种手段构建多层次安全防护。
🔒 一句话总结:
跨域(CORS)是浏览器为了保护用户数据而设计的一种浅层防护机制,它不能阻止真正的黑客或服务端直接访问你的后端接口。如果你使用 Cookie 类认证,CSRF 防护才是真正防御恶意请求的关键,无论是 Django 还是 FastAPI,都应当引起足够重视。安全,永远是一个多层次、立体化的工程。
如你喜欢,这篇文章还可以扩展为系列内容,比如:
- 《Django 安全实战:如何正确配置 CORS 与 CSRF》
- 《FastAPI 安全指南:身份认证、JWT、CSRF 与接口防护》
- 《从同源策略到 OAuth2:一文读懂 Web 安全的核心机制》
欢迎继续交流,一起打造更安全的 Web 应用!🚀