Which CSRF token validation is correct?
See original GitHub issueWe migrated from .NET Framework to .NET 6, but still use AngularJS with Razor pages and cookie authentication. Our login page is a plain Razor page that is provided by our AccountController. We noticed that when we use the AbpAutoValidateAntiforgeryTokenAttribute as filter for our controllers, that it does not work on the login page if you delete the CSRF cookies, which also allows for CSRF attacks if you login from a different domain that contains a login form like this one:
<html>
<body>
<form action="https://localhost:443322/Account/Login?returnUrl=" method="POST">
<input type="hidden" name="tenancyName" value="tenancyName" />
<input type="hidden" name="usernameOrEmailAddress" value="username" />
<input type="hidden" name="password" value="password" />
<input type="hidden" name="returnUrlHash" value="" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
If we use the AutoValidateAntiforgeryTokenAttribute from .NET, as a replacement for the AutoValidateAntiforgeryTokenAttribute from ABP, it all works properly.
Based on the documentation, we only need the .NET attribute, yet in the MVC Startup class, I see this:
//MVC
services.AddControllersWithViews(
options => { options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute()); }
)...
If I use both, I am able to skip CSRF validation. What is the correct implementation?
Info:
- Your Abp package version. 7.3.0
- Your base framework: .NET 6.
Issue Analytics
- State:
- Created 3 months ago
- Comments:12 (12 by maintainers)
Top GitHub Comments
AbpAutoValidateAntiforgeryTokenAttribute should be used. I think we should update the documentation.
@ismcagdas, no problem of course. Good communication is essential. 😛
Non-browser clients presumption
In the current implementation of the AbpAutoValidateAntiforgeryTokenAuthorizationFilter, we skip validation if no CSRF token cookie is sent in the request, because we expect this situation to only occur with non-browser clients.
The problem with this presumption is that it is not correct for all situations. If we go to a malicious site with a new browser session, meaning the browser was fully closed and is reopened, and we send a request from the malicious site to a public API endpoint, we do not send the cookie because it does not exist yet until the user has visited the web app. This would make CSRF attacks, such as Login CSRF attacks, possible because we skip validation if the cookie does not exist yet.
An issue with storing the token in a session cookie
If we visit the web app, the CSRF token cookie will be created. If the user leaves the web app, but keeps their browser open, the cookie will stay alive since the browser session has not ended yet. If we now visit a malicious site and they execute a request to our web app from their domain, it means that the cookie will be sent in the request from the malicious domain, for example utilizing a hidden form that is submitted from JavaScript or a Fetch API request with credentials included, which would also make CSRF attacks possible because the validation would pass. This would also work with protected endpoints if cookie based authentication is used and the user logged into the app in this browser session.
My question
The things I describe above have let me to believe that CSRF is not working properly.