jeff
jeff

Reputation: 596

Implementing Cross-Domain CSRF Protection

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

Answers (1)

Juraj Martinka
Juraj Martinka

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

Related Questions