【问题标题】:django and python requests - getting a 403 on a post requestdjango 和 python 请求 - 在发布请求中获得 403
【发布时间】:2016-07-21 19:24:35
【问题描述】:

我正在使用requests 登录到我的 Django 站点进行测试(是的,我知道 Django TestClient,但我在这里需要纯 http)。我可以登录,只要我发出 get 请求,一切正常。

当我尝试改用 post 时,我从 csrf 中间件收到 403。在我看来,我现在已经通过使用@crsf_exempt 来解决这个问题,但我更喜欢长期解决方案。

这是我的代码:

with requests.Session() as ses:

    try:

        data = {
            'username': self.username,
            'password': self.password,
        }

        ses.get(login_url)
        try:
            csrftoken = ses.cookies["csrftoken"]
        except Exception, e:
            raise
        data.update(csrfmiddlewaretoken=csrftoken)

        _login_response = ses.post(login_url, data=data)

        logger.info("ses.cookies:%s" % (ses.cookies))

        assert 200 <= _login_response.status_code < 300, "_login_response.status_code:%s" % (_login_response.status_code)

        response = ses.post(
            full_url,
            data=data,
            )

        return self._process_response(response)

登录正常,我可以在这里看到 csrf 令牌。

 INFO:tests.helper_fetch:ses.cookies:<RequestsCookieJar[<Cookie csrftoken=TmM97gnNHs4YCgQPzfNztrAWY3KcysAg for localhost.local/>, <Cookie sessionid=kj6wfmta

但是,中间件将 cookie 视为空的。

INFO:django.middleware.csrf:request.COOKIES:{}

我已经添加了日志记录代码:

def process_view(self, request, callback, callback_args, callback_kwargs):

    if getattr(request, 'csrf_processing_done', False):
        return None

    try:
        csrf_token = _sanitize_token(
            request.COOKIES[settings.CSRF_COOKIE_NAME])
        # Use same token next time
        request.META['CSRF_COOKIE'] = csrf_token
    except KeyError:
        # import pdb
        # pdb.set_trace()
        import logging
        logger = logging.getLogger(__name__)
        logger.info("request.COOKIES:%s" % (request.COOKIES))

我在调用请求的 session.post 的方式上是否遗漏了什么?我尝试在其中添加cookie,没有任何区别。但是我完全可以理解为什么 crsf 中间件会出现问题。我认为 cookie 是会话的一部分,那么为什么在我的第二篇文章中它们不见了?

            response = ses.post(
                self.res.full_url,
                data=data,
                cookies=ses.cookies,
                )

How to send cookies in a post request with the Python Requests library? 启发的这种变化也没有导致任何东西被传递给 csrf 中间件:

            response = ses.post(
                self.res.full_url,
                data=data,
                cookies=dict(csrftoken=csrftoken),
                )

【问题讨论】:

    标签: django python-requests django-csrf


    【解决方案1】:

    对于登录后的后续请求,请尝试将其作为标头 X-CSRFToken 提供。

    以下内容对我有用:

    with requests.Session() as sesssion:
        response = session.get(login_url)
        response.raise_for_status()  # raises HTTPError if: 400 <= status_code < 600
    
        csrf = session.cookies['csrftoken']
        data = {
            'username': self.username,
            'password': self.password,
            'csrfmiddlewaretoken': csrf
        }
    
    
        response = session.post(login_url, data=data)
        response.raise_for_status()
    
        headers = {'X-CSRFToken': csrf, 'Referer': url}
        response = session.post('another_url', data={}, headers=headers)
        response.raise_for_status()
    
        return response  # At this point we probably made it
    

    文档参考:https://docs.djangoproject.com/en/dev/ref/csrf/#csrf-ajax

    【讨论】:

    • 感谢您的帮助。让它工作正常,我不得不做一些更改,这需要很长时间才能弄清楚,因为 session.post(login_url, data=data) 之后的 csrf 令牌 changes。更改后的令牌需要进入X-CSRFToken。但它看起来相当不错,而且 X-CSRFToken 与我为 JS Ajax 帖子所做的相匹配。我要做的是在本周末写下我的答案 - 使用清理后的代码 - 并接受你的答案,为我指明正确的方向。暂时保持开放,因为这不是一个便宜的赏金,有人可能会有另一个很好的答案。保重。
    • 如果我在星期一之前还没有接受它,给我一个嗡嗡声。我假设如果我超过赏金期限,SO 仍会奖励你 100 分,但如果不是这种情况,我不想让你错过赏金。
    • 不用担心赏金!我刚刚回答了这个问题,因为我恰好在不久前解决了这个问题。您是对的,在登录后 csrf 令牌更改,但如果您使用会话,它会为您透明地处理。如需完整的解决方案,请按照您的操作方式随时查看我在 github 上的库(进行访问控制检查):github.com/stphivos/fnval/blob/master/fnval/net.py#L41
    • 赏金是不可退还的,所以我已经付了钱。还不如奖励它。是的,也许会话应该处理它,但它没有。或者至少似乎没有。另外,我看到人们在其他地方评论 S​​O 有时requests 的会话可以让 cookie 掉入裂缝。有趣的是,有时编写 30 行代码(有效)会花费你几天的时间;-)
    • 好主意,fnval。我正在做一些可能与它有关的事情。 django 测试用例针对 TestClient 运行,使用一些数据触发 URL,然后您可以解析响应。但它们针对本地测试客户端工作,尽管实际上大部分工作是定义测试参数和 unittest 断言以针对响应运行。我的代码重用了所有这些,但使用requests 针对任意主机名+端口将其触发。就我而言,我最想抓的是我需要测试一个基于 VM 的设备。我在每次 url 调用时都登录 @,所以会话没什么大不了的。
    【解决方案2】:

    您也可以尝试在视图上使用this decorator,而不是csrf_exempt。我试图重现您的问题,这对我也很有效。

    from django.views.decorators.csrf import ensure_csrf_cookie`
    
    @ensure_csrf_cookie
    def your_login_view(request):
        # your view code
    

    【讨论】:

    • ensure_csrf_cookie 的工作是确保一个 csrf 令牌出去,即使页面上没有表单。我没有这个问题 - 我的文章特别说我可以看到 csrf_token,我只是不知道如何将它传递回实际目标(而不是登录)POST。
    • 好的,感谢您的评论,这里最重要的是您找到适合您的解决方案 ;),并且我们想要帮助实现这一点,请牢记这一点,这是我的目标;)。再次感谢,祝你好运!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-01
    • 2023-03-14
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 2019-10-29
    相关资源
    最近更新 更多