Do I need CSRF protection for /login endpoint?
Issue opened for the creation of a wiki page that summarizes the doubts and problems for newbies (https://github.com/volatiletech/authboss/issues/210).
I know
this question has already been asked so many times, but after hours of searching I still don't have a clear answer to my problem.
Even projects like https://github.com/pillarjs/understanding-csrf have been abandoned and have not answered to new questions and doubts over the years like this.
PROBLEM
Let's say I have:
- a back-end on
back.domain.comand - a front-end on
front.domain.com.
My back-end is a simply app with these rest endpoints:
-
POST /login:- accepts JSON body like:
{"username": "myname", "password": "mypass"} - verify credentials
- if OK gives 200 and create a cookie with session
- if NOT gives 401
- accepts JSON body like:
-
GET /players:- check session in cookie
- if OK gives 200 with {"players": "[...]"}
- if NOT gives 401
-
POST /player/1:- check session in cookie
- if OK gives 200 and edit player
- if NOT gives 401
My front-end app has:
-
/loginpage with a form (withusernameandpasswordfields) for issue aPOSTrequest toback.domain.com/login -
/playerswhich request aGETrequest toback.domain.com/players -
a button which issues a
POSTrequest toback.domain.com/player/1
QUESTIONS
-
Do I need CSRF protection in this scenario?
I think YES, I need because an attacker can issue a request to
back.domain.com/player/1frommalicious.site.comand use my session cookie to edit player because I'm logged in (and I still have a session cookie) on mydomain.com. -
Do I need CSRF protection (e.g. an
X-CSRF-Tokenheader) when I the first time login onback.domain.com/login?- In this scenario I still don't have any session cookie in my browser.
- And also I don't know where to get my CSRF token for
X-CSRF-Tokenauthorization header too.
I read on https://fractalideas.com/blog/making-react-and-django-play-well-together-single-page-app-model they are creating a dedicated endpoint on back-end for this and they explain it's not a security vulnerability.
I saw in authboss-sample you just add the X-CSRF-TOKEN header on OPTIONS calls.
if *flagAPI {
// In order to have a "proper" API with csrf protection we allow the options request to return the csrf token that's required to complete the request when using post
optionsHandler := func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-CSRF-TOKEN", nosurf.Token(r))
w.WriteHeader(http.StatusOK)
}
// We have to add each of the authboss get/post routes specifically because chi sees the 'Mount' above as overriding the '/*' pattern.
routes := []string{"login", "logout", "recover", "recover/end", "register"}
mux.MethodFunc("OPTIONS", "/*", optionsHandler)
for _, r := range routes {
mux.MethodFunc("OPTIONS", "/auth/"+r, optionsHandler)
}
}
But why are you using that?
Are you using that token in javascript after the OPTIONS call?
But the very first one call is failing, right?
I'm confused.
What do you think about using the dataInjector func to inject csrf token in json response? In fact it is like when we render csrf token in html. Right?
@frederikhors Browsers automatically fire off OPTIONS pre-flight requests when they're needed to determine CORS headers.
Note the section on pre-flighted requests here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
The options handler is a single request, and the login post is a follow-up to that that happens after the options is complete. There is no first request failure happening.
It's actually possible there's a bug here because in order for the browser to send back that header it may have to be specified in the CORS headers.
For anyone else, Authboss puts CSRF token in the header. If you are importing Authboss into a new server(project), it may be a good idea to put CSRF token in form or somewhere else, or your tokens will fail on both sides. I use echo framework with Authboss and I put token in the form.
e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
TokenLookup: "form:csrf", // Please use token in form // authboss puts token in header causing clashes
}))