【问题标题】:Tornado - '_xsrf' argument missing from POSTTornado - POST 中缺少“_xsrf”参数
【发布时间】:2012-10-15 06:03:21
【问题描述】:

从下面的代码中可以看出,我有一个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="test@test.com"/>
                  </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

这个错误是什么意思,我该如何纠正?谢谢。

【问题讨论】:

    标签: python python-3.x tornado


    【解决方案1】:

    我想您在设置中启用了跨站点请求伪造 cookie (默认开启)。

    Tornado's XSRF is here

    要解决此问题,请在您的设置中将其关闭:

    settings = {
        "xsrf_cookies": False,
    }
    

    注意:通常你不想关闭它,通常你会在这样的模板中生成 HTML:请注意添加 XSRF cookie 的 xsrf 位。

     <form method="post" action="/register">
         <input name="user_name" value="test@test.com"/>
         <input name="password" type="password"/>
         <input type="submit" value="submit"/>
    {% raw xsrf_form_html() %}
     </form>
    

    ---编辑以下cmets--- 而不是:

      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")
    

    (并将其放在您的模板目录中)

    【讨论】:

    • 它会引发一个错误,即名称 xsrf_form_html() 未定义。我需要一些进口吗?如果是,如何将其添加到我的模板中?
    • 您不需要任何特殊的导入。请注意,xsrf_form_html() 仅适用于模板。您在上面发布的代码不是模板。
    • 这是我的 Python 代码 - refheap.com/paste/5955,这是我的模板 - refheap.com/paste/5956
    • 错误信息为NameError: global name 'xsrf_form_html' is not defined
    【解决方案2】:

    同样的问题。 在深入研究代码后,我检查了执行该检查的 tornado 内置函数:

    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-XSRFTokenX-CSRFToken 的自定义 HTTP 标头中

    因此我尝试设置一个名为 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-token。这个函数的定义直接取自TornadoCSRF documentation

    function getCookie(name) {
        var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
        return r ? r[1] : undefined;
    }
    

    【讨论】:

      【解决方案3】:

      有冲突: 您如何制作 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 中发现它是一个隐藏标签。

      【讨论】:

        猜你喜欢
        • 2018-01-17
        • 1970-01-01
        • 2019-09-04
        • 1970-01-01
        • 2018-04-23
        • 1970-01-01
        • 2012-09-21
        • 2019-04-04
        • 2011-08-27
        相关资源
        最近更新 更多