With anonymous cookies
If you are happy to generate secure tokens which are set as anonymous users' cookies, but not to store them server side then you could simply double submit cookies.
e.g. Legitimate user:
- Anon user navigates to the login page, receives cookie which is sent to the browser.
- Anon user logs in and the browser sends the cookie as a header and as a hidden form value.
- User now logged in.
This cannot be abused by the attacker as the following will now happen:
- The attacker creates a host account on the trusted domain
- The attacker forges a login request in the victim's browser with this host account's credentials However, the attacker does not have access to the victim's cookie value and cannot forge it as the CSRF token in the request body. The attack fails.
Even if your site is only accessible over HTTPS and you correctly set the Secure Flag, care must be taken with this approach as an attacker could potentially MiTM any connection from the victim to any HTTP website (if the attacker is suitably placed of course), redirect them to your domain over HTTP, which is also MiTM'd and then set the required cookie value. This would be a Session Fixation attack. To guard against this you could output the cookie value to the header and the hidden form field every time this (login) page is loaded (over HTTPS) rather than reuse any already set cookie value. This is because although a browser can set the Secure Flag, it will still send cookies without the Secure Flag over a HTTPS connection, and the server will not be able to tell whether the Secure Flag was set. (Cookie attributes such as the Secure Flag are only visible when the cookie is set, not when it is read. The only thing the server gets to see is the cookie name and value.) Implementing HSTS would be a good option for protection in supported browsers.
It is advisable to set X-Frame-Options to prevent a UI redress click jacking attack (otherwise the attacker could possibly use site functionality to pre fill their username and password awaiting the user to click and submit them along with the CSRF value).
Without anonymous cookies
If you do not want to set cookies for anonymous users (which then may suspect that they are being tracked server side) then the following approach may be used instead: A multi-stage login form.
The first stage is the usual username / password combination.
After the form is submitted, it redirects to another form. This form is protected by a special intermediary authentication token cookie and a CSRF token. The authentication here will only allow the second stage authentication to be submitted, but will not allow any other actions on the account (except possibly a full logout). This will enable the CSRF token to be associated and used by this user account only on this intermediary session.
Now it is only when this form is submitted, including the token cookie and CSRF hidden form value that the user is fully authenticated with the domain. Any attacker attempting a CSRF attack will not be able to retrieve the CSRF token and their full login attempt will fail.
The only drawback is that the user will have to manually click to complete login, which may be a clunky user experience. It is advisable to set X-Frame-Options to prevent this being used in combination with a UI redress click jacking attack. Any auto submission with JavaScript would be beneficial to the attacker and would cause their attack to possibly succeed, so at the moment I can only see a manual click by the user working.
It would now play out like this:
- The attacker creates a host account on the trusted domain
- The attacker forges a login request in the victim's browser with this host account's credentials but they cannot proceed past stage two to become fully authenticated
- The attacker tricks the victim into using the trusted site - but as they are not fully authenticated, the site will act as though the user is unauthenticated