1
votes

I am building an plugin for a certain application in c# which has to communicate with a Django rest interface. As you know, Django uses CSRF tokens for extra security.

I have a problem when doing a POST/PUT call to Django. The call will always return "CSRF Failed: CSRF token missing or incorrect.". I tested the call in the browser (The web interface can also edit data, and will call the same PUT request), and to my surprise a different csrf token value is used in the X-CSRFToken header, than the one stored in the cookie. Is the X-CSRFToken header supposed to use a newly generated token ? The X-CSRFToken value is never used again after this call (the old csrftoken value is still used for all subsequent calls...).

Obiously I have also read the Django docs regarding csrf tokens. Even though I think I understand how it works, I don't get why a new token is used for a POST/PUT. There are a lot of examples which do not make sense for use in my C# plugin.

What i do before doing the PUT:

  • Fetch the CSRF token by requesting the login page which contains the csrfmiddlewaretoken in it's body.
  • Store a cookie "csrftoken" with the token as value (The cookie is not stored automatically, unfortunately)
  • Login the user (POST) using credentials (csrfmiddlewaretoken={token} is also passed as parameter)

After doing the above, all GET calls work perfectly. The problem occurs when i try to do a POST or PUT. This is my PUT code, ignoring the json and url generation since these are not the issue:

// The _csrftoken used here, is the one I fetch at the beginning
Client().DefaultRequestHeaders.Add("X-CSRFToken", _csrftoken);

// The content is json
var response = Client().PutAsync(url, content).GetAwaiter().GetResult();

// The returned content is: {"detail":"CSRF Failed: CSRF token missing or incorrect."}"
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();

// 403 is returned
if (response.IsSuccessStatusCode) { ... }

The above code is made with the following state:

  • User is logged in (sessionid cookie is available)
  • csrf token is available from login (csrftoken cookie is also available)

What I have tried to resolve the issue:

  • Pass csrfmiddlewaretoken={token} as a parameter
  • store a X-CSRFToken cookie
  • Using the X-CSRFToken which i copied from a PUT done in the web interface
  • Trying HttpWebRequest instead of PutAsync
  • Replicate a PUT from the web interface by copying all the headers/parameters/cookies from a PUT done in the web interface
  • ...

Can someone help me understand what I am doing wrong ?

Thank you very much for any help you can give me.

1

1 Answers

0
votes

So the problem here was that we were trying to use the API out of the context of browsing webpages, and purely as an api to do GET/POST/.. calls on. This does not work when you are using CSRF tokens because these tokens are passed along when browsing in the form of cookies and metadata in the html page.

So using an api with CSRF tokens only works if the API is used in the context of browsing. If you want to use the API out of that context, you need to use a different form of authentication like OAuth2.

For this to work without the context of browsing, you have to fetch a html page for every POST/PUT/DELETE to get a new CSRF token which you have to pass along with the call. Obviously this brings a lot of overhead, and this is not how you want to work with an API.

I asked the API developper, to provide OAuth2 authentication.