flask_wtforms مدخل إلى استخدام مكتبة WTForms للتعامل مع نماذج HTML في تطبيقات Flask


عبدالهادي الديوري

مقدّمة

بعد أن تعرّفنا على كيفيّة التّعامل مع قاعدة بياناتنا لإجراء العمليّات الشّائعة مثل الإضافة، القراءة والحذف في سلسلة مدخل إلى إطار العمل Flask فقد حان الوقت للانتقال إلى مبدأ آخر من مبادئ تطوير الويب، ألا وهو كيفيّة التّحقق من مُدخلات المُستخدم، وسنتعرّف معا في هذا الدّرس على كيفيّة استغلال كل ما تعلّمناه لنُمكّن المُستخدم الذي يستعمل تطبيقنا من إرسال بياناته إلى التّطبيق بطريقة آمنة، ومن ثمّ ستتمكّن أنت من استغلال ما ستتعلّمه لإدخال هذه البيانات إلى قواعد بيانات تطبيقاتك، وخلال هذا المشوار سنعرّف على كيفيّة مُعالجة نماذج HTML ليُرسلها المستخدم إلى الخادوم عن طريق صفحة HTML، وذلك باستخدام كل من مكتبة WTForms و إضافة Flask-WTF التّي تُسهّل لنا التّعامل مع المكتبة، والغرض من كل هذا هو التّعرف على كيفيّة إنشاء وإدارة النّماذج لاستعمالها في مختلف الصّفحات في تطبيقاتك (صفحة تسجيل الدّخول، صفحة تسجيل مستخدمين جدد، صفحة إضافة مقال …).

flask-wtforms.png

المُتطلّبات

يُعتبر هذا الدّرس مُقدّمة لسلسلة جديدة حول كيفيّة إدارة نماذج HTML عند استخدام لغة بايثون وإطار العمل Flask لبناء تطبيقات الويب، لذا فسيتوجّب عليك فهم أساسيّات إطار العمل وكذا فهم آليّة عمل نماذج HTML أو HTML Forms.
إن لم تكن لك خبرة في التّعامل مع إطار العمل Flask فأنصحك بهذه السّلسلة من الدّروس.

لماذا WTForms

قد تتساءل عن السّبب الذي سيدفعنا لاستخدام مكتبة WTForms لإدارة نماذج HTML في تطبيقنا، ففي السّابق، كنّا نقوم بإنشاء نموذج HTML ونستخدم المُساعد request المُتواجد بإطار العمل Flask للحصول على البيانات التّي يُرسلها المُستخدم.

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

@app.route('/login', methods = ['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    if username == 'admin' and password == 'admin':
        # login the user ...
        return redirect(url_for('index'))

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

إليك أحد أخطار عدم التّأكد من أنّ البيانات التّي يُرسلها المُستخدم آمنة قبل إدخالها إلى قاعدة البيانات، لنقل بأنّك تُتيح التّسجيل لزوار الموقع، عبر منحهم إمكانية إدخال اسم مُستخدم، بريد إلكتروني وكلمة مرور، رغم أنّ المُستخدمين عادة ما يوفّرون مدخلات معقولة إلّا أنّ عدم التّحقق منها قد يؤدي إلى قاعدة البيانات ممتلئة أو بيانات تخريبيّة بإرسال كم هائل من النّصوص كقيم للحقول الثّلاثة مثلا (اسم المُستخدم، كلمة المرور، البريد الإلكتروني)، ولو عرضت اسم المُستخدم في أحد صفحات التّطبيق فسيُخرّب الاسم الطّويل الصّفحة وستبدو غير متناسقة؛ هذا جانب من العواقب التّي قد تحدث إن لم تتحقّق من مُدخلات المُستخدم.

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

تُوفّر مكتبة WTForms طريقة بسيطة لإنشاء نماذج HTML وتأكيدها قبل أن تصل إلى الخادوم لنتمكّن من إرسالها إلى قاعدة البيانات، على سبيل المثال، توفّر لنا المكتبة إمكانيّة التأكّد من أنّ قيمة حقل تحتوي على عدد معيّن من الأحرف، أو تأكيد حقل بريد إلكتروني أو أنّ قيمة حقل تُساوي قيمة حقل آخر (عند تأكيد كلمة المرور مثلا).
هناك بالطّبع مكتبات أخرى تؤدّي نفس الغرض (كمكتبة Deform)، لكنّنا اخترنا هذه المكتبة بالذّات لأنّ لها إضافة تُسهل علينا ربطها بالتطبيقات المكتوبة بإطار العمل فلاسك، بالإضافة إلى أنّها خيار شائع بين المُطوّرين الذين يستخدمون إطار العمل هذا، ما يعني بأنّ تعلّم كيفيّة التّعامل مع المكتبة سيُساعدك على فهم معظم المشاريع مفتوحة المصدر.

الحماية من هجمات CSRF

بالإضافة إلى مُساعدتنا على تأكيد البيانات وتقييدها بشروط خاصّة، فمكتبة WTForms تحمينا من هجمات CSRF (اختصار لـ Cross-site request forgery)، وترجمته ‘تزوير الطلب عبر المواقع’، وهي نوع معيّن من الهجمات تُمكّن المخترق من القيام بأمور خطيرة متنكّرا على هيئة المُستخدم الضّحيّة (الذي قد سجّل دخوله بتطبيقك)، وقد استخدمت هذه الهجمة من قبل لتحويل أموال بشكل غير شرعي من طرف مُخترق لأحد الأبناك في السّابق، وظهرت هذه الثّغرة من قبل في موقع Youtube ما خوّل للمُخترق بالقيام بجميع العمليّات التّي يُمكن أن يقوم بها المُستخدم الذي سجّل دخوله، ويُمكنك زيارة صفحة ويكيبيديا عن الهجمة لمزيد من المعلومات.

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

الهجمة خطيرة وعليك الحذر منها عند تطويرك لتطبيقات الويب، لكن لا تقلق، فمكتبة WTForms تُوفّر لنا طريقة بسيطة لحماية تطبيقنا من هذا النّوع من الهجمات، إذ تعتمد على الحل الذي ذكرته مسبقا بحيث تُحدّد رمزا مخفيا في بداية النّموذج ليُرسل مع طلب المُستخدم في كل مرّة.

التّحقق من المُدخلات بالواجهة الأماميّة أم الواجهة الخلفيّة؟

بما أنّ التّحقق من مُدخلات المُستخدم هو الاستخدام الشّائع لمكتبة WTForms (للتحقق مثلا من أن المدخل على شكل بريد إلكتروني وليس أية سلسلة نصيّة أخرى)، فمن المُفيد أن تُدرك بأنّك تستطيع التّحقق من المُدخلات على جهتين أثناء تطوير تطبيقاتك، ومن المهم أن تتبّع الأسلوب الصّحيح، لذا فهذا القسم يُغطي جزءا ممّا يجب عليك معرفته.

الواجهة الأماميّة أو جهة العميل Client side مُصطلح يهم كل ما يحدث في مُتصفّح المُستخدم، وعادة ما تتم مُعالجة البيانات فيه باستخدام لغة جافاسكربت أو ميّزات HTML5 بحيث نستطيع مثلا أن نتأكّد من أنّ المُدخل المطلوب لا يُرسَل فارغا أو أنّه بطول مُعيّن وأمور أخرى.

أمّا في جهة الخادوم، فالتّحقق من المُدخلات يتم باستخدام لغة بايثون، إمّا عبر كتابة الشّيفرة التّي تقوم بالتّحقق بنفسك أو بالاعتماد على مكتبات مثل مكتبة WTForms.

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

عندما تتحقّق من المُدخلات باستعمال جافاسكربت فقط فالأمر مُجد في كثير من الأوقات، لكن ماذا لو عطّل المُستخدم جافاسكربت على مُتصفّحه؟، ويُمكنك كذلك استخدام الميّزات الجديدة في HTML5 للتّحقق من أنّ المُدخلات صحيحة، وهذه الطّريقة يسهل الاحتيال عليها كذلك، لذا فأفضل حل هو أن تقوم بالتّحقق من مُدخلات المُستخدم في جهة الخادوم لأنّها الأكثر أمانا، وهذا يرجع إلى كون البيانات التّي نستقبلها من المُستخدم تُدخل إلى قاعدة البيانات على جهة الخادوم لذا يجب أن توقف هناك، وبالطّبع تستطيع استعمال الميّزات الجديدة في لغة HTML5 (وهذا ما أنصح به) أو أحد أطر العمل المكتوبة بلغة جافاسكربت للتّحقق من مُدخلات المُستخدم، لكن تذكّر فقط بأنّ الجهة الخلفيّة (استخدام مكتبة WTForms في حالتنا هذه) هي الأهم والأكثر أمانا.

إضافة Flask-WTF

لاستخدام مكتبة WTForms، سنلجأ إلى إضافة خاصّة بها لإطار العمل فلاسك، وتُسمى هذه الإضافة Flask-WTF اختصارا لـ Flask-WTForms ، لذا تأكّد من أنّ البيئة الوهميّة مُفعّلة ونفّذ الأمر التّالي لتنصيب الإضافة (والمكتبة كذلك):

pip install Flask-WTF

لن نستخدم الإضافة كثيرا ولن تجد استخداما لها في المشاريع التي تعتمد على إطار العمل فلاسك في معظم الحالات والاعتماد سيكون على مكتبة WTForms أكثر من الإضافة، إذ سنعتمد على الإضافة لنسترد منها صنفا واحدا فقط باسم FlaskForm لترث منها الأصناف الأخرى التّي ستُمثّل نماذج HTML، وهذا لأنّ WTForms يعتمد على الأصناف في لغة بايثون لإنشاء نماذج HTML بحيث يعبّر كل صنف عن نموذج معيّن وخصائص الصّنف هي التّي تُمثّل الحقول المُختلفة (حقل كلمة المرور، حقل البريد الإلكتروني، حقل الاختيارات المُتعدّدة …) وسنتطرّق إلى هذا الأمر بتفصيل أكثر فيما بعد.

استخدام مكتبة WTForms مع أطر العمل الأخرى

إنّ استخدام WTForms أمر شائع بين مُطوري الويب الذين يستخدمون أطر العمل الصّغيرة مثل Flask و Bottle و Pyramid، وحقيقة أنّ هذه السّلسلة من الدّروس موجّهة إلى مُطوري الويب الذين يستخدمون إطار العمل Flask لا تعني بالضّرورة بأنّك لن تستفيد من هذا الدّرس إذا كنت تستعمل أحد أطر العمل الأخرى، وقد قرّرت أن أجعل الأمثلة بسيطة جدا ليتمكّن الكل من فهم كيفيّة العمل المبدئيّة، كما أنّ الهدف من هذه السّلسلة هو تقريب مفهوم التحقق من بيانات المُستخدم إلى أكبر شريحة ممكنة من المُطورين لتوفير حماية أفضل لتطبيقاتهم، وبالتّالي فهي سلسلة مُستقلّة تماما عن أي سلسلة دروس أخرى، رغم أنّني أنصحك بقراءة سلسلة مدخل إلى إطار العمل فلاسك قبل الشّروع في هذه السّلسلة.

تحديد مفتاح سري

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

عادة ما يُسمى المفتاح السرّي مِلحًا Salt في مجال التّشفير، ويعمل على جعل السّلسلة المُولّدة ثابتة إن كان المفتاح السّري هو نفسه، فإن تغيّر المفتاح السّري (أو الملح) فإنّ فك التّشفير غير ممكن، لذا يجب أن يكون المفتاح السّري سريّا جدّا بحيث لا يُمكن توقّعه، كما يجب ألّا تضعه في مكان مكشوف في شيفرتك البرمجيّة، وتأكّد من ألّا تضع نفس المفتاح السّري الذي تستخدمه في تطبيقك على مواقع مثل Github و Pastebin وغيرها، فإن وصل أي شخص إلى مفتاحك السّري الذي تستخدمه لإدارة جلسات مُستخدميك فقد تُسرق بياناتهم أو تُخرّب.

هذه المرّة، لن نحتاج إلى المفتاح السّري من أجل الجلسة، بل ستستعمله إضافة Flask-WTF لتوليد المفتاح الخفي الذي تحدّثنا عنه مُسبقا، وتستخدمه بعض الإضافات الأخرى لأغراض التّشفير كذلك، لذا فمن المُهمّ أن تُبقيّه غير قابل للتّنبّؤ.

ولتوليد مفتاح سري جيّد، يُمكنك استخدام الدّالّة urandom من الوحدةos على مُفسّر بايثون كما يلي:

>>> import os

>>> os.urandom(16)
' T\x1f\x85\x9b\xfe^\x0f\x14\x9f\xa3x\xa4\xb5\x92\xbe'

كما تُلاحظ، قمنا بتوليد سلسلة عشوائيّة من الصّعب توقّعها، وبالطّبع فعندما تُنفّذ أنت السّطر أعلاه، فستحصل على سلسلة أخرى غير السّلسلة التّي حصلت عليها.

الآن كلّ ما عليك فعله هو نسخ السّلسلة النّصيّة التّي حصلت عليها ووضعها في الإعداد SECRET_KEY الخاص بتطبيق فلاسك الذي تعمل عليه، والتّالي مثال على كيفيّة إضافة هذا الإعداد. المهم أن تتأكّد من أنّ السّطر التّالي متواجد مُباشرة بعد تعريف الكائن app:

app.config['SECRET_KEY'] = 'T\x1f\x85\x9b\xfe^\x0f\x14\x9f\xa3x\xa4\xb5\x92\xbe'

والتّالي هو ما أعنيه بتعريف الكائن app:

app = Flask(__name__)

وهو نفس الكائن الذي تستعمله لتحديد موجّهات التّطبيق الأساسيّة.

مُلاحظة: إن حاولت استخدام WTForms دون توفير مفتاح سري فستحصل على خطأ نصّه كما يلي: “Exception: Must provide secret_key to use csrf.”.

خاتمة

تعرّفنا في هذا المقال على أهم المفاهيم الأساسيّة التّي يتوجّب عليك الإلمام بها لاستكمال مشوارك في تطوير الويب، فقد تعرّفنا على الأسباب التّي ستجعلك تتحقّق من مُدخلات المُستخدم، ولمَ يُفضّل استخدام مكتبة مثل WTForms عوضا عن القيام بذلك بنفسك، وتعرّفنا كذلك على هجمة CSRF التّي تعتبر واحدة من الهجمات التّي يُمكن أن يتعرّض لها تطبيقك في أي وقت، وقد كان هذا الدّرس تمهيدا بسيطا لسلسلة من الدّروس حول التّحقق من مُدخلات المُستخدم باستخدام مكتبة WTForms.



1 شخص أعجب بهذا


تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن