Menu
CoddyTech

CSRF e API

Fa parte della sezione API JSON del percorso Django di Coddy. Lezione 6 di 19.

Un programma reale che invia POST /api/tasks/ riceve 403 Forbidden. La protezione CSRF di Django vuole che ogni POST includa un token segreto proveniente da una delle tue pagine, così un altro sito web non può fare in modo che il browser di un visitatore invii i tuoi moduli. Un programma non ha una pagina di questo tipo né un token.

@csrf_exempt, da django.views.decorators.csrf, disattiva il controllo per una vista:

@csrf_exempt
def webhook(request):
    ...

Questo comporta una rinuncia: qualsiasi sito web può ora fare in modo che il browser di un visitatore invii questa richiesta POST. Va bene per una vista che non dipende da chi ha effettuato l'accesso, come l'elenco delle attività. Un'API che agisce come l'utente che ha effettuato l'accesso mantiene attiva la protezione CSRF e lo script della sua pagina invia il token in un'intestazione X-CSRFToken.

challenge icon

Sfida

Medio

Consenti ai programmi di creare attività senza un token CSRF. In tasks/views.py, esenta dal controllo solo task_list, in modo che POST /api/tasks/ funzioni senza token.

Esegui la riga qui sotto nella scheda Shell, dove enforce_csrf_checks=True fa sì che il client di test controlli i token: stampa 201 invece di 403.

from django.test import Client; print(Client(enforce_csrf_checks=True).post("/api/tasks/", {"title": "Pay rent"}, content_type="application/json").status_code)

Provalo tu

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))

Tutte le lezioni di API JSON

Esercitati da solo: Playground Django