为啥 Selenium 会导致 CSRF 403?

Posted

技术标签:

【中文标题】为啥 Selenium 会导致 CSRF 403?【英文标题】:Why is Selenium causing a CSRF 403?为什么 Selenium 会导致 CSRF 403? 【发布时间】:2015-11-02 23:09:51 【问题描述】:

我正在尝试使用 Django 和 Selenium 创建一个简单的登录测试,但由于 CSRF 失败而得到 403。我期望中间件在 GET 请求中添加 cookie,然后在 POST 上将其解析回。

这是我目前检查过的内容:

1.对/accounts/login/ 的 GET 请求是否设置了 cookie?

是的,cookie 是在process_response 方法中设置的

2。 cookie 在 Selenium 驱动程序上可用吗?

是的

ipdb> self.selenium.get_cookies()
[u'domain': u'localhost', u'name': u'csrftoken', u'value': u'DzNbEn9kZw0WZQ4OsRLouriFN5MOIQos', u'expiry': 1470691410, u'path': u'/', u'httpOnly': False, u'secure': True]

3.在 POST 请求期间是否找到了 cookie?

不,来自django.middleware.CsrfViewMiddleware.process_view 的尝试/除外失败:

source

try:
    csrf_token = _sanitize_token(
        request.COOKIES[settings.CSRF_COOKIE_NAME])
    # Use same token next time
    request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
    csrf_token = None
    # Generate token and store it in the request, so it's
    # available to the view.
    request.META["CSRF_COOKIE"] = _get_new_csrf_key()

代码

class TestLogin(StaticLiveServerTestCase):

    @classmethod
    def setUpClass(cls):
        cls.selenium = getattr(webdriver, settings.SELENIUM_WEBDRIVER)()
        cls.selenium.maximize_window()
        cls.selenium.implicitly_wait(5)

        super(TestLogin, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        cls.selenium.quit()

        super(TestLogin, cls).tearDownClass()

    def test_login(self):

        self.selenium.get(''.format(self.live_server_url, '/accounts/login/?next=/'))
        assert "Django" in self.selenium.title
        un_el = self.selenium.find_element_by_id('id_username').send_keys('the_un')
        pw_el = self.selenium.find_element_by_id('id_password')
        pw_el.send_keys('the_pw')
        pw_el.send_keys(Keys.RETURN)

        try:
            WebDriverWait(self.selenium, 5).until(EC.title_contains("New Title"))
        except TimeoutException as e:
            msg = "Could not find 'New Title' in title. Current title: ".format(self.selenium.title)
            raise TimeoutException(msg)
        finally:
            self.selenium.quit()

问题

接下来我可以尝试什么来调试它?

【问题讨论】:

【参考方案1】:

老问题,但在纠结了几个小时后,答案很简单。

来自docs:

如果浏览器最初通过 HTTP 连接,这是默认的 大多数浏览器,现有的 cookie 都可能被泄露。为了 这个原因,你应该设置你的 SESSION_COOKIE_SECURE 和 CSRF_COOKIE_SECURE 设置为 True。这指示浏览器 仅通过 HTTPS 连接发送这些 cookie。请注意,这将 意味着会话将无法通过 HTTP 和 CSRF 保护 将阻止通过 HTTP 接受任何 POST 数据(这将是 如果您将所有 HTTP 流量重定向到 HTTPS,则很好)。

像我一样,您可能在大部分工作中使用django_extensions + Werkzeug,并且默认情况下通过 SSL 运行所有本地工作。

如果您使用的是unittest 或它的 Django 版本,我建议您在测试运行时修改这些设置,如下所示:

...
from django.conf import settings

class ProfilePagetest(LiveServerTestCase):

    def setUp(self):

        settings.CSRF_COOKIE_SECURE = False
        settings.SESSION_COOKIE_SECURE = False

        self.url = reverse('clientpage:profile')
        self.username = 'name@names.com'
        self.password = 'strange decisions...'
        get_user_model().objects.create_user(self.username, self.username, self.password)
        self.browser = webdriver.Firefox()

这应该会停止 CSRF 验证问题。

【讨论】:

我会使用 djangos override_settings 装饰器来确保在测试执行后重置设置。 docs.djangoproject.com/en/1.9/topics/testing/tools/…

以上是关于为啥 Selenium 会导致 CSRF 403?的主要内容,如果未能解决你的问题,请参考以下文章

由于 CSRF 故障(Django 1.2.3)导致间歇性 403

带有 django 的 JQuery AJAX 获取 csrf 错误 403

Django的POST请求时因为开启防止csrf,报403错误,及四种解决方法

Spring CSRF:即使在设置请求标头后,Ajax 也会出现 403 错误

即使禁用 csrf,Spring security 403 禁止错误也会不断发生

Django:AJAX + CSRF POST 给出 403