Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django/Graphene/Apollo/django-webpack-loader/Vue: CORS/CSRF not working together?

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?

like image 495
nerdoc Avatar asked May 21 '19 15:05

nerdoc


1 Answers

It's probably fine to skip CSRF check for that request, but it's hart t 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 web browsers work. This hole goes as follows: any website can contain a from that submits data to your website, and when doing so, cookies will be passed along 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.
like image 88
GwynBleidD Avatar answered Oct 22 '22 17:10

GwynBleidD