41

I am working on a single page application and I am using Laravel 5 for the web service.

All forms are submitted asynchronously and I use a beforeSend on them to attach the CSRF token which I take from the meta tag like so:

$.ajax({
    url: '/whatever/route',
    type: 'POST',
    dataType: 'JSON',
    data: $('form#whatever-form').serialize(),
    beforeSend: function(request) {
        return request.setRequestHeader('X-CSRF-Token', $("meta[name='token']").attr('content'));
    },
    success: function(response){
        rivets.bind($('#whateverTag'), {whateverData: response});
    },
    error: function(response){
    }
});

All my forms work fine but dropzone upload doesn't. It gives me back a TokenMismatchException exception. Here is my dropzone code to update the profile photo:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!'
});

I have tried putting the beforeSend in here too:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!',
    beforeSend: function(request) {
        return request.setRequestHeader('X-CSRF-Token', $("meta[name='token']").attr('content'));
    },
});

I have also tried to put a global ajaxSetup in my main file like so:

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="token"]').attr('content')
    }
});

It is still not working. What am I doing wrong? How can I pass the CSRF token in the header with the dropzone upload so as to not a get an exception?

Rohan
  • 13,308
  • 21
  • 81
  • 154
  • You wrote "Dropbox," but I don't see anything in your question that relates to Dropbox. Perhaps you meant to say Dropzone? I'm going to remove the Dropbox tag for now. – user94559 May 10 '15 at 08:47
  • Thank you for bringing it to my notice. That was so dumb of me. – Rohan May 10 '15 at 08:56

13 Answers13

68

Okay so this code is working just fine now:

$("#mydropzone").dropzone({
    url: "/profile/update-photo",
    addRemoveLinks : true,
    maxFilesize: 5,
    dictDefaultMessage: '<span class="text-center"><span class="font-lg visible-xs-block visible-sm-block visible-lg-block"><span class="font-lg"><i class="fa fa-caret-right text-danger"></i> Drop files <span class="font-xs">to upload</span></span><span>&nbsp&nbsp<h4 class="display-inline"> (Or Click)</h4></span>',
    dictResponseError: 'Error uploading file!',
    headers: {
        'X-CSRF-TOKEN': $('meta[name="token"]').attr('content')
    }
});

So basically I needed to add the X-CSRFToken in the header of the Dropzone request. Works like charm now.

oliverbj
  • 5,771
  • 27
  • 83
  • 178
Rohan
  • 13,308
  • 21
  • 81
  • 154
  • Documentation on Dropzone site: http://www.dropzonejs.com/#config-headers – sameers Dec 22 '16 at 00:53
  • 8
    just wanted to clear up the naming a little bit, the default Laravel install is looking for `'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')`. https://laravel.com/docs/5.5/csrf#csrf-x-csrf-token – Andrew Brown Sep 18 '17 at 03:02
  • Could you edit the code and change the header parameter from `X-CSRFToken` to `X-CSRF-Token`. Someone new could spend many hours figuring out that small bit. – Eugene Mar 22 '18 at 23:32
  • For Yii (PHP) framework, it works when I set as `'X-CSRF-TOKEN'` or `'X-Csrf-Token'` in Dropzone config and read as `$_SERVER['HTTP_X_CSRF_TOKEN']` on server side. But if I set as `'X_CSRF_TOKEN'` in Dropzone config, it does not show on server side. Just a note, not complaining. – davneetnarang Apr 11 '19 at 15:21
7

You can add csrf token for every jquery ajax request within your application with these code.

$.ajaxSetup({
    headers: {
        'X-CSRF-Token': $('meta[name="_token"]').attr('content')
    }
});
Nyan Lynn Htut
  • 657
  • 1
  • 8
  • 10
  • 2
    Per [the jQuery doc on this function](https://api.jquery.com/jquery.ajaxsetup/), "its use is not recommended." Also, I think this might only affect AJAX calls made via `$.ajax()` in the jQuery library? – sameers Dec 22 '16 at 00:52
3

This also works pretty well :

$("#mydropzone").dropzone({
  url: "/profile/update-photo",
  addRemoveLinks : true,
  maxFilesize: 5,
  dictResponseError: 'Error uploading file!',
  headers: {
    'X-CSRF-Token': $('input[name="authenticity_token"]').val()
  }
});
Feuda
  • 2,335
  • 30
  • 28
2
Dropzone.autoDiscover = false;
        // or disable for specific dropzone:
        // Dropzone.options.myDropzone = false;

        $(function () {
            // Now that the DOM is fully loaded, create the dropzone, and setup the
            // event listeners

            var myDropzone = new Dropzone("#my-awesome-dropzone");
            myDropzone.on("addedfile", function (file) {
                /* Maybe display some more file information on your page */
            });
            myDropzone.on("sending", function (file, xhr, formData) {
                 formData.append('csrfmiddlewaretoken', document.getElementsByName('csrfmiddlewaretoken')[0].value);
                /* Maybe display some more file information on your page */
            });
        });

You could include it this way.

Jeffrey Chidi
  • 341
  • 3
  • 14
1

I believe the best way to handle this is to set it by default for all ajax posts (with jQuery) as according to the Django docs

https://docs.djangoproject.com/en/1.8/ref/csrf/#ajax

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

function sameOrigin(url) {
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isn't scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            // Send the token to same-origin, relative URLs only.
            // Send the token only if the method warrants CSRF protection
            // Using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

In your example, you have a typo when adding it to the Dropzone.js ajax post.

'X-CSRF-Token'

should be

'X-CSRFToken'

jb0t
  • 31
  • 3
  • Setting it to default ajax posts creates another issue that I encountered later on. I was trying to consume a third party service and got stuck because I could not understand what the error was. Later I realized, the default settings have been sending the CSRF token to the third party and it had been causing a problem. :) – Rohan Aug 20 '15 at 06:48
  • Good point and worth remembering! Would say judgement call. If you have far more local ajax calls in your code then perhaps making an exception for remote calls would make more sense, or inverse when the opposite is true. Whatever ultimately yields less work. cheers! – jb0t Aug 21 '15 at 18:52
1

For anyone using default Laravel setup:

window.Laravel = {!! json_encode([
    'csrfToken' => csrf_token(),
]) !!};

Dropzone.options.attachments = {
    url: 'upload',
    headers: {
        'X-CSRF-TOKEN': Laravel.csrfToken
    }
}
Steve Bauman
  • 8,165
  • 7
  • 40
  • 56
1

For those of you that came here and are looking for the Rails solution, add the header with the following code:

  headers: {
    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
  },

The same applies for Laravel 6.x according to the docs: https://laravel.com/docs/6.x/csrf#csrf-x-csrf-token

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});
Paulo Fidalgo
  • 21,709
  • 7
  • 99
  • 115
0

We can set CSRF token in request header.

 xhr = open("POST",logURL,true);
      //Set CSRF token in request header for prevent CSRF attack.
 xhr.setRequestHeader(CSRFHeaderName, CSRFToken);
kiran
  • 77
  • 1
  • 11
0
you can add a headers.

var myDropzone = new Dropzone("#drop_id", {
    url: "/upload/",
    headers: {'x-csrftoken': $.cookie('csrftoken')},
    method:"post",  
    ...
}
0

Django solution (thanks to @Rohan):

headers: {
    'X-CSRFTOKEN': $('meta[name="token"]').context.cookie.split('=')[1]
},
A Petrov
  • 417
  • 1
  • 9
  • 23
0

None of the other answers seem to point out that you first need the meta tag added to your layout blade file, presumably because the default blade layout files have it, but for ease of reference, it can be added as follows:

<meta name="csrf-token" content="{{ csrf_token() }}">

You can then reference the X-CSRF-TOKEN header in your Dropzone call's parameters:

Dropzone.autoDiscover = false;
jQuery(document).ready(function($) {
  $("div#uploader").dropzone({ 
        headers: { 
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }, 
        paramName: 'attachment', 
        url: "/upload/path"
  });
});
Grant
  • 5,709
  • 2
  • 38
  • 50
0

Wow! Amazing feedback and suggestions! I took a bit of every reply and made it fit to what I needed.

Thus, let me pass-it-forward with the code that I am now using for my FLASK server using Flask-WTF and the "X-CSRF-Token" Dropzone Header.

<form>
<div class="form-horizontal">
    <div class="upload-drop-zone" id="drop-zone-licenseKey">
        <div class="dz-message">
            Drag and Drop, or Click to<br> enter your new license key
        </div>
    </div>
    <script>
        var uploadLicenseKey = new Dropzone("div#drop-zone-licenseKey",{
        init: function() 
            {
                // Do Stuff
            },
        url: "/myLicenseURL",
        paramName: "myKey",
        maxFilesize: 1, //MB,
        maxFiles: 1,
        uploadMultiple: false,
        addRemoveLinks: true,
        autoProcessQueue: false, // do not upload until save is pressed
        acceptedFiles: ".txt",
        headers: { "X-CSRF-Token" : "{{ csrf_token() }}" }
        });
    </script>
</div>
Stryker2k2
  • 108
  • 9
0

Using Laravel 9, Attach this method at configuration field ...has worked fine to me!

 sending: function(file, xhr, formData) {
        formData.append("_token", "{{ csrf_token() }}");
    },

From Dropzone CSRF token mismatch Laravel 5

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Bruno
  • 11
  • 1