Django / دليل حلول تحريري
تشخيص ردود CSRF 403 دون تعطيل الحماية
افصل غياب الرمز وتسليم الارتباط وفحص الأصل وافتراض الوكيل قبل تغيير عقد الطلب.
قاعدة معرفة للقراءة فقط · بلا تعليقات عامة
تعرّف على الأعراض
يتلقى إرسال نموذج أو JSON رفض 403 رغم إمكان مشاهدة الصفحة، وقد يظهر بعد الدخول أو بمضيف آخر أو وكيل HTTPS. وقد لا يعيده Client العادي لأنه يرخي CSRF افتراضيا.
افهم الأسباب المحتملة
- قد يغيب الرمز أو يختلف عن سر الارتباط أو لا يصل الارتباط أو يفشل الأصل والمرجع، وقد تقادم الصفحة بعد تدوير الدخول.
- تختلف CSRF والهوية والمدخل والإذن؛ فلا تعفي الجلسة ولا تنشئ ALLOWED_HOSTS أصلا موثوقا لـCSRF.
1. حدد حد الرفض الفعلي
سجل الطريقة والوجهة والأصل والحالة والسبب الآمن بدليل خاص دون الارتباط والأسرار والإعداد الكامل. حدد هل الرفض من CSRF أو إذن التطبيق أو مكون أمامي، وأعد ببيانات مصطنعة والمسار المقصود. نجاح GET لا يثبت فحوص POST. احفظ السبب الأول ولا تغير عدة إعدادات معا. كما يجب الحفاظ على أول سبب واضح من السجل الخاص، لأن تغيير إعدادات الرمز والأصل والوكيل معا يجعل نجاح التجربة اللاحقة غير كاف لتحديد الإصلاح المسؤول.
2. تتبع توليد الرمز وتسليم الارتباط
افحص سياق الطلب والوسمة أو ترتيب AJAX الموثق، ثم نطاق الارتباط ومساره وأمانه وsame-site ووصوله فعلا. إذا منعته سياسة JavaScript فاستخدم رمز DOM الموثق لا تضعف الجميع. أعد تحميل النموذج القديم بعد الدخول عند لزوم تحديثه، ولا ترسل الرموز لخارج غير مرتبط. وتختلف قراءة JavaScript للارتباط من مجرد إرساله مع الطلب؛ فقد تسمح سياسة الارتباط بالتسليم وتمنع القراءة، ولا ينبغي إضعافها دون فهم طريقة أخذ الرمز الموثقة.
3. راجع الأصل وتفسير الوكيل منفصلين
قارن الأصل والهدف والموثوق المقصود بالمخطط وفق 5.2، ولا تساوه بقائمة المضيف. قد يحتاج العميل المتقاطع CORS واعتمادات منفصلة لا تنفذها CSRF. تحقق من TLS وتحكم الوكيل بالترويسات قبل الثقة. لا تصلح المجهول ببديل عام أو exemption يمحو الحماية. ولا تضبط قائمة الأصول الموثوقة كل ما يحتاجه متصفح يتصل من أصل آخر؛ فسياسة الاعتمادات وCORS مستقلة ويجب أن تتوافق مع تصميم الواجهة الحقيقي.
4. تحقق من الرفض وإرسال محمي صحيح
تفعل العينة الفحص وترفض بلا رمز ثم تجلبه من GET وتعيده مع ارتباط العميل دون خادم وكتابة. تثبت الوسيط المحلي لا الوكيل والمتصفح. أعد الحقيقي بعينة معتمدة وافحص الصحيح والسالب وافصل الإذن والمدخل. يفسر الإصلاح السبب والحد الباقي. أما نجاح البرنامج فيثبت الطلبين داخل العملية فقط، ولذلك يبقى مرور المتصفح بالوكيل ومصدر HTTPS وتدوير الرمز بعد الدخول محتاجا إلى دليل مستقل في التطبيق.
from django.conf import settings
settings.configure(
SECRET_KEY="synthetic-course-key",
USE_I18N=False, USE_TZ=True,
INSTALLED_APPS=[],
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}},
DEFAULT_AUTO_FIELD="django.db.models.AutoField",
ALLOWED_HOSTS=["testserver"],
)
import django
django.setup()
import json
from django.http import HttpResponse, JsonResponse
from django.middleware.csrf import get_token
from django.test import Client, override_settings
from django.urls import path
def endpoint(request):
if request.method == "GET":
return JsonResponse({"csrf": get_token(request)})
return HttpResponse("accepted", content_type="text/plain")
urlpatterns = [path("form/", endpoint)]
with override_settings(ROOT_URLCONF=__name__, MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"], TEMPLATES=[{"BACKEND": "django.template.backends.django.DjangoTemplates", "APP_DIRS": False}]):
client = Client(enforce_csrf_checks=True)
print(client.post("/form/", {}).status_code)
token = json.loads(client.get("/form/").content)["csrf"]
print(client.post("/form/", {}, HTTP_X_CSRFTOKEN=token).status_code)
403
200
تجري العينة طلبات مصطنعة داخل العملية؛ يرفض الأول ويسمح الرمز والارتباط بالثاني.
تحقق من نجاح الإصلاح
ينجح تدفق المتصفح ويبقى بلا رمز أو الأصل الممنوع مرفوضا، وافحص الاستمرار بعد الدخول والوكيل الحقيقي. لا تنسب الطبقات المنشورة للعينة.
امنع تكرار المشكلة
- احفظ الوسيط والنماذج القياسية ووثق الأصل والوكيل واختبار الصحيح والسالب، ولا تجعل الاستثناء الواسع تصحيحا عاديا.