djangocsrf-protectiondjango-csrf

CSRF fails with multiple domains (not subdomains) served by Django app


I have a Django app that serves under domain.com predominantly, but also under other domains (mostly for testing). I also have a separate subdomain that serves the web app client code to users.

The primary domain for the API is api.domain.com, and the apps are being deployed to apps.domain.com. The staging servers are not under the same domain name; they live at stagingX.otherdomain.com. This is out of my control.

The problem I am having is that I am getting CSRF failures when I try to log in from apps.domain.com:

Failed to load resource: the server responded with a status of 403 (OK)
login error: "CSRF Failed: Referer checking failed - http://apps.domain.com/ does not match https://api.domain.com/."

It a simple world I would simply set CSRF_COOKIE_DOMAIN to *.domain.com and be done. However, this completely destroys my ability to stage code prior to deploying, because directly accessing the API at stagingX.otherdomain.com also needs to work for different tests that need to be run.

Can anyone enlighten me on how exactly I should be setting this up? I am ruing the day I decided to deploy apps to a separate server, but app deployment and API deployment should be separated for other reasons... it's just driving me nuts.

Thanks in advance.

-- EDITED to add additional info --

For what it's worth, I am using CORS protection as well. It looks like maybe I should be using some sort of CORS header rather than CSRF in this case, but I don't fully understand the CORS mechanism enough to understand if this is where the solution lies. I do have apps.domain.com whitelisted, but this isn't working on login and perhaps on other endpoints (can't get past login to check the others).


Solution

  • In 1.9, the CSRF_TRUSTED_ORIGINS setting was added. You can set this to a list of (sub)domains that are valid in the csrf referrer check:

    CSRF_TRUSTED_ORIGINS = ['api.domain.com', 'apps.domain.com', 'stagingX.otherdomain.com']
    

    Set this in the settings file for api.domain.com, and it will accept POST requests from all 3 domains.