CSRF Protection¶
This part of the documentation covers the CSRF protection.
Why CSRF¶
Flask-WTF form is already protecting you from CSRF, you don’t have to worry about that. However, you have views that contain no forms, and they still need protection.
For example, the POST request is sent by AJAX, but it has no form behind it. You can’t get the csrf token prior 0.9.0 of Flask-WTF. That’s why we created this CSRF for you.
Implementation¶
To enable CSRF protection for all your view handlers, you need to enable
the CsrfProtect
module:
from flask_wtf.csrf import CsrfProtect
CsrfProtect(app)
Like any other Flask extensions, you can load it lazily:
from flask_wtf.csrf import CsrfProtect
csrf = CsrfProtect()
def create_app():
app = Flask(__name__)
csrf.init_app(app)
Note
You need to setup a secret key for CSRF protection. Usually, this is the same as your Flask app SECRET_KEY.
If the template has a form, you don’t need to do any thing. It is the same as before:
<form method="post" action="/">
{{ form.csrf_token }}
</form>
But if the template has no forms, you still need a csrf token:
<form method="post" action="/">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
</form>
Whenever a CSRF validation fails, it will return a 400 response. You can customize the error response:
@csrf.error_handler
def csrf_error(reason):
return render_template('csrf_error.html', reason=reason), 400
We strongly suggest that you protect all your views from CSRF. But there is a chance that you might exclude some view handlers, it can be done:
@csrf.exempt
@app.route('/foo', methods=('GET', 'POST'))
def my_handler():
# ...
return 'ok'
You can also disable CSRF protection in all views by default, by setting
WTF_CSRF_CHECK_DEFAULT
to False
, and selectively call
csrf.protect()
only when you need. This also enables you to do some
pre-processing on the requests before checking for the CSRF token:
@app.before_request
def check_csrf():
if not is_oauth(request):
csrf.protect()
AJAX¶
Sending POST requests via AJAX is possible where there is no forms at all. This feature is only available since 0.9.0.
Assumimg you have done CsrfProtect(app)
, you can get the csrf token via
{{ csrf_token() }}
. This method is available in every template, that
way you don’t have to worry if there are no forms for rendering the csrf token
field.
The suggested way is that you render the token in a <meta>
tag:
<meta name="csrf-token" content="{{ csrf_token() }}">
And it is also possible to render it in the <script>
tag:
<script type="text/javascript">
var csrftoken = "{{ csrf_token() }}"
</script>
We will take the <meta>
way for example, the <script>
way is far
more easier, you don’t have to worry if there is no example for it.
Whenever you send a AJAX POST request, add the X-CSRFToken
for it:
var csrftoken = $('meta[name=csrf-token]').attr('content')
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken)
}
}
})
Troubleshooting¶
When you define your forms, if you make the mistake of importing
Form
from wtforms
instead of from flask.ext.wtf
, most
features besides CSRF protection will work (aside from
form.validate_on_submit()
), but CSRF protection will fail. Upon
submitting forms, you’ll get
Bad Request
/CSRF token missing or incorrect
(and the
form.csrf_token
in your template will produce no output). The
problem is in your broken import statements, not your configuration.