Your understanding is correct.
Background
The simplest way to think of a CSRF attack is that your browser has two tabs open - Tab A: www.mybank.com
and Tab B: www.attacker.com
.
(As @Alex points out in comments, multiple tabs are not necessary; the important part is that your browser has auth cookies for mybank.com
in memory. CSRF can equally happen if you were on mybank.com
earlier and then without logging out, browse to attacker.com
)
Cookies
Historically, it was common to store your auth / session token in a cookie. This is convenient for pure HTML pages (ie no javascript) because when you send a request to www.mybank.com
, the browser will automatically attach any relevant auth cookies -- no action required on the part of your HTML page in order to maintain the session. CSRF is an attack on cookie-based auth tokens: if Tab B (www.attacker.com
) sends a request to www.mybank.com
, the browser will automatically attach the authentication cookie, ie Tab B can send requests as if they were logged in to Tab A.
Solutions
You need to put your auth / session token somewhere that's not a cookie. Remembering that Tab B can't see any of the content in Tab A, there are any number of places you could put the auth / session token:
- somewhere on the HTML page (maybe in a hidden HTML element),
- or in a non-cookie HTTP Response Header,
- or if this is an API then you could put it in a
token: ___
field of the JSON body, etc.
It really doesn't matter where, so long as it's not in a cookie.
For completeness, I'll add that your anti-CSRF token needs to be long and random enough to prevent the attacker from guessing it. For example, if you use the same anti-CSRF token for all users, or you derive it from the user name, then it doesn't matter that Tab B can't read the content of Tab A because they can guess the token and include it in their blind CSRF request.
References
SameSite
flag for cookies as well as (part of) your protection against CSRF.