0

I'm developing two separate django (version 3.1) projects. I keep getting a "403: CSRF cookie not set" error and have no idea what the problem is.

  • Project 1 is running on an Apache server & takes POST requests containing JSON data & sends back a JSON response.
  • Project 2, running on localhost, sends the POST via Javascript XMLHttpRequest() function to Project 1.

This question seems to be having the same problem as me, but I'm trying to send info between different URL's (more specifically, POST from localhost:8000, eventually url.com, to engine.url.com) and I don't think that user's solution will work for me.

I've tried using the @csrf_exempt decorator on Project 1 views.py, and it works just fine. However, personally identifying information might be sent between the two, so I want to make sure things are secure. A csrf cookie is best practice if I'm not mistaken, so I have the decorator @ensure_csrf_cookie before my view.

I've tried everything I could find online to fix the problem. In Project 1 settings.py, I've included 'corsheaders' in my INSTALLED_APPS, 'django.middleware.csrf.CsrfViewMiddleware' in MIDDLEWARE and have the following code at the end of the file:

CSRF_COOKIE_SECURE = False
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_HTTPONLY = False

CORS_ALLOWED_ORIGINS = [
    'http://project.com',
    'https://project.com',
    'http://engine.project.com',
    'https://engine.project.com',
    'localhost:8000',
    'http://localhost:8000',
    '127.0.0.1',
    'http://127.0.0.1',
]

CORS_ALLOW_HEADERS = [
    'X-CSRFToken',
    'x-csrftoken',
    'X-CSRFTOKEN',
    'content-type',
]

I include a {% csrf_token %} in the Project 2 template, but the actual POST request is coming from the Javascript. Pertinent excerpt from the Javascript static file below:

function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // 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;
}

function getPaymentDates(jsonData) {
  const csrftoken = getCookie('csrftoken');
  jsonData.csrfmiddlewaretoken = csrftoken;
  var sentJsonString = JSON.stringify(jsonData);
  var xhr = new XMLHttpRequest();
  xhr.open("POST", "http://engine.project.com/app/", false);
  xhr.setRequestHeader('X-CSRFToken', csrftoken);
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.send(sentJsonString);
  // xhr response should be a json string
  return xhr.response;
}

getCookie() is lifted straight from the csrf django documentation.

After testing in the console, I know a cookie is obtained, but I get a 403 error before the app can do anything with the data. Again, if I use @csrf_exempt then the app works just fine, but if that makes the data vulnerable to malintent, then I want to prevent that.

Am I redundantly calling for a csrf cookie which causes an error? I'm stumped and somewhat new to web development. Happy to provide more code if that aids in answering this question.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197

2 Answers2

1

Please try this: Set the needed cookie on the document by adding code below on the template before calling getCookie():

<script>
   document.cookie = "csrftoken={{ csrf_token }}"
</script>
Ruperto
  • 326
  • 2
  • 8
  • Unfortunately this didn't work. I tried this line in the template, also tried adding to the static file but I'm still getting a 403 error. – user560554 Feb 04 '21 at 14:33
1

@Ruperto is right. You can also try and not set any cookie just a global variable

<script>
   var csrftoken="{{ csrf_token | escapejs }}"
</script>

and skip the line const csrftoken = getCookie('csrftoken');.

Or you should probably set some additional safety properties to your cookie. eg.

document.cookie = "csrftoken={{ csrf_token | escapejs }};SameSite=Strict;Secure"

UPDATE:
One solution for sending the csrftoken is to set up a csrf middleware. In settings.py my middleware looks like this:

MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    # ...
]
Barney Szabolcs
  • 11,846
  • 12
  • 66
  • 91
  • Ruperto's solution didn't work unfortunately. I tried yours but I'm also still getting a 403 error. Question: Do I need to add the @ensure_csrf_cookie at the top of the view that is _sending_ the POST or _receiving_ the POST? – user560554 Feb 04 '21 at 14:35
  • I usually use csrf middleware. I have updated my answer. I hope it helps. – Barney Szabolcs Feb 04 '21 at 14:44
  • 1
    this might help too https://stackoverflow.com/questions/40616115/django-403-csrf-verification-failed – Ruperto Feb 05 '21 at 02:05
  • 1
    @Ruperto thanks, funnily enough I ran into a csrf expiry problem myself today and used your link to fix it. :) – Barney Szabolcs Feb 06 '21 at 12:22
  • @Barney you're welcome. 'nice to know it was useful – Ruperto Feb 06 '21 at 13:23