1
votes

I've been reading a lot about CSRF protection, however I have a doubt that I haven't been able to clarify, not even here in stackoverflow.

So, i'm using flask to build a web-app, and there is a function named csrf_token that you can call to generate a token and put it into a form (in this case the login form) as a hidden input. However, i was thinking, if an attacker enters the login site to get his csrf token, doesn't this kill the csrf protection on the site? because then he can attach his csrf token to an ajax request and this would be equal to having no protection at all basically.

2
Although the question has been answered already, thank you as well, the thing is that what i didn´t fully comprehend was the behavior of the CSRF token, not the CSRF methodology itself. - Cesar Cuevas

2 Answers

3
votes

You misunderstand how CSRF works.

No, an attacker can't just take any CSRF token. The CSRF token sent to different users differs. Taking an existing CSRF token then using that to try to trick a different user with that different token is going to fail because the token will not match what was already given to the browser the user is accessing the form with.

Not only is the token going to differ, the point of a CSRF token is that it is given to a user twice, in ways where an attacker can't access both values. The CSRF token is stored both in a cookie (preferably with HttpOnly set to True, so it is not even accessible to code running in the browser) and it is embedded in the form. When the form is submitted, the token posted with the form must match the token in the cookie.

So not only can't the attacker use an earlier CSRF token (which would differ already from what the user was given), the attacker also can't know what cookie the browser would have sent the user, nor can they ever access that cookie even if HttpOnly was not set, because an attacker on domain A can't read cookies for domain B.

Presumably you are using the Flask-WTF CSRF protection here (given the function name). This package stores CSRF tokens in the Flask session, and cryptographically signs tokens and attaches timeout, so an attacker can't replace the session with another that contains their own token, either. In addition, if your users are accessing the site over HTTPS, the HTTP Referer header must match the hostname that the client accessed.

As for the question if login forms, specifically, need to be protected with CSRF: Yes, because if an attacker can choose what login is used by the victim, they can then get access to whatever else the victim has entered into the site. By protecting your login form from CSRF attacks, you protect your users from this scenario.


Fun fact, I reported this issue to then-still-very-new Mozilla project in May 2000 to discuss how browsers could help to mitigate what is now known as CSRF, after having identified the issue on the Open Source Zope platform. Boy, that makes me feel old now.

1
votes

Here's my answer to the above question with a different perspective.

I'm going to answer it in two parts, both of which might seem a bit contradictory in the beginning. But I insist that you read it till the end.

PART 1: You don't have to explicitly do anything to protect your login page against CSRF, as the control is already present by default.

Let's revisit the basics for a second. CSRF demands a random token (usually known as the 'Anti-CSRF token') to be present in the request body whose sole purpose is to make the request unique, right?

Well, there is already such a token present in the login request by default (though it was never meant for that purpose, but it's there anyway). That token is the USER PASSWORD.

Password makes the login request unique. For an attacker to craft a forged login request and do whatever he intends to do with it (though logically I can't think of anything that he would accomplish by forging the login request), he must know the target user's password, right? That's highly unlikely. If he knew the password, he won't do CSRF, he would straight-up log in with it himself.

PART 2: However, recently I encountered an issue while performing a security assessment of a web app. There was a hidden random token in the login POST request, but apparently its purpose was not to mitigate CSRF but to stop password brute-forcing automation. It was their workaround for not having a temporary account lockout / CAPTCHA.

I wrote a small Python script to overcome that situation and brute-force the password anyway.

(Here's the GitHub link to my script in case you wish to try. Demo videos are included in the README.md file.)


Bottom-line:

  1. You don't need any additional CSRF protection for your login page.
  2. But make sure that you have a good deterrent to stop password brute-forcing automation, such as a temporary account lockout or a CAPTCHA.

Hope that helps. Please feel free to correct me if I'm wrong. Thanks.