Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Selenium causing a CSRF 403?

I'm trying to create a simple login test using Django and Selenium, but getting a 403 due to a CSRF failure. I'm expecting the middleware to add the cookie on the GET request and then parse it back out on the POST.

Here's what I've checked so far:

1. Is the cookie being set on the GET request to /accounts/login/?

Yes, the cookie is being set in the process_response method

2. Is the cookie available on the Selenium driver?

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. Is the cookie found during the POST request?

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

Code

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

Question

What can I try next to debug this?

like image 473
Brian Dant Avatar asked Aug 10 '15 21:08

Brian Dant


People also ask

What does CSRF verification failed mean?

This error message means that your browser couldn't create a secure cookie, or couldn't access that cookie to authorize your login. This can be caused by ad- or script-blocking plugins, but also by the browser itself if it's not allowed to set cookies.

How do I enable CSRF cookies in Chrome?

Chrome. Open Chrome Settings. In the Privacy and security section, click Cookies and other site data. Scroll down to Sites that can always use cookies and click Add.

How do I use CSRF token in Postman?

The CSRF token generated automatically by spring security when you logged in. It will be shown at the response header. The CSRF token can be used on subsequent request by setting X-CSRF-TOKEN with CSRF token on header.


1 Answers

Oldish question, but after getting stuck with this for a few hours the answer was simple.

From the 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).

Like me, you are probably using django_extensions + Werkzeug for the majority of your work, and are by default running all of your local work over SSL.

If you're using unittest or Djangos version of it, I'd recommend that you modify these settings at test runtime, like so:

...
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 = '[email protected]'
        self.password = 'strange decisions...'
        get_user_model().objects.create_user(self.username, self.username, self.password)
        self.browser = webdriver.Firefox()

This should stop the CSRF validation issues.

like image 136
Jamie S Avatar answered Nov 11 '22 11:11

Jamie S