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.
אתגר
בינוניאפשר לתוכניות ליצור משימות ללא אסימון 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 אונליין