Search code examples
httpauthenticationcookiescsrfprogressive-enhancement

Ways to prevent CSRF and have good cookie etiquette on a no-JavaScript login page?


Context: I'm building a website that has a password-locked admin portal. Additionally, JS is only used for progressive enhancement of the 100% already-working site.

Security issue: The login form is vulnerable to CSRF.

My fix: A hidden form token and a pre-session cookie. This is a typical CSRF defense, and works. The pre-session cookie gets set when a user clicks "I Agree" to cookie consent. Remember, this uses no JS, so it happens by means of a POST request.

Why that was dumb: The cookie consent page POST request is vulnerable to CSRF.

Yes—it's literally nothing compared to a login CSRF.

But my integrity demands that I fix this. I truly believe that cookie consent should not be forgable.

After two days, I see two solutions:

  • Give up and use JavaScript. Just set the pre-session cookie on client side. Good UX and but now JavaScript is a hard requirement.
  • Have a 2nd cookie consent page, which just confirms the first one. Bad UX, but this 2nd page would be immune to CSRF. EDIT: I realized this idea wouldn't work at all, because I would need to have a cookie set to prevent the CSRF to begin with. So, any and all ideas are welcome.

Does anyone have other solutions? Thanks in advance.


Solution

  • In discussion with a friend, we came up with the following.

    Use the Referer header for CSRF protection, instead of a pre-session cookie. It comes down to checking that the Referer is from the expected domain. If not, the login attempt should fail.

    From a UX perspective, this is great. The "Log In" button can now double as the "Accept Cookies" button, so there's only one step.

    Some sources say the Referer header is a weaker defense, but it seems like the least troublesome option here.