Reputation: 596
I know this question has been asked several times, but I have never found a clear solution. How does one protect a web application against a CSRF attack?
I currently have two domains, frontend.com
and backend.com
. The backend uses flask-wtf
to create a session (stored as a cookie on frontend browser) and returns a CSRF token
in the header of the response.
I am able to get the token and send requests with the token attached to the header. However, I noticed the cookie was never set. As a result, I am assuming this is why I'm getting an error a 400: Bad Request
. After continuous reading and researching, I am almost convinced that it's meant to be used in the same domain.
My current technology stack is:
DNS: Cloudflare
Hosting: Heroku
I can't see how CSRF tokens can be used in this scenario, unless I am missing something that is critical for it to work.
That said, what would be the best approach addressing CSRF protection in a cross-domain setting given my situation?
In addition, this all works in my local development environment. I'm not sure why it suddenly stopped working in production.
A sample request looks like this:
const headers = {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
};
...
const handleFormSubmission = async e => {
e.preventDefault();
await axios.post('https://backend.com/add-results', { tokenId: tokenId }, { withCredentials: true, headers: headers })
}
And the backend has the following settings:
...
CORS(app, origins=["https://www.frontend.com"], expose_headers=["Content-Type", "X-CSRFToken"], supports_credentials=True)
...
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['REMEMBER_COOKIE_SECURE'] = True
app.config['REMEMBER_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
It's very strange as this works on development environment.
Upvotes: 0
Views: 815
Reputation: 4358
You actually seem to have a working CSRF protection in place. You said:
I am able to get the token and send requests with the token attached to the header. However, I noticed the cookie was never set.
So your problem seems to be that you are not getting the session cookie which is a different thing.
This is a classic problem for cross-domain requests.
By default, browsers won't send cookies to another domain because of security reasons.
You either need to enable CORS via Access-Control-*
headers or use an alternative token storage mechanism instead of cookies (e.g. Web Storage).
I can very much recommend the Api Security in Action book that deals with this topic in Chapter 5 (specifically 5.1 and then 5.2 for Web Storage)
Upvotes: 2