在Flask JWT Extended中将csrf_access_token存入Cookie为何被认为安全?
你的核心疑问在于:恶意网站发起跨域POST请求时,浏览器会自动携带目标站点的Cookie,那存Cookie里的csrf_access_token岂不是会被攻击者利用? 其实这是对同源策略和CSRF防护逻辑的误解,下面拆解关键原理:
1. 浏览器同源策略禁止跨域读取Cookie
浏览器的同源策略(Same-Origin Policy)会严格限制不同域名下的JavaScript代码访问其他域的Cookie。也就是说:
- 你的网站是
your-domain.com,恶意网站是evil.com,evil.com的JS代码无法通过document.cookie读取your-domain.com下的任何Cookie(包括access_token和csrf_access_token)。 - 只有同源页面(比如你自己的
/logoutpage)才能读取这些Cookie,这也是你能在登录成功后,把csrf_access_token从Cookie取出来放到表单隐藏字段里的原因。
2. CSRF攻击的核心是「利用浏览器自动带Cookie,但拿不到令牌」
CSRF攻击的本质是:攻击者诱导用户在已登录你的网站的情况下,访问恶意网站,恶意网站通过表单/JS发起对你接口的请求,此时浏览器会自动携带你的网站的Cookie(这是浏览器的默认行为,和同源策略不冲突)。但关键问题是:
- 攻击者无法获取
csrf_access_token的具体值,因为跨域JS读不到Cookie。 - 你的后端接口(比如
/logout)会验证两个东西:请求中是否携带了正确的csrf_token参数(比如表单里的隐藏字段),以及这个参数值是否和Cookie里的csrf_access_token一致。 - 攻击者没法在恶意表单里填上正确的
csrf_token值,所以后端会拒绝这个请求,攻击失败。
3. 结合你的代码理解
看你提供的代码:
@app.route("/login", methods=["POST"]) def login(): username = request.form.get("username", None) password = request.form.get("password", None) response = jsonify({"msg": "login successful"}) access_token = create_access_token(identity="example_user") set_access_cookies(response, access_token) return response @app.route("/logoutpage", methods=["GET"]) @jwt_required() def logout_page(): logout = """<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>LOGOUT</title> </head> <body> <h1>LOGOUT</h1> <form action="/logout" method="post"> <input type="hidden" name="csrf_token" value="{{csrfval}}"> <input type="submit" value="Logout"> </form> </body> </html>""" return render_template_string(logout, csrfval=request.cookies.get("csrf_access_token"))
- 登录时,
set_access_cookies会把access_token和csrf_access_token都存在your-domain.com的Cookie里,且这些Cookie默认是HttpOnly(防止XSS攻击读取)、SameSite=Lax/Strict(进一步限制跨域携带)。 - 访问
/logoutpage时,你的后端可以读取同源的Cookie,把csrf_access_token的值填充到表单的隐藏字段里。用户提交表单时,这个值会作为csrf_token参数发送给后端,后端验证它和Cookie里的csrf_access_token匹配,才会执行登出操作。 - 恶意网站的表单根本没法拿到这个
csrfval的值,所以提交的请求会因为缺少正确的csrf_token参数被拒绝。
总结
把csrf_access_token存Cookie的核心安全逻辑是:
- 同源页面可以读取并使用这个令牌,合法请求能通过验证。
- 跨域攻击者无法读取令牌,无法构造出符合要求的请求,从而阻断CSRF攻击。
内容的提问来源于stack exchange,提问作者MW_DEVEL
相关产品推荐
相关产品推荐

