From 90b8078739c8166d26de723178fb2151043e8218 Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Tue, 8 Feb 2011 22:29:40 -0800 Subject: [PATCH] BACKWARDS-INCOMPATIBLE: Fix XSRF security vulnerability. This is a backwards-incompatible change. Applications that previously relied on a blanket exception for XMLHTTPRequest may need to be modified to explicitly include the XSRF token when making ajax requests. The tornado chat demo application demonstrates one way of adding this token (specifically the function postJSON in demos/chat/static/chat.js). More information about this change and its justification can be found at http://www.djangoproject.com/weblog/2011/feb/08/security/ http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails Closes #214. --- tornado/web.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/tornado/web.py b/tornado/web.py index ee9cbd32d4..71603342da 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -709,16 +709,27 @@ def xsrf_token(self): def check_xsrf_cookie(self): """Verifies that the '_xsrf' cookie matches the '_xsrf' argument. - To prevent cross-site request forgery, we set an '_xsrf' cookie - and include the same '_xsrf' value as an argument with all POST - requests. If the two do not match, we reject the form submission - as a potential forgery. + To prevent cross-site request forgery, we set an '_xsrf' + cookie and include the same value as a non-cookie + field with all POST requests. If the two do not match, we + reject the form submission as a potential forgery. + + The _xsrf value may be set as either a form field named _xsrf + or in a custom HTTP header named X-XSRFToken or X-CSRFToken + (the latter is accepted for compatibility with Django). See http://en.wikipedia.org/wiki/Cross-site_request_forgery + + Prior to release 1.1.1, this check was ignored if the HTTP header + "X-Requested-With: XMLHTTPRequest" was present. This exception + has been shown to be insecure and has been removed. For more + information please see + http://www.djangoproject.com/weblog/2011/feb/08/security/ + http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails """ - if self.request.headers.get("X-Requested-With") == "XMLHttpRequest": - return - token = self.get_argument("_xsrf", None) + token = (self.get_argument("_xsrf", None) or + self.request.headers.get("X-Xsrftoken") or + self.request.headers.get("X-Csrftoken")) if not token: raise HTTPError(403, "'_xsrf' argument missing from POST") if self.xsrf_token != token: