为什么 Selenium 会导致 CSRF 403?

Why is Selenium causing a CSRF 403?

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

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

1. cookie 是否在 /accounts/login/ 的 GET 请求中设置?

Yes, the cookie is being set in the process_response method

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

Yes

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?

No, this try/except from django.middleware.CsrfViewMiddleware.process_view fails:

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

问题

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

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

来自docs:

If a browser connects initially via HTTP, which is the default for most browsers, it is possible for existing cookies to be leaked. For this reason, you should set your SESSION_COOKIE_SECURE and CSRF_COOKIE_SECURE settings to True. This instructs the browser to only send these cookies over HTTPS connections. Note that this will mean that sessions will not work over HTTP, and the CSRF protection will prevent any POST data being accepted over HTTP (which will be fine if you are redirecting all HTTP traffic to HTTPS).

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

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

...
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 验证问题。