Flask框架 CSRF 保护实现方法详解
本文详解Flask框架中CSRF保护的实现方法。涵盖CsrfProtect模块的初始化、表单与无表单视图的保护配置、自定义错误处理及AJAX请求头设置。针对常见导入错误导致的Bad Request异常提供排查建议,帮助开发者全面保障Web应用安全。
本文详细解析Flask框架中CSRF(跨站请求伪造)保护的实现机制,旨在为开发者提供清晰的配置与使用指南。
Flask CSRF 保护
- 为什么需要 CSRF?
- 具体操作步骤
- 实现
- 后端书写
- 在表单添加保护
- 自定义错误响应和关闭保护
- ajax提交数据
- 故障排除
Flask-WTF 提供的表单保护机制能有效抵御 CSRF 威胁,通常无需额外干预。然而,对于不涉及标准表单提交的视图函数,仍需采取保护措施。
例如,通过 AJAX 发起的 POST 请求往往没有关联 HTML 表单。在 Flask-WTF 0.9.0 之前的版本中,无法直接获取 CSRF 令牌,因此需要手动实现 CSRF 保护机制。
基于 csrf_token 的校验原理,具体实施步骤如下: 1. 后端生成 csrf_token 值。当用户访问登录或注册页面时,后端将该值传递给前端,传递方式通常包括: - 在模板的 Form 表单中添加隐藏字段 - 通过 Cookie 将 csrf_token 传递给前端 2. 前端发起请求时,在表单数据或请求头中携带指定的 csrf_token。 3. 后端接收请求后,提取前端发送的 csrf_token,并与第 1 步生成的值进行比对。 4. 若两者一致,则视为合法请求;否则判定为伪造请求并拒绝通过。
在 Flask 中,CsrfProtect 类专门用于对指定应用实例进行 csrf_token 校验。开发者需完成以下操作: - 生成 csrf_token 的值 - 将该值传递给前端浏览器 - 确保前端请求时携带 csrf_token 值
后端书写
为使所有视图函数受到 CSRF 保护,需启用 CsrfProtect 模块:
from flask_wtf.csrf import CsrfProtect
CsrfProtect(app)
与其他 Flask 扩展类似,支持惰性加载方式:
from flask_wtf.csrf import CsrfProtect
csrf = CsrfProtect()
def create_app():
app = Flask(__name__)
csrf.init_app(app)
注意 必须为 CSRF 保护设置密钥。通常,该密钥与 Flask 应用的 SECRET_KEY 保持一致。
在表单添加保护
若模板中包含表单,无需额外操作,保持原有结构即可:
<form method="post" action="/">
{{ form.csrf_token }}
</form>
若模板中不存在表单,则需手动添加 CSRF 令牌:
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
自定义错误响应和关闭保护
当 CSRF 验证失败时,默认返回 400 错误响应。可自定义该错误响应:
@csrf.error_handler
def csrf_error(reason):
return render_template('csrf_error.html', reason=reason), 400
强烈建议对所有视图启用 CSRF 保护。同时,提供了无需保护的视图函数装饰器:
@csrf.exempt
@app.route('/foo', methods=('GET', 'POST'))
def my_handler():
# ...
return 'ok'
默认情况下,可通过将 WTF_CSRF_CHECK_DEFAULT 设置为 False 来禁用所有视图的 CSRF 保护。仅在需要时调用 csrf.protect()。这允许在检查 CSRF 令牌前执行预处理逻辑:
@app.before_request
def check_csrf():
if not is_oauth(request):
csrf.protect()
ajax提交数据
无需表单即可通过 AJAX 发送 POST 请求。该功能在 Flask 0.9.0 版本后可用。
- 若已使用 CsrfProtect(app),可通过 {{ csrf_token() }} 获取 CSRF 令牌。该方法适用于所有模板,无需担心无表单时如何渲染 CSRF 令牌字段。
推荐在 meta 标签中渲染 CSRF 令牌:
<meta name="csrf-token" content="{{ csrf_token() }}">
在 script 标签中渲染同样可行:
<script type="text/javascript">
var csrftoken = "{{ csrf_token() }}"
</script>
以下示例采用在 meta 标签渲染的方式。在 script 中渲染更为简便,此处不再赘述相关示例。
发送 AJAX POST 请求时,需添加 X-CSRFToken 头:
var csrftoken = $(‘meta[name=csrf-token]').attr(‘content')
KaTeX parse error: Expected '}', got 'EOF' at end of input: …|OPTIONS|TRACE)/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader(“X-CSRFToken”, csrftoken)
}
}
})
或者采用如下 AJAX 写法: 在提交请求时,需在请求头中添加 X-CSRFToken 的键值对
$.ajax({
...
headers: {
"X-CSRFToken": getCookie("csrf_token")
},
...
})
定义表单时,若错误地从 wtforms 导入 Form 而非从 flask.ext.wtf 导入,CSRF 保护的大部分功能仍可工作(除 form.validate_on_submit() 外),但 CSRF 保护会抛出异常。提交表单时将收到 Bad Request/CSRF token missing or incorrect 错误。此错误源于导入路径错误,而非配置问题。
希望本文所述内容能对基于 Flask 框架的 Python 程序设计有所帮助。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









