如下面的代码所示,我有一个用于注册的
GET
,它将其工作委托给 POST
。
class RegistrationHandler(tornado.web.RequestHandler):
def get(self):
s = """
<h1>Register</h1>
<form method="post" action="/register">
<div>
<label>User</label>
<input name="user_name" value="[email protected]"/>
</div>
<div>
<label>password</label>
<input name="password" type="password"/>
</div>
<div>
<input type="submit" value="submit"/>
</div>
</form>
"""
self.write(s)
@log_exception()
def post(self):
user_name = self.request.arguments['user_name']
password = self.request.arguments['password']
log.debug('Registering user with credentials %r' % (user_name, password))
with sa_session() as db_session:
User.register(user_name, password, db_session)
当我从网络浏览器访问该 URL 时,我会收到一份注册表单,提交后我会收到“403:禁止”。
控制台日志:
2012-10-15 11:27:42,482 - __main__ - DEBUG - Starting server on port 8080
2012-10-15 11:27:49,377 - root - INFO - 304 GET /register (127.0.0.1) 0.78ms
2012-10-15 11:27:53,143 - root - WARNING - 403 POST /register (127.0.0.1): '_xsrf' argument missing from POST
2012-10-15 11:27:53,144 - root - WARNING - 403 POST /register (127.0.0.1) 1.05ms
这个错误是什么意思以及如何纠正它?谢谢。
我想您在设置中启用了跨站点请求伪造 cookie (默认情况下它是打开的)。
要解决此问题,请在设置中将其关闭:
settings = {
"xsrf_cookies": False,
}
注意:通常您不想关闭此功能,并且通常您会在如下模板中生成 HTML:请注意添加 XSRF cookie 的 xsrf 位。
<form method="post" action="/register">
<input name="user_name" value="[email protected]"/>
<input name="password" type="password"/>
<input type="submit" value="submit"/>
{% raw xsrf_form_html() %}
</form>
---编辑以下评论--- 而不是:
def get(self):
loader = template.Loader("resources")
page_contents = loader.load('register_page.html').generate()
self.write(page_contents)
做:
def get(self):
self.render("../resources/register_page.html")
或更好:
def get(self):
self.render("register_page.html")
(并将其放入模板目录中)
这里同样的问题。 在深入研究代码一段时间后,我检查了龙卷风内置函数,该函数进行了检查:
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 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
.. versionchanged:: 3.2.2
Added support for cookie version 2. Both versions 1 and 2 are
supported.
"""
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")
_, token, _ = self._decode_xsrf_token(token)
_, expected_token, _ = self._get_raw_xsrf_token()
if not token:
raise HTTPError(403, "'_xsrf' argument has invalid format")
if not _time_independent_equals(utf8(token), utf8(expected_token)):
raise HTTPError(403, "XSRF cookie does not match POST argument")
正如您可以阅读方法定义:
值可以设置为名为_xsrf
的表单字段 或者在名为_xsrf
或X-XSRFToken
的自定义 HTTP 标头中X-CSRFToken
因此我尝试设置一个名为
X-CSRFToken
的 HTTP 标头。以下是ajax定义:
$.ajax({
url: this.my_url,
type: 'POST',
data: JSON.stringify(body),
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', getCookie('_xsrf'));},
});
最后,我们需要定义函数
getCookie
,它负责检索正确的 CSRF 令牌。该函数的定义直接取自 Tornado CSRF 文档:
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
有冲突: 你如何制作
get
以及你放置 method="post"
的形式?这就是为什么有error 403
如果您使用
get
,那么您将不需要 xsrf
保护。否则,您可以在表单声明之后将其添加为
<form method="post" action="/register">
{% raw xsrf_form_html() %} # the 'raw' word is to force escape to be desactivated (it is by default activated)
因此,您会发现 xsrf 在渲染的 html 中是一个隐藏标签。
我通过更改 HTML 文件中的表单方法解决了这个问题:
对于