3
votes

I have an ajax request that sends a file to django, it is saying that the csrf token is missing but i copied my other ajax request that are working. I'm sure it has something to do with trying to pass a file to upload.

I get a 403 and csrf missing return.

base.html

<script type='text/javascript' src='http://code.jquery.com/jquery-1.8.2.js'></script>
<script type="text/javascript">
    $(document).ready(function() {  
                $("#create_token").click(function() {
                        var username = document.getElementById("username").value;
                        $.ajax({
                            url : "/gettoken/", 
                            type : "POST",
                            dataType: "json", 
                            data : {
                                csrfmiddlewaretoken: '{{ csrf_token }}',
                                create_token: 'create_token',
                                username: username,
                                },
                                success : function(json) {
                                    document.getElementById('output').innerHTML = ('Token: ' + json['token']);
                                },
                                error : function(xhr,errmsg,err) {
                                    console.log(xhr.status + ": " + xhr.responseText);
                                    document.getElementById('output').innerHTML = "Token:" + " Request Failed.";
                                }

                        });
                        return false;  
                }); 

                $("#add_friend").click(function() {
                        var token = document.getElementById("friend_token").value;
                        $.ajax({
                            url : "/addfriend/", 
                            type : "POST",
                            dataType: "json", 
                            data : {
                                csrfmiddlewaretoken: '{{ csrf_token }}',
                                add_friend: token,
                                },
                                success : function(json) {
                                    document.getElementById('output').innerHTML = (json['message']);
                                },
                                error : function(xhr,errmsg,err) {
                                    console.log(xhr.status + ": " + xhr.responseText);
                                    document.getElementById('output').innerHTML = "Request Failed.";
                                }

                        });
                        return false;  
                }); 

                $("#uppropic").click(function() {
                        var file = document.getElementById("profile_pic").files[0];
                        console.log(file);
                        $.ajax({
                            url : "profilepic/", 
                            type : "POST",
                            dataType: "json", 
                            processData: false,
                            data : {
                                csrfmiddlewaretoken: '{{ csrf_token }}',
                                profile_pic: file,
                                },
                                success : function(json) {
                                    document.getElementById('output').innerHTML = (json['message']);
                                },
                                error : function(xhr,errmsg,err) {
                                    console.log(xhr.status + ": " + xhr.responseText);
                                    document.getElementById('output').innerHTML = " Request Failed.";
                                }

                        });
                        return false;  
                }); 


            });

home.html

{% extends "base.html" %}
{% block title %}
{% for user in user_data %}
    {{user.username}}
{%endfor%}
{% endblock %}
{% block content %}
    {% for user in user_data %}
        Username: {{user.username}}<br>
        First Name: {{user.first_name}}<br>
        Last Name: {{user.last_name}}<br>
        About: {{user.about}}<br>
        Title: {{user.title}}<br>
    {%endfor%}
    Friends:
    {% for friend in friend_data %}
        {{friend}}<br>
    {%endfor%}
            {% if is_user_profile %}
        <form method='POST' >
        {% csrf_token %}
        <input type='text' name='friend_token' id='friend_token'>
        <button id='add_friend' name = 'add_friend' value='add_friend' > Add Friend </button>
        </form>

        <form method='POST' >
        {% csrf_token %}
        <button id='create_token' name = 'create_token' value='create_token' > Create Token </button>
        {% for user in user_data %}
        <input type='hidden' id='username' value='{{user.username}}'>
        {%endfor%}
        </form>

        <p id='output'>
        </p>
        {%endif%}

        <form method='POST'>
        {% csrf_token %}
        <input type='file' name='profile_pic' id='profile_pic'>
        <button id='uppropic'> Upload Profile Pic</button>
        {% for user in user_data %}
        <input type='hidden' id='username' value='{{user.username}}'>
        {%endfor%}
        </form>

    <a href="/logout/">Logout</a>
{% endblock %}

views.py

@login_required  
@csrf_protect       
def upload_profilepic(request):
    context = {}
    if request.method == 'POST':
        post_data = request.POST.copy()
        profile_pic = post_data['profile_pic']
        print post_data, profile_pic
        handle_uploaded_file(request.FILES['file'])
        context.update({'message':'You must select a file to upload.'})
        return HttpResponse(simplejson.dumps(context), content_type='application/json')   
    else:
        context.update({'message':'You must select a file to upload.'})
        return HttpResponse(simplejson.dumps(context), content_type='application/json')   

consoleoutput

File { name: "10923328_1112855418728180_5377511192406844214_n.png", lastModified: 1421116207673, lastModifiedDate: Date 2015-01-13T02:30:07.673Z, size: 664332, type: "image/png" } home:56
"403: 
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE">
  <title>403 Forbidden</title>
  <style type="text/css">
    html * { padding:0; margin:0; }
    body * { padding:10px 20px; }
    body * * { padding:0; }
    body { font:small sans-serif; background:#eee; }
    body>div { border-bottom:1px solid #ddd; }
    h1 { font-weight:normal; margin-bottom:.4em; }
    h1 span { font-size:60%; color:#666; font-weight:normal; }
    #info { background:#f6f6f6; }
    #info ul { margin: 0.5em 4em; }
    #info p, #summary p { padding-top:10px; }
    #summary { background: #ffc; }
    #explanation { background:#eee; border-bottom: 0px none; }
  </style>
</head>
<body>
<div id="summary">
  <h1>Forbidden <span>(403)</span></h1>
  <p>CSRF verification failed. Request aborted.</p>


</div>

<div id="info">
  <h2>Help</h2>

    <p>Reason given for failure:</p>
    <pre>
    CSRF token missing or incorrect.
    </pre>


  <p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
  <a
  href='http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ref-contrib-csrf'>Django's
  CSRF mechanism</a> has not been used correctly.  For POST forms, you need to
  ensure:</p>

  <ul>
    <li>Your browser is accepting cookies.</li>

    <li>The view function uses <a
    href='http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext'><code>RequestContext</code></a>
    for the template, instead of <code>Context</code>.</li>

    <li>In the template, there is a <code>{% csrf_token
    %}</code> template tag inside each POST form that
    targets an internal URL.</li>

    <li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
    <code>csrf_protect</code> on any views that use the <code>csrf_token</code>
    template tag, as well as those that accept the POST data.</li>

  </ul>

  <p>You're seeing the help section of this page because you have <code>DEBUG =
  True</code> in your Django settings file. Change that to <code>False</code>,
  and only the initial error message will be displayed.  </p>

  <p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
</div>

</body>
</html>
"

After doing some research the problem has to do with the way the ajax request is passing the file to django thats causing my csrf error. So I tried to follow what other people have done by appending the csrf token to formData.

here is a stack post that has this JQuery: post FormData AND csrf token together

The following settings are messing up the ajax request processData: false, contentType: false,

            $("#uppropic").click(function() {
                    var file = document.getElementById("profile_pic").files[0];
                    console.log(file);
                    var csrf = '{{ csrf_token }}';
                    console.log(csrf);
                    var formData = new FormData(document.getElementById("profile_pic_form"));
                    formData.append('csrfmiddlewaretoken', csrf );
                    console.log(formData);

                    $.ajax({
                        url : "/profilepic/", 
                        type : "POST",
                        dataType: "json", 
                        processData: false,
                        contentType: false,
                        data : {
                            csrfmiddlewaretoken: '{{ csrf_token }}',
                            profile_pic: file,
                            },
                            success : function(json) {
                                document.getElementById('output').innerHTML = (json['message']);
                            },
                            error : function(xhr,errmsg,err) {
                                console.log(xhr.status + ": " + xhr.responseText);
                                document.getElementById('output').innerHTML = " Request Failed.";
                            }

                    });
                    return false;  
            }); 
3
It's usually not a good idea to mix server-side templating tags into your client-side JavaScript. Things like {{ ... }} should be in the html, or if you absolutely must, you should put the line var csrf = '{{ csrf_token }}'; in a script tag in the <head> and the rest of your JS should go in .js files without any template tags. This won't solve your problem, but it will help you debug, because you can "view source" and see what's in the markup.sstur

3 Answers

1
votes

Usually I've seen this error when the template does not set the csrf_token. From the docs

If your view is not rendering a template containing the csrf_token template tag, Django might not set the CSRF token cookie. This is common in cases where forms are dynamically added to the page. To address this case, Django provides a view decorator which forces setting of the cookie: ensure_csrf_cookie().

Is the page rendering the file upload form setting the token? Maybe the {% if is_user_profile %} condition evaluates to False and the token is not set?

You could try using the @ensure_csrf_cookie decorator in you view that renders the file upload form if nothing else helps.

0
votes

I solved the problem by adding the following to my head-section:

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:csrfMetaTags />
<script type="text/javascript">
  var csrfParameter = $("meta[name='_csrf_parameter']").attr("content");
  var csrfHeader = $("meta[name='_csrf_header']").attr("content");
  var csrfToken = $("meta[name='_csrf']").attr("content");
</script>

The data for the Request looks like:

var file = document.getElementById("rawdatafile");
var completeData = new FormData();
completeData.append("rawData", file.files[0]);

And in the Ajax-Request I did:

$.ajax({
  type: "POST",
  url: "<c:url value='/myurl/etc' />",
  headers: {'X-CSRF-TOKEN': csrfToken},
  cache: false,
  processData: false,
  contentType: false,
  data: completeData,
  dataType: "text",
  ...

This way I can upload a multipart file and also pass the csrf token via the http header.

0
votes

Don't use JSON like data. Looks like:

$.ajax({
    data: {
        csrfmiddlewaretoken:'{{ csrf_token }}',
        profile_pic: file
    },
})

Use JS method new FormData() to create data object. Looks like:

$.ajax({
    data: new FormData($('form')[0]),
})

Or:

var formData = new FormData()
formData.append("csrfmiddlewaretoken", '{{ csrf_token }}')
formData.append("profile_pic", file)

$.ajax({
    data: formData,
})

Full AJAX example:

   $.ajax({
       type : 'post',
       url : "profilepic/",
       data : formData,

       // You *must* include these options!
       cache : false,
       processData : false, 
       contentType : false, 

       success : function(){}
       error : function(){ }
   })