CSRF وواجهات API
جزء من قسم واجهات API بصيغة JSON في رحلة Django على Coddy. الدرس 6 من 19.
يحصل برنامج حقيقي يرسل POST /api/tasks/ على 403 محظور. يريد نظام الحماية من CSRF في Django أن يتضمن كل طلب POST رمزًا سريًا من إحدى صفحاتك، حتى لا يتمكن موقع آخر من جعل متصفح الزائر يرسل نماذجك. ليس لدى البرنامج صفحة كهذه ولا رمز.
يعطّل @csrf_exempt، الموجود في django.views.decorators.csrf، عملية التحقق لعرض واحد:
@csrf_exempt
def webhook(request):
...لكن لهذا ثمن: يمكن لأي موقع الآن أن يجعل متصفح الزائر يرسل طلب POST هذا. وهذا مناسب لعرض لا يعتمد على هوية المستخدم الذي سجّل الدخول، مثل قائمة المهام. أما واجهة برمجة التطبيقات التي تعمل باسم المستخدم المسجّل دخوله، فتبقي الحماية من CSRF مفعّلة، ويرسل البرنامج النصي في صفحتها الرمز ضمن ترويسة X-CSRFToken.
التحدي
متوسطاسمح للبرامج بإنشاء المهام من دون رمز CSRF:
- يعمل
POST /api/tasks/من دون رمز. - يُستثنى
task_listفقط.
اضغط على تشغيل، ثم اكتب هذا في علامة تبويب الصدفة. قبل التغيير، يطبع 403، وبعده يطبع 201: 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
2قراءة الطلبات
التصفية باستخدام استعلامالإنشاء باستخدام POSTتدرّب بنفسك: Playground لـ Django