14

I'm working on a project using Django as backend, Vue as frontend, and trying to implement Apollo/Graphene/GraphQL as data transfer layer. Most of it works, but I don't get my head around the CORS/CSRF settings.

(Had really much research here. here, and here and here.

Does anyone know how to solve securing the graphql/graphene API via a CSRF token? On the django log terminal, I get:

Forbidden (CSRF token missing or incorrect.): /graphql/

...while on the Vue/Js Console I see

Cross-Origin Request Blocked: The Same Origin Policy disallows 
reading the remote resource at http://localhost:8080/sockjs-node/
info?t=1558447812102. 

You can see (and checkout, it's open source) this project here.

http://localhost:8000, http://localhost:8000/admin and http://localhost:8000/ work nicely. The query query{menuItems{id, title, slug, disabled}} works well in graphiql.

settings.py:


INSTALLED_APPS = [
    # ...
    'corsheaders',
    'rest_framework',
    'webpack_loader',
    'graphene_django',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware', # new
    # ...
]

CORS_ORIGIN_ALLOW_ALL = DEBUG    # (=True)

The problem is here: * yarn serve is running on http://localhost:8080 * ./manage.py runserver is running on http://localhost:8000, and proxies through webpack the Vue frontend dev server.

vue.config.js:


module.exports = {
    // The base URL your application bundle will be deployed at
    publicPath: 'http://localhost:8080',

    // ...

    chainWebpack: config => {
        // ...
        config.devServer
            .public('http://localhost:8080')
// ...

vue-apollo.js:

const httpEndpoint = process.env.VUE_APP_GRAPHQL_HTTP || 'http://localhost:8000/graphql/'

EDIT: If I wrap the graphql/ api urlpath with csrf_exempt, it works:

urlpatterns = [ # ...
    path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),
]

But this is a BadIdea(TM) securitywise. How can I get that token into the frontend using Vue with Django and webpack_loader?

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
nerdoc
  • 1,044
  • 10
  • 28
  • Small note: The order of the installed apps matters. So maybe try messing around with that, I am sure this wont be a solution but you can never be too sure. – Kevin Hernandez May 21 '19 at 22:56
  • *“Does anyone know how to solve this?”* Solve what? You don’t make clear what actual problem you’re having. *“The cause is that IMHO the server refuses to give data outside because of a malformed CORS setting.”* What exact error message do you get indicating a “malformed CORS setting”? What have to done to try fixing that? *“But I really gave up here.”* You gave up on trying to solve the actual cause of the problem? Now you’re looking for a workaround? Or what? *“So maybe the main question is: how to integrate CSRF tokens into a graphql api request?”* How’s that related to the CORS issue? – sideshowbarker May 21 '19 at 22:58
  • 1
    A better way of solving this is to serve everything from one domain/port, and use a frontend proxy to route things to the two servers as appropriate. I recommend Traefik for this. – halfer May 21 '19 at 23:08
  • (To add to my remarks from yesterday, you can get into a pickle with corporate/mobile firewalls if you try to use non-standard networks for AJAX or WS requests. Stick to 80/443 if you can, unless you know your userbase won't have an issue). – halfer May 22 '19 at 08:59
  • @sideshowbarker I edited the question to make it a bit clearer, thanks for bumping. Hope it's more understandable now. I came a bit further with a solution and could narrow down the problem a bit. Please be patient, I am no professional programmer, and it's my first bigger project with js frontends. So my knowledge basics here are a bit low. – nerdoc May 23 '19 at 06:18
  • @halfer at a development server with Django, webpack, webpack_loader, it' s not possible to use one port. At production sure. – nerdoc May 23 '19 at 06:19
  • Ok, voted to reopen – sideshowbarker May 23 '19 at 07:19
  • 1
    @nerdoc...I use Axios with vue.js to gather the data needed from my backend. In this instance there are a lot of posts as to how to allow CSRF tokens to be passed so Django supports your call but more or less you just set a global default to allow it: ```axios.defaults.xsrfHeaderName = "X-CSRFToken"``` and then ```axios.defaults.xsrfCookieName = 'csrftoken'``` – ViaTech May 17 '20 at 16:04

1 Answers1

2

It's probably fine to skip CSRF check for that request, but it's hard to assess it from information you've given, so let me explain why do we need CSRF checks at the first place.

CSRF was created to fix a "hole" that is present in how HTTP and how web browsers work. This hole goes as follows: any website can contain a form that submits data to your website, and when doing so, cookies will be passed along with the form submitted by user.

This means that 3rd party website can trick your users to perform some action on your website. To prevent that, idea of CSRF tokens was created. Simply put: any form on your website that is responsible for performing any action that can be in any way harmful for user when tricked by 3rd party website to submit it, has to contain a CSRF token field next to all data being submitted for this action. The same CSRF token needs to be present either in user's session or in cookies. When form is submitted, those 2 tokens are compared and if they don't match or any of them doesn't exist, form will be rejected.

This protects any form to be submitted by 3rd party website, because cookies from your website cannot be read by other websites, even if they are passed along with request coming from such website. It is impossible then for that website to set matching token in form data.

That being said, this issue is not present when you are not keeping user sessions by using cookies. It is also not an issue when your frontend is on separate domain, as all requests coming from your frontend will have Origin header with it's domain name. So if either of those is the case, you can disable CSRF checks accordingly:

  • When not using cookies for user sessions or user authentication (if you purely rely on JWTs passed by headers for example), you can disable CSRF altogether for all views that are not using cookies.
  • When your frontend is on separate domain (or subdomain), allowed by CORS to connect to your website, use CSRF_TRUSTED_ORIGINS to whitelist it.
Arun T
  • 1,114
  • 6
  • 17
GwynBleidD
  • 20,081
  • 5
  • 46
  • 77