كيفية تأمين تطبيق Django الخاص بك باستخدام سياسة أمان المحتوى

اختار الكاتب فتيات البرمجة لتلقي تبرع كجزء من برنامج اكتب للتبرعات.

المقدمة

عند زيارتك لموقع ويب، يتم استخدام موارد مختلفة لتحميله وعرضه. على سبيل المثال، عندما تذهب إلى https://www.digitalocean.com، يقوم متصفحك بتنزيل الـ HTML و CSS مباشرةً من digitalocean.com. ومع ذلك، يتم تنزيل الصور والأصول الأخرى من assets.digitalocean.com، وتحميل النصوص التحليلية من نطاقاتها المحددة.

تستخدم بعض المواقع مجموعة متعددة من الخدمات والأنماط والنصوص لتحميل وعرض محتواها، وسيقوم متصفحك بتنفيذ كل ذلك. لا يعرف المتصفح ما إذا كان الكود ضارًا، لذا فإن مسؤولية المطور هي حماية المستخدمين. نظرًا لأنه يمكن أن تكون هناك العديد من الموارد على موقع ويب، فإن وجود ميزة في المتصفح تسمح فقط بالموارد المعتمدة هو وسيلة جيدة لضمان عدم تعرض المستخدمين للمخاطر. هذا هو الغرض من سياسات أمان المحتوى (CSPs).

باستخدام رأس CSP، يمكن للمطور السماح صراحة بتشغيل بعض الموارد مع منع جميع الآخرين. نظرًا لأن معظم المواقع يمكن أن تحتوي على ما يصل إلى 100 مورد، ويجب أن يتم الموافقة على كل واحد لفئة معينة من المورد، يمكن أن يكون تنفيذ CSP مهمة مملة. ومع ذلك، ستكون مواقع الويب التي تحتوي على CSP أكثر أمانًا لأنها تضمن أن يتم السماح فقط بالموارد المعتمدة بالتشغيل.

في هذا البرنامج التعليمي، ستقوم بتنفيذ CSP في تطبيق Django أساسي. ستقوم بتخصيص CSP للسماح ببعض النطاقات والموارد الداخلية للتشغيل. يمكنك، اختياريًا، أيضًا استخدام Sentry لتسجيل الانتهاكات.

المتطلبات المسبقة

لإكمال هذا البرنامج التعليمي، ستحتاج إلى:

الخطوة 1 — إنشاء عرض توضيحي

في هذه الخطوة، ستقوم بتعديل كيفية تعامل تطبيقك مع العروض لكي تتمكن من إضافة دعم CSP.

كمتطلب أساسي، قمت بتثبيت Django وإعداد مشروع عينة. العرض الافتراضي في Django بسيط جدًا لعرض كل إمكانيات وسيط CSP، لذا ستقوم بإنشاء صفحة HTML بسيطة لهذا البرنامج التعليمي.

انتقل إلى مجلد المشروع الذي قمت بإنشائه في المتطلبات المسبقة:

  1. cd django-apps

أثناء تواجدك داخل مجلد django-apps، قم بإنشاء بيئة افتراضية. سنطلق عليها اسم env، ولكن يجب عليك استخدام اسم ذو معنى بالنسبة لك ولمشروعك.

  1. virtualenv env

الآن، قم بتفعيل البيئة الافتراضية باستخدام الأمر التالي:

  1. . env/bin/activate

داخل البيئة الافتراضية، قم بإنشاء ملف views.py في مجلد مشروعك باستخدام nano، أو محرر النصوص المفضل لديك:

  1. nano django-apps/testsite/testsite/views.py

الآن، ستضيف عرضًا أساسيًا سيقوم بعرض قالب index.html الذي ستقوم بإنشائه لاحقًا. أضف ما يلي إلى views.py:

django-apps/testsite/testsite/views.py
from django.shortcuts import render

def index(request):
    return render(request, "index.html")

قم بحفظ الملف وإغلاقه عند الانتهاء.

قم بإنشاء قالب index.html في مجلد templates جديد:

mkdir django-apps/testsite/testsite/templates
nano django-apps/testsite/testsite/templates/index.html

أضف ما يلي إلى index.html:

django-apps/testsite/testsite/templates/index.html
<!DOCTYPE html>
<html>
    <head>
        <title>Hello world!</title>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link
            href="https://fonts.googleapis.com/css2?family=Yellowtail&display=swap"
            rel="stylesheet"
        />
        <style>
            h1 {
                font-family: "Yellowtail", cursive;
                margin: 0.5em 0 0 0;
                color: #0069ff;
                font-size: 4em;
                line-height: 0.6;
            }

            img {
                border-radius: 100%;
                border: 6px solid #0069ff;
            }

            .center {
                text-align: center;
                position: absolute;
                top: 50vh;
                left: 50vw;
                transform: translate(-50%, -50%);
            }
        </style>
    </head>
    <body>
        <div class="center">
            <img src="https://html.sammy-codes.com/images/small-profile.jpeg" />
            <h1>Hello, Sammy!</h1>
        </div>
    </body>
</html>

العرض الذي قمت بإنشائه سيقوم بعرض هذه الصفحة HTML البسيطة. سيعرض النص Hello, Sammy! بالإضافة إلى صورة لـ Sammy the Shark.

قم بحفظ الملف وإغلاقه عند الانتهاء.

للوصول إلى هذا العرض، ستحتاج إلى تحديث urls.py:

  1. nano django-apps/testsite/testsite/urls.py

قم بإستيراد ملف views.py وأضف مسارًا جديدًا عن طريق إضافة الأسطر المظللة:

django-apps/testsite/testsite/urls.py
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index),
]

سيكون العرض الجديد الذي قمت بإنشائه مرئيًا الآن عندما تزور / (عند تشغيل التطبيق).

قم بحفظ الملف وإغلاقه.

أخيرًا، ستحتاج إلى تحديث INSTALLED_APPS لتضمين testsite في settings.py:

  1. nano django-apps/testsite/testsite/settings.py
django-apps/testsite/testsite/settings.py
# ...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'testsite',
]
# ...

هنا، يتم إضافة testsite إلى قائمة التطبيقات في settings.py بحيث يمكن لـ Django أن يفترض بعض الأمور حول هيكل مشروعك. في هذه الحالة، سيفترض أن المجلد templates يحتوي على قوالب Django التي يمكنك استخدامها لتقديم العروض.

من دليل مشروعك الرئيسي (testsite)، ابدأ خادم تطوير Django بالأمر التالي، مع استبدال your-server-ip بعنوان IP الخاص بخادمك.

  1. cd ~/django-apps/testsite
  2. python manage.py runserver your-server-ip:8000

افتح متصفحًا وقم بزيارة your-server-ip:8000. يجب أن تبدو الصفحة مشابهة لهذا:

في هذه المرحلة، تعرض الصفحة صورة الملف الشخصي لـ Sammy the Shark. تحت الصورة، يوجد نص Hello, Sammy! بخط أزرق.

لإيقاف خادم تطوير Django، اضغط على CONTROL-C.

في هذه الخطوة، قمت بإنشاء عرض أساسي يعمل كصفحة رئيسية لمشروع Django الخاص بك. في الخطوة التالية، ستضيف دعم CSP إلى تطبيقك.

الخطوة 2 — تثبيت وسيط CSP

في هذه الخطوة، ستقوم بتثبيت وتنفيذ وسيط CSP بحيث يمكنك إضافة رؤوس CSP والعمل مع ميزات CSP في عروضك. يضيف الوسيط وظائف إضافية إلى أي طلب أو استجابة يتعامل Django معها. في هذه الحالة، يضيف وسيط Django-CSP دعم CSP إلى استجابات Django.

أولاً، ستقوم بتثبيت وسيط CSP Middleware من Mozilla في مشروع Django الخاص بك باستخدام pip، مدير الحزم الخاص بـ Python. استخدم الأمر التالي لتثبيت الحزمة اللازمة من PyPi، فهو فهرس الحزم الخاص بـ Python. لتشغيل الأمر، يمكنك إما إيقاف خادم تطوير Django باستخدام CONTROL-C أو فتح علامة تبويب جديدة في وحدة التحكم الخاصة بك:

  1. pip install django-csp

بعد ذلك، قم بإضافة الوسيط إلى إعدادات مشروع Django الخاصة بك. افتح settings.py:

  1. nano testsite/testsite/settings.py

بعد تثبيت django-csp، يمكنك الآن إضافة الوسيط في settings.py. سيقوم هذا بإضافة رؤوس CSP إلى استجاباتك.
أضف السطر التالي إلى مصفوفة تكوين MIDDLEWARE:

testsite/testsite/settings.py
MIDDLEWARE = [
    'csp.middleware.CSPMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

احفظ وأغلق الملف عند الانتهاء. مشروع Django الخاص بك الآن يدعم CSPs. في الخطوة التالية، ستبدأ في إضافة رؤوس CSP.

الخطوة 3 — تنفيذ رأس CSP

الآن بعد أن يدعم مشروعك CSPs، فهو جاهز لتعزيز الأمان. لتحقيق ذلك، ستقوم بتكوين المشروع لإضافة رؤوس CSP إلى استجاباتك. رأس CSP هو ما يخبر المتصفح كيفية التصرف عندما يواجه نوعًا معينًا من المحتوى. لذا، إذا قال الرأس السماح فقط بالصور من نطاق معين، فإن المتصفح سيسمح فقط بالصور من ذلك النطاق.

باستخدام نانو أو محرر النصوص المفضل لديك، افتح settings.py:

  1. nano testsite/testsite/settings.py

حدد المتغيرات التالية في أي مكان في الملف:

testsite/testsite/settings.py
# سياسة أمان المحتوى

CSP_IMG_SRC = ("'self'")

CSP_STYLE_SRC = ("'self'")

CSP_SCRIPT_SRC = ("'self'")

هذه القواعد هي النموذج الأساسي لـ CSP الخاص بك. تشير هذه الأسطر إلى المصادر المسموح بها للصور والأنماط والنصوص على التوالي. في الوقت الحالي، تحتوي جميعها على سلسلة 'self'، والتي تعني أنه يُسمح فقط بالموارد من نطاقك الخاص.

احفظ وأغلق الملف عند الانتهاء.

شغّل مشروع Django الخاص بك باستخدام الأمر التالي:

  1. python manage.py runserver your-server-ip:8000

عند زيارة عنوان-الخادم-الخاص-بك:8000، سترى أن الموقع معطّل:

كما هو متوقع، لا تظهر الصورة ويظهر النص بالتنسيق الافتراضي (عريض أسود). هذا يعني أن رأس CSP يتم فرضه، وأن صفحتنا الآن أكثر أمانًا. نظرًا لأن العرض الذي قمت بإنشائه مسبقًا يشير إلى أنماط وصور من نطاقات ليست من نطاقك الخاص، يمنع المتصفحها.

لديك الآن مشروع يحتوي على CSP يعمل يُخبر المتصفح بحظر الموارد التي ليست من نطاقك. فيما بعد، ستقوم بتعديل CSP للسماح بموارد محددة، مما سيُصلح صورة الموقع الرئيسية المفقودة والتنسيق.

الخطوة 4 — تعديل CSP للسماح بالموارد الخارجية

الآن بعد أن لديك CSP الأساسي، ستقوم بتعديله استنادًا إلى ما تستخدمه على موقعك. كمثال، الموقع الذي يستخدم خطوط Adobe ومقاطع فيديو YouTube المضمنة سيحتاج إلى السماح بتلك الموارد. ومع ذلك، إذا كان موقعك يعرض الصور فقط من داخل نطاقك الخاص، فيمكنك ترك إعدادات الصور على القيم الافتراضية القياسية الصارمة.

الخطوة الأولى هي العثور على كل مورد تحتاج إلى الموافقة عليه. يمكنك استخدام أدوات تطوير المتصفح الخاص بك للقيام بذلك. افتح مراقب الشبكة في عنصر التفتيش، قم بتحديث الصفحة، وانظر إلى الموارد المحظورة:

سجل الشبكة يظهر أن موردين من الموارد يتم حظرهم بواسطة CSP: ورقة أنماط من fonts.googleapis.com وصورة من html.sammy-codes.com. للسماح بتلك الموارد في رأس CSP، ستحتاج إلى تعديل المتغيرات في settings.py.

للسماح بالموارد من النطاقات الخارجية، أضف النطاق إلى جزء من CSP الذي يتطابق مع نوع الملف. لذا، للسماح بصورة من html.sammy-codes.com، ستضيف html.sammy-codes.com إلى CSP_STYLE_SRC.

افتح settings.py وأضف ما يلي إلى متغير CSP_STYLE_SRC:

testsite/testsite/settings.py
CSP_IMG_SRC = ("'self'", 'https://html.sammy-codes.com')

الآن، بدلاً من السماح فقط بالصور من نطاقك، يسمح الموقع أيضًا بالصور من html.sammy-codes.com.

تستخدم صفحة الفهرسة خطوط Google. تزوّد Google موقعك بالخطوط (من https://fonts.gstatic.com) وبورقة أنماط لتطبيقها (من https://fonts.googleapis.com). للسماح بتحميل الخطوط، أضف ما يلي إلى CSP الخاص بك:

testsite/testsite/settings.py
CSP_STYLE_SRC = ("'self'", 'https://fonts.googleapis.com')

CSP_FONT_SRC = ("'self'", 'https://fonts.gstatic.com/')

على غرار السماح بالصور من html.sammy-codes.com، ستسمح أيضًا بورقات الأنماط من fonts.googleapis.com والخطوط من fonts.gstatic.com. للسياق، يُستخدم ورق الأنماط المحمّل من fonts.googleapis.com لتطبيق الخطوط. تُحمّل الخطوط بذاتها من fonts.gstatic.com.

احفظ وأغلق الملف.

تحذير: بالمثل لـself، هناك كلمات أخرى مثل unsafe-inline، unsafe-eval، أو unsafe-hashes التي يمكن استخدامها في CSP. من الأفضل بشدة تجنب استخدام هذه القواعد في CSP الخاص بك. على الرغم من أن هذه القواعد ستُسهّل التنفيذ، إلا أنها يمكن أن تُستخدم لتفادي CSP وجعلها غير فعّالة.

لمزيد من المعلومات، انظر إلى وثائق منتج Mozilla لـ “Unsafe inline script”.

الآن، ستُسمح لـ Google Fonts بتحميل الأنماط والخطوط على موقعك، وستُسمح لـhtml.sammy-codes.com بتحميل الصور. ومع ذلك، عندما تزور صفحة على خادمك، قد تلاحظ أن الصور فقط تُحمّل الآن. هذا لأن الأنماط المضمنة في HTML والتي تُستخدم لتطبيق الخطوط ليست مُسموح بها. ستُصلح ذلك في الخطوة التالية.

الخطوة 5 — العمل مع النصوص والأنماط الداخلية

في هذه النقطة، قمت بتعديل CSP للسماح بالموارد الخارجية. ولكن الموارد الداخلية، مثل الأنماط والنصوص في عرضك، لا تزال غير مسموح بها. في هذه الخطوة، ستجعل هذه العناصر تعمل بحيث يمكنك تطبيق تنسيق الخط.

هناك طريقتان للسماح بالنصوص والأنماط الداخلية: الأرقام العشوائية والهاش. إذا وجدت أنك تقوم بتعديل النصوص والأنماط الداخلية بشكل متكرر، استخدم الأرقام العشوائية لتجنب التغييرات المتكررة على CSP الخاص بك. إذا كنت نادرًا ما تقوم بتحديث النصوص والأنماط الداخلية، فاستخدام الهاش هو نهج معقول.

استخدام nonce للسماح بالنصوص الداخلية

أولاً، ستستخدم النهج nonce. النهج nonce هو رمز مولد عشوائي يكون فريدًا لكل طلب. إذا زار شخصان موقعك، سيحصل كل منهما على nonce فريد يتم تضمينه في النصوص والأنماط الداخلية التي توافق عليها. فكر في النهج nonce ككلمة مرور مرة واحدة توافق أجزاء معينة من الموقع لتشغيل جلسة واحدة فقط.

لإضافة دعم nonce إلى مشروعك، ستقوم بتحديث CSP في settings.py. افتح الملف للتعديل:

  1. nano testsite/testsite/settings.py

أضف script-src في CSP_INCLUDE_NONCE_IN في ملف settings.py.

قم بتعريف CSP_INCLUDE_NONCE_IN في أي مكان في الملف وأضف 'script-src' إليه:

testsite/testsite/settings.py
# سياسة الأمان للمحتوى

CSP_INCLUDE_NONCE_IN = ['script-src']

CSP_INCLUDE_NONCE_IN يشير إلى النصوص الخطية التي يُسمح لك بإضافة سمة nonce إليها. يُعالج CSP_INCLUDE_NONCE_IN على أنه مصفوفة لأن مصادر البيانات المتعددة تدعم الأرقام المرجعية (على سبيل المثال، style-src).

احفظ وأغلق الملف.

يُسمح الآن بتوليد الأرقام المرجعية للنصوص الخطية عند إضافة السمة nonce إليها في قالب العرض الخاص بك. لتجربة هذا، ستستخدم مقتطف جافا سكريبت بسيط.

افتح index.html للتحرير:

  1. nano testsite/testsite/templates/index.html

أضف المقتطف التالي في الجزء <head> من HTML:

testsite/testsite/templates/index.html
<script>
    console.log("Hello from the console!");
</script>

هذا المقتطف يقوم بطباعة Hello from the console!" في وحدة التحكم بالمتصفح. ومع ذلك، نظرًا لأن مشروعك يحتوي على سياسة أمان للمحتوى تسمح فقط بالنصوص الخطية إذا كانت تحتوي على nonce، فلن يتم تشغيل هذا النص وبدلاً من ذلك سيتم إنتاج خطأ.

يمكنك رؤية هذا الخطأ في وحدة التحكم بالمتصفح عندما تقوم بتحديث الصفحة:

يتم تحميل الصورة لأنك سمحت بالموارد الخارجية في الخطوة السابقة. كما هو متوقع، التنسيق حاليًا هو الافتراضي لأنك لم تسمح بعد بالأنماط الخطية. كما هو متوقع أيضًا، لم تتم طباعة رسالة وحدة التحكم بالمتصفح ولكن عادت بخطأ. ستحتاج إلى إعطائها nonce للموافقة عليها.

يمكنك فعل ذلك عن طريق إضافة nonce="{{request.csp_nonce}}" إلى هذا السكريبت كخاصية. افتح index.html للتحرير وأضف الجزء المظلل كما هو موضح هنا:

testsite/testsite/templates/index.html
<script nonce="{{request.csp_nonce}}">
    console.log("Hello from the console!");
</script>

احفظ وأغلق الملف عند الانتهاء.

إذا قمت بتحديث الصفحة، سيتم تنفيذ السكريبت الآن:

عندما تنظر في عنصر التفتيش، ستلاحظ عدم وجود قيمة للخاصية:

القيمة لا تظهر لأسباب أمنية. لقد قام المتصفح بمعالجة القيمة بالفعل. إنها مخفية بحيث لا يمكن لأي سكريبت لديه الوصول إلى DOM الوصول إليها وتطبيقها على سكريبت آخر. إذا قمت بـ عرض مصدر الصفحة بدلاً من ذلك، هذا ما تلقاه المتصفح:

لاحظ أنه في كل مرة تقوم فيها بتحديث الصفحة، تتغير قيمة nonce. يحدث هذا لأن برنامج الوسيط CSP في مشروعنا يولد nonce جديدة لكل طلب.

تتم إضافة قيم nonce هذه إلى رأس CSP عندما يتلقى المتصفح الاستجابة:

كل طلب يقوم به المتصفح إلى موقعك سيكون له قيمة nonce فريدة لهذا السكريبت. نظرًا لأن الـ nonce مقدم في رأس CSP، فهذا يعني أن خادم Django وافق على تشغيل تلك النصوص المحددة.

لقد قمت بتحديث مشروعك للعمل مع nonce، والذي يمكن تطبيقه على موارد متعددة. على سبيل المثال، يمكنك تطبيقه على الأنماط أيضًا، من خلال تحديث CSP_INCLUDE_NONCE_IN للسماح style-src. ولكن هناك نهج أبسط للموافقة على الموارد الداخلية، وهذا ما ستفعله فيما بعد.

استخدام التجزئة للسماح بالأنماط الداخلية

طريقة أخرى للسماح بالنصوص والأنماط الداخلية هي باستخدام التجزئة. التجزئة هي معرف فريد للمورد الداخلي المعطى.

كمثال، هذا هو النمط الداخلي في قالبنا:

testsite/testsite/templates/index.html
<style>
    h1 {
        font-family: "Yellowtail", cursive;
        margin: 0.5em 0 0 0;
        color: #0069ff;
        font-size: 4em;
        line-height: 0.6;
    }

    img {
        border-radius: 100%;
        border: 6px solid #0069ff;
    }

    .center {
        text-align: center;
        position: absolute;
        top: 50vh;
        left: 50vw;
        transform: translate(-50%, -50%);
    }
</style>

حاليًا، إلا أن الأنماط لا تعمل. عند عرض الموقع في المتصفح، تحميل الصور بنجاح، ولكن الخطوط والأنماط لا يتم تطبيقها:

في وحدة التحكم للمتصفح، ستجد خطأ بأن النمط الداخلي ينتهك سياسة الأمان. (قد تظهر أخطاء أخرى، ولكن ابحث عن الخطأ المتعلق بالنمط الداخلي.)

يتم إنتاج الخطأ لأن النمط لم يتم الموافقة عليه من قبل سياسة الأمان لدينا. ولكن، لاحظ أن الخطأ يوفر التجزئة اللازمة للموافقة على مقتطف النمط. هذه التجزئة فريدة من نوعها لهذا المقتطف النمطي الخاص. لن يكون لدى مقتطفات أخرى نفس التجزئة أبدًا. عند وضع هذه التجزئة داخل سياسة الأمان، في أي وقت يتم تحميل هذا النمط المحدد، سيتم الموافقة عليه. ولكن، إذا قمت بتعديل هذه الأنماط في أي وقت، ستحتاج إلى الحصول على التجزئة الجديدة واستبدال القديمة بها في سياسة الأمان.

الآن، ستقوم بتطبيق التجزئة عن طريق إضافتها إلى CSP_STYLE_SRC في settings.py، على النحو التالي:

  1. nano testsite/testsite/settings.py
testsite/testsite/settings.py
CSP_STYLE_SRC = ("'self' 'sha256-r5bInLZB0y6ZxHFpmz7cjyYrndjwCeDLDu/1KeMikHA='", 'https://fonts.googleapis.com')

إضافة التجزئة sha256-... إلى قائمة CSP_STYLE_SRC ستسمح للمتصفح بتحميل ورقة الأنماط دون أي أخطاء.

احفظ وأغلق الملف.

الآن، أعد تحميل الموقع في المتصفح، ويجب أن تحمّل الخطوط والأنماط بنجاح:

تعمل الأنماط والنصوص المضمّنة الآن بشكل صحيح. في هذه الخطوة، استخدمت نهجين مختلفين، الأول بواسطة الرموز السرية والثاني بواسطة العناوين الهاش، للسماح بالأنماط والنصوص المضمّنة.

ولكن، هناك مشكلة مهمة يجب معالجتها. إن سياسات أمان مصادر المحتوى هي مهمة للغاية للحفاظ على الأمان، لكنها تستلزم صيانة مستمرة، خاصة للمواقع الكبيرة. قد تحتاج إلى طريقة لتتبع حينما تمنع سياسة أمان مصادر المحتوى موردًا حتى تتمكن من تحديد ما إذا كان موردًا خبيثًا أم مجرد جزء معطل من موقعك. في الخطوة القادمة، ستستخدم Sentry لتسجيل وتتبع جميع الانتهاكات التي تنتجها سياسة أمان مصادر المحتوى الخاصة بك.

الخطوة 6 — الإبلاغ عن الانتهاكات باستخدام Sentry (اختياري)

نظرًا لصرامة سياسات أمان مصادر المحتوى، فإنه من الجيد معرفة عندما تمنع محتوى معين — خاصةً نظرًا لأن منع المحتوى يعني على الأرجح عدم عمل بعض الوظائف على موقعك. يمكن لأدوات مثل Sentry إعلامك عندما تمنع سياسة أمان مصادر المحتوى طلبات المستخدمين. في هذه الخطوة، ستقوم بتكوين Sentry لتسجيل والإبلاغ عن انتهاكات سياسة أمان مصادر المحتوى.

كمتطلب أساسي، قمت بالتسجيل في حساب مع Sentry. الآن ستقوم بإنشاء مشروع.

في الزاوية العلوية اليسرى من لوحة تحكم Sentry، انقر على علامة التبويب المشاريع:

في الزاوية العلوية اليمنى، انقر على زر إنشاء مشروع:

سترى عدداً من الشعارات مع عنوان يشير إلى اختر منصة. اختر Django:

ثم، في الأسفل، اسم مشروعك (لهذا المثال، سنستخدم sammys-tutorial)، وانقر فوق زر إنشاء مشروع:

سيقدم لك Sentry كود مقتطف لإضافته إلى ملف settings.py. قم بحفظ هذا المقتطف لإضافته في خطوة لاحقة.

في الطرفية الخاصة بك، قم بتثبيت مجموعة أدوات Sentry:

  1. pip install --upgrade sentry-sdk

افتح settings.py مثل هذا:

  1. nano testsite/testsite/settings.py

أضف ما يلي إلى نهاية الملف، وتأكد من استبدال SENTRY_DSN بالقيمة من لوحة المعلومات:

testsite/testsite/settings.py
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration

sentry_sdk.init(
    dsn="SENTRY_DSN",
    integrations=[DjangoIntegration()],

    # ضبط معدل العينات لـ traces_sample_rate على 1.0 لالتقاط 100٪
    # من المعاملات لمراقبة الأداء.
    # نوصي بضبط قيمة هذا المعدل في الإنتاج.
    traces_sample_rate=1.0,

    # إذا كنت ترغب في ربط المستخدمين بالأخطاء (بشرط استخدام
    # django.contrib.auth) يمكنك تمكين إرسال بيانات PII.
    send_default_pii=True
)

يُقدم هذا الكود من Sentry بحيث يمكنه تسجيل أي أخطاء تحدث في تطبيقك. إنه التكوين الافتراضي لـ Sentry ويهيئ Sentry لتسجيل المشكلات على خادمنا. من الناحية التقنية، ليس عليك تهيئة Sentry على خادمك لمخالفات CSP، ولكن في الحالة النادرة التي تحدث فيها بعض مشاكل تقديم nonces أو hashes، سيتم تسجيل هذه الأخطاء في Sentry.

قم بحفظ الملف وإغلاقه.

ثم، ارجع إلى لوحة التحكم لمشروعك وانقر فوق أيقونة العتاد للدخول إلى الإعدادات:

انتقل إلى علامة التبويب رؤوس الأمان:

انسخ report-uri:

أضفه إلى CSP الخاص بك على النحو التالي:

testsite/testsite/settings.py
# سياسة أمان المحتوى

CSP_REPORT_URI = "your-report-uri"

تأكد من استبدال your-report-uri بالقيمة التي قمت بنسخها من لوحة التحكم.

احفظ وأغلق ملفك. الآن، عندما يتسبب فرض CSP في انتهاك، سيقوم Sentry بتسجيله على هذا الرابط URI. يمكنك تجربة ذلك عن طريق إزالة نطاق أو تجزئة من CSP الخاص بك، أو عن طريق إزالة nonce من النص الذي أضفته سابقًا. قم بتحميل الصفحة في المتصفح وسترى الخطأ في صفحة مشاكل Sentry:

إذا وجدت نفسك مضغوطًا بعدد السجلات، يمكنك أيضًا تعريف CSP_REPORT_PERCENTAGE في settings.py لإرسال نسبة مئوية من السجلات إلى Sentry فقط.

testsite/testsite/settings.py
# سياسة أمان المحتوى
# إرسال 10% من السجلات إلى Sentry
CSP_REPORT_PERCENTAGE = 0.1

الآن، عندما يحدث انتهاك لـ CSP، ستتلقى إشعارًا ويمكنك عرض الخطأ في Sentry.

الاستنتاج

في هذا المقال، قمت بتأمين تطبيق Django الخاص بك باستخدام سياسة أمان المحتوى. قمت بتحديث السياسة الخاصة بك للسماح بالموارد الخارجية، واستخدمت الأرقام العشوائية والهاشات للسماح بالنصوص والأنماط الداخلية. كما قمت بتكوينه لإرسال الانتهاكات إلى Sentry. كخطوة مقبلة، تحقق من وثائق Django CSP لمعرفة المزيد حول كيفية فرض سياسة أمان المحتوى الخاصة بك.

Source:
https://www.digitalocean.com/community/tutorials/how-to-secure-your-django-application-with-a-content-security-policy