Menu
CoddyTech

CSRF und APIs

Teil des Abschnitts JSON-APIs der Django-Journey von Coddy. Lektion 6 von 19.

Ein echtes Programm, das POST /api/tasks/ sendet, erhält 403 Forbidden. Djangos CSRF-Schutz verlangt, dass jeder POST ein geheimes Token von einer deiner eigenen Seiten enthält, damit eine andere Website den Browser eines Besuchers nicht dazu bringen kann, deine Formulare abzusenden. Ein Programm hat keine solche Seite und kein Token.

@csrf_exempt aus django.views.decorators.csrf deaktiviert die Prüfung für eine einzelne View:

@csrf_exempt
def webhook(request):
    ...

Damit gibst du etwas auf: Jede Website kann jetzt den Browser eines Besuchers dazu bringen, diesen POST zu senden. Für eine View, die nicht davon abhängt, wer angemeldet ist, wie die Aufgabenliste, ist das in Ordnung. Bei einer API, die als angemeldeter Benutzer handelt, bleibt CSRF aktiviert, und das Skript der zugehörigen Seite sendet das Token in einem X-CSRFToken-Header.

challenge icon

Aufgabe

Mittel

Erlaube Programmen, ohne CSRF-Token Aufgaben zu erstellen:

  • POST /api/tasks/ funktioniert ohne Token.
  • Nur task_list ist davon ausgenommen.

Klicke auf „Run“ und gib dann Folgendes im Tab „Shell“ ein. Vor der Änderung gibt es 403 aus, danach 201: from django.test import Client; print(Client(enforce_csrf_checks=True).post("/api/tasks/", {"title": "Pay rent"}, content_type="application/json").status_code)

Probier es selbst

import json

from django.http import JsonResponse
from django.shortcuts import render

from .models import Task


def task_to_dict(task):
    return {
        "id": task.id,
        "title": task.title,
        "done": task.done,
        "priority": task.priority,
    }


def home(request):
    return render(request, "tasks/home.html", {"tasks": Task.objects.all()})


def stats(request):
    data = {
        "total": Task.objects.count(),
        "done": Task.objects.filter(done=True).count(),
    }
    return JsonResponse(data)


def task_list(request):
    if request.method == "POST":
        data = json.loads(request.body)
        task = Task.objects.create(
            title=data["title"],
            priority=data.get("priority", "normal"),
        )
        return JsonResponse(task_to_dict(task), status=201)
    tasks = Task.objects.all()
    done = request.GET.get("done")
    if done == "true":
        tasks = tasks.filter(done=True)
    elif done == "false":
        tasks = tasks.filter(done=False)
    return JsonResponse([task_to_dict(task) for task in tasks], safe=False)


def task_detail(request, pk):
    task = Task.objects.filter(pk=pk).first()
    if task is None:
        return JsonResponse({"error": "Task not found"}, status=404)
    return JsonResponse(task_to_dict(task))

Alle Lektionen in JSON-APIs

Übe selbstständig: Django-Playground