I have a .NET API and React SPA hosted on the same domain (api.domain.com and app.domain.com).
I am using same-site=strict, secure and httponly cookies for communication from the SPA to the API. I also have strict CORS rules setup. I also want to add anti forgery tokens, but can't find any good solutions for this. So what im thinking of doing is:
- once user is logged in, call endpoint /api/csrf to get a anti-csrf token. This will be placed in local storage.
- on every request, attach it as a header
- when a request is received, using a middleware: check if the user is authenticated with a cookie (I also have to support daemon apps calling the API with bearer tokens, in these cases, skip the CSRF check), and check if the token is valid. Return 400/401 if it is not.
I'm using OpendIdConnect to issue out the cookies using auth code flow against AD FS. Does the MS OIDC lib have any way to issue out anti-forgery tokens as well? If not, can I just create tokens and add them to an in-memory cache, and only allow cookie requests if the token exists in the cache? Or is this too "loose" CSRF check?
Firstly, if you have
same-site=strict, your website is already CSRF protected for users using browsers supporting thesame-siteattribute. Thus, for them, the CSRF token is irrelevant. You only need CSRF tokens for the users who are using browsers from before thesame-siteattribute. So my following comments are in the light of these "outdated" browsers.Comments on your plan:
Storing CSRF tokens locally (I assume in cookies) would not solve the issue. This is because browsers would automatically attach the cookie with any requests, which would defeat the purpose of using these tokens.
Using an API to generate CSRF token is an overkill from the browser's perspective. CSRF tokens are to authenticate actions generated from your own website. So, in theory, what one should be doing is adding unique tokens to each and every action for each page on their website. This can easily be done when the pages are generated on the server side, which alleviates the need for additional communication from the browser.
It was unclear how many tokens you would generate in a single go. However, reusing the same token for different possible actions is a bad idea as it reduces the attack complexity.
Hope this was helpful!