Menu
CoddyTech

CSRF ו-API

חלק מהיחידה ממשקי API של JSON במסלול ה-Django של Coddy. שיעור 6 מתוך 19.

תוכנית אמיתית ששולחת POST /api/tasks/ מקבלת 403 Forbidden. הגנת ה־CSRF של Django דורשת שכל בקשת POST תכלול אסימון סודי מאחד הדפים שלך, כדי שאתר אחר לא יוכל לגרום לדפדפן של מבקר לשלוח את הטפסים שלך. לתוכנית אין דף כזה ואין לה אסימון.

@csrf_exempt, מתוך django.views.decorators.csrf, משבית את הבדיקה עבור תצוגה אחת:

@csrf_exempt
def webhook(request):
    ...

יש לכך מחיר: כל אתר יכול כעת לגרום לדפדפן של מבקר לשלוח את בקשת ה־POST הזאת. זה מתאים לתצוגה שאינה תלויה בזהות המשתמש המחובר, כמו רשימת המשימות. ממשק API שפועל בשם המשתמש המחובר משאיר את הגנת CSRF פעילה, והסקריפט של הדף שלו שולח את האסימון בכותרת X-CSRFToken.

challenge icon

אתגר

בינוני

אפשר לתוכניות ליצור משימות ללא אסימון CSRF. בקובץ tasks/views.py, יש לפטור רק את task_list מהבדיקה, כך ש-POST /api/tasks/ יפעל ללא אסימון.

יש להריץ את השורה שלמטה בכרטיסייה Shell, שבה enforce_csrf_checks=True גורם ל-Client הבדיקה לבדוק אסימונים: היא מדפיסה 201 במקום 403.

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

נסו בעצמכם

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

כל השיעורים ביחידה ממשקי API של JSON

תרגלו בעצמכם: Django אונליין