دليل · python3 smtp

كيف ينفّذ فريق المنتج Python 3 SMTP بأمان؟

نفّذ Python 3 SMTP خلف عامل خادم مصرَّح به، لا في شيفرة المتصفح أو شيفرة يتحكم فيها المستخدم. ابنِ الرسائل باستخدام EmailMessage، وافصل مستلِمي الظرف عن الترويسات الظاهرة، وأنشئ سياق SSL موثَّقًا، وحدّد مهلات اتصال محدودة، واستخدم إما SMTP_SSL للحصول على TLS منذ بدء الاتصال أو SMTP.starttls() متبوعًا بـ EHLO للترقية الصريحة. حمّل بيانات الاعتماد من مدير أسرار، واستدعِ send_message()، وافحص نتائج المستلِمين المرفوضين، واحفظ نتيجة المحاولة بدقة. أعد المحاولة للإخفاقات المؤقتة فقط بتراجع محدود، ولا تعتبر قبول SMTP دليلًا على الوصول إلى صندوق الوارد.

حدّد عملية بريد واحدة مصرَّحًا بها

ابدأ من حدث منتج معتمد مثل توثيق الحساب أو إيصال أو تنبيه مطلوب أو إشعار أمني. خزّن مهمة صادرة دائمة قبل فتح اتصال SMTP. ينبغي أن تحتوي المهمة على مفتاح حدث عمل ثابت والمستأجر وفئة الرسالة ومراجعة القالب ومُرسِل الظرف والمستلِمين المعتمدين وهوية From الظاهرة وأساس الموافقة أو الضرورة ونتيجة المنع الحالية. ويجب ألا يختار المتصفح أو الجوال أو القالب أو مدخلات المستخدم مضيفات SMTP أو بيانات الاعتماد أو مُرسِلي الظرف أو الترويسات التعسفية أو المستلِمين غير المقيَّدين. فوّض المستدعي والمستأجر، وتحقق من العناوين، وحدّد أعداد المستلِمين والمرفقات، وامنع حقن السطر الجديد. استحوذ على المهمة مرة واحدة واحتفظ بسجل محاولات للإضافة فقط. إن smtplib في Python عميل بروتوكول؛ ولا يوفّر عدم التكرار في منطق العمل ولا عزل المستأجرين ولا الموافقة ولا المنع ولا طابورًا دائمًا. هذه الضوابط تعود إلى التطبيق المحيط به.

ابنِ رسائل منظَّمة باستخدام EmailMessage

استخدم email.message.EmailMessage بدلًا من ربط سلاسل الترويسات وMIME. اضبط From وTo وSubject وترويسة ارتباط ثابتة للتطبيق من قيم متحقَّق منها، ثم استدعِ set_content للنص العادي، وadd_alternative لجزء HTML عند الحاجة، وadd_attachment لأنواع الملفات وأحجامها المدعومة صراحةً فقط. ولِّد النص وHTML من مراجعة القالب المعتمدة نفسها. هرّب (escape) القيم غير الموثوقة بحسب سياق المخرجات وتجنّب عرض HTML خام من المستخدم. ولا تضع أسرارًا أو رموز وصول أو بيانات شخصية غير ضرورية أو مفاتيح قاعدة بيانات داخلية في الترويسات أو المواضيع أو حقول التتبع أو أسماء المرفقات. تقوم حزمة email بالتسلسل وفق سياستها وقد تولّد حدود MIME أثناء التسطيح، لذا وقّع التمثيل المتسلسل النهائي أو جزّئه إذا كانت ضوابط السلامة اللاحقة تعتمد على البايتات الدقيقة. وأبقِ ظرف SMTP منفصلًا: فترويستا To وCc الظاهرتان تخاطبان القرّاء، بينما تتحكم قائمة مستلِمي النقل في أوامر RCPT TO.

اختر TLS الضمني أو STARTTLS عن قصد

استخدم SMTP_SSL عندما يشترط الخادم TLS منذ بداية الاتصال. واستخدم SMTP لاتصال غير مشفّر فقط عندما يتطلب سير عمل الخادم الموثَّق ترقية STARTTLS فورية. تنص وثائق smtplib في Python على أن starttls يضع أوامر SMTP اللاحقة داخل TLS وأن على العميل استدعاء ehlo مرة أخرى بعد ذلك. لا تصادق أبدًا قبل ترقية TLS المطلوبة. أنشئ السياق باستخدام ssl.create_default_context ليعتمد التحقق من الشهادة وفحص اسم المضيف على الإعدادات الافتراضية الآمنة للعميل، ومرّر اسم مضيف الخادم المتوقع عبر اتصال المكتبة المعتاد. وتعامل مع غياب دعم STARTTLS أو فشل الشهادة أو عدم تطابق اسم المضيف أو فشل تفاوض TLS بوصفه توقفًا إلزاميًا عندما يكون التشفير مطلوبًا. لا تعطّل التحقق ولا تستبدل سياقًا غير موثَّق لجعل الإنتاج يعمل. يحمي TLS بين القفزات اتصال SMTP، لا المحتوى المخزَّن ولا معالجة المزوّد ولا تخزين المستقبِل ولا صندوق البريد النهائي.

أبقِ بيانات الاعتماد على الخادم ومحدودة الصلاحيات

حمّل اسم مستخدم SMTP أو كلمة المرور أو الرمز وقت التشغيل من خدمة أسرار مُدارة. لا تضعها أبدًا في مستودع المصدر أو طبقات Docker أو إعدادات مودَعة في Git أو عناوين URL أو وسائط سطر الأوامر أو مخرجات التنقيح أو التحليلات أو تقارير الاستثناءات أو لقطات الاختبار أو الدفاتر (notebooks) أو التذاكر أو المطالبات (prompts). وفضّل بيانات اعتماد محدودة ببيئة واحدة أو نطاق إرسال أو عبء عمل مسموح به على سر إداري على مستوى الحساب. وافصل الإنتاج عن التطوير وCI. واجعل التدوير روتينيًا: جهّز بديلًا، وحدّث العامل، وأجرِ اختبار تسليم مضبوطًا، وأكّد أدلة المصادقة والنتيجة، ثم أبطل بيانات الاعتماد القديمة. وقيّد الوصول إلى الأسرار بعملية الإرسال ودقّق عمليات القراءة الإدارية. تجرّب طريقة login في Python آليات المصادقة التي يعلن عنها الخادم؛ ولا يزال على التطبيق أن يقرر ما إذا كان الخادم وأمان الاتصال والحساب والآلية مقبولة. وينبغي أن تُوقف إخفاقات المصادقة المتكررة الفوج المعني وتستدعي التحقيق بدلًا من إعادة محاولة كلمة المرور بسرعة.

استخدم مهلات صريحة وعمر اتصال محدودًا

مرّر مهلة محدودة إلى SMTP أو SMTP_SSL حتى لا تستطيع عمليات الاتصال والحجب أن تشغل عاملًا إلى أجل غير مسمى. وطبّق موعدًا نهائيًا خارجيًا للمهمة وسياسة إلغاء لأن مهلة مقبس واحدة ليست ضبطًا كاملًا لعمر الطابور. ولا تحتفظ بكائن SMTP مشترك بين مهام متزامنة ما لم يكن الوصول متسلسلًا وثبتت سلامة حالته. يفتح التصميم البسيط اتصالًا واحدًا لدفعة محدودة، ويحيّي الخادم، ويُنشئ TLS عند الحاجة، ويصادق، ويرسل عددًا صغيرًا من الرسائل، ويستدعي quit، ثم يتخلص من الاتصال بعد الأخطاء أو بلوغ حدود العمر. وقد تقلل إعادة الاستخدام الكلفة، لكنها تزيد الالتباس بعد انقطاعات الخادم أو المهلات أو الحالة الجزئية. حدّد عدد الرسائل لكل اتصال وأعد الاتصال عن قصد. وراقب زمن الاتصال وتفاوض TLS والمصادقة وزمن الأوامر وانقطاعات الخادم وعمر المهمة دون تسجيل بيانات الاعتماد أو محتوى الرسائل. وقد يفرض خادم SMTP حدودًا تتغير باستقلال عن Python.

أرسل رسالة واحدة واحتفظ بنتائج المستلِمين الجزئية

تستخدم SMTP.sendmail القيمتين from_addr وto_addrs لظرف النقل ولا تعيد كتابة ترويسات الرسالة. أما SMTP.send_message فتسلسل EmailMessage وتستنتج القيم الافتراضية ما لم تُمرَّر قيم ظرف صريحة. في شيفرة الإنتاج، مرّر مُرسِل الظرف وقائمة المستلِمين المعتمدين صراحةً ليبقى التعامل مع Bcc وتفويض المستأجر غير ملتبس. توثّق Python أن sendmail تعود بشكل طبيعي عندما يُقبل مستلِم واحد على الأقل وتعيد قاموسًا لكل مستلِم مرفوض. لذلك فإن مسار الاستثناء الفارغ لا يعني نجاح جميع المستلِمين. خزّن نطاقات المستلِمين المقبولين والمرفوضين على حدة، بما في ذلك رمز الحالة والتشخيص المنقّى. ولا تعِد محاولة المستلِمين المقبولين عندما رُفض بعضهم فقط. وتعامل مع كل مستلِم بوصفه نتيجة مفوَّضة باستقلال مع الاحتفاظ بمحاولة الرسالة المشتركة. واستثناء لاحق في مرحلة DATA يختلف عن رفض RCPT ويحتاج إلى تصنيفه الخاص.

صنّف الاستثناءات بحسب المرحلة والديمومة

تعامل مع استثناءات smtplib صراحةً واحتفظ برموز SMTP فيها ورسائل الخادم المنقّاة. قد يكون SMTPConnectError والمهلة مؤقتين، لكنهما قد يكشفان أيضًا مضيفًا أو منفذًا خاطئًا أو جدارًا ناريًا أو انقطاعًا. وينبغي أن يوقف SMTPNotSupportedError بعد STARTTLS أو SMTPUTF8 إعدادًا يشترط الميزة. ويتطلب SMTPAuthenticationError التحقيق في بيانات الاعتماد والحساب والآلية وTLS، لا إعادة محاولة عمياء. ويتطلب SMTPSenderRefused وSMTPRecipientsRefused قرارات على مستوى الهوية أو المستلِم. ويصف SMTPDataError استجابة DATA غير متوقعة وقد يمثل سلوك محتوى أو سياسة أو حصة أو سلوك مستقبِل مؤقتًا بحسب حالته المحسَّنة. صنّف استجابات 4xx مرشحةً لإعادة محاولة محدودة واستجابات 5xx دائمة لتلك المحاولة، مع مراعاة وثائق المزوّد الخاصة. واستخدم تراجعًا أُسّيًا وتشويشًا عشوائيًا (jitter) وسقوفًا للمحاولات وعمر الطابور وحالة dead-letter. ولا تعِد المحاولة أبدًا بعد أدلة منع أو شكوى أو إلغاء اشتراك أو تفويض مُبطَل أو مستلِم غير صالح.

طابِق نتائج الإرسال الملتبسة

انتهاء مهلة الشبكة أو الانقطاع بعد أن أرسل العميل بيانات الرسالة وقبل أن يلاحظ الرد النهائي من الخادم أمر ملتبس. فقد يكون الخادم قد قبل المسؤولية رغم أن Python رفعت استثناءً. لا تنشئ فورًا إرسالًا منطقيًا جديدًا. علّم المحاولة بأنها غير معروفة، وأبقِ معرّفات الحدث والتتبع الثابتة لها، واستعلم عن سجلات المزوّد أو أحداث التسليم اللاحقة عند توفرها. وإذا لم توفّر خدمة SMTP عدم تكرار ولا ارتباطًا قابلًا للبحث، فحدّد قرارًا للمنتج بناءً على فئة الرسالة وعمرها وضرر التكرار وتجربة المستخدم. فلتنبيهات الأمان ورسائل إعادة تعيين كلمة المرور مخاطر تكرار مختلفة عن الإيصالات أو الإشعارات المالية. احتفظ بالمحاولة الأصلية وأي رابط لإعادة المحاولة في السجل. ولا تدّعِ أبدًا التسليم مرة واحدة تمامًا (exactly-once) لأن SMTP لا يوفّره من طرف إلى طرف. واختبر هذا الفرع بعينة خادم مضبوطة تقطع الاتصال في كل مرحلة من البروتوكول، بما في ذلك قبل قبول DATA وبعده.

افصل قبول SMTP عن التسليم والتفاعل

يعني نجاح استدعاء send_message أن مستلِمًا واحدًا على الأقل قُبل في مرحلة SMTP المرصودة بحسب دلالات Python الموثَّقة. ولا يثبت أن كل المستلِمين قُبلوا، ولا أن الخادم الوجهة احتفظ بالرسالة لاحقًا، ولا أنها وصلت إلى مجلد صندوق الوارد، ولا أن شخصًا قرأها. نمذج التسليم إلى المزوّد وقبول خادم المستلِم والإخفاق المؤقت أو الدائم والارتداد اللاحق والشكوى وإلغاء الاشتراك وموضع صندوق البريد والتفاعل بوصفها أدلة منفصلة. واستقبل أحداث المزوّد الموثَّقة عند توفرها، وأزل تكرارها، واحتفظ بوقت الحدوث منفصلًا عن وقت المعالجة. وطبّق الارتدادات الدائمة والشكاوى وإلغاءات الاشتراك مباشرةً قبل الإرسال اللاحق. أما الفتحات والنقرات فليست دليل نقل ويمكن أن تتأثر بتقنيات الخصوصية. واحتفظ بمقاييس مجمّعة تقلل الخصوصية بحسب فوج آمن للمستأجر ومراجعة القالب ونطاق المُرسِل وفئة الحالة والوقت. ونبّه عند ارتفاعات الرفض والنتائج غير المعروفة وعمر الطابور وإخفاقات TLS وإخفاقات المصادقة وتوزيع المستلِمين غير المعتاد.

اختبر محليًا دون إرسال بريد عملاء حقيقي

اختبر وحديًا إنشاء الرسالة، ورفض حقن الترويسة، وتفويض المستلِم، وإزالة Bcc، والبدائل النصية العادية وHTML، ومعالجة Unicode، وحدود المرفقات، وفحوص المنع. استخدم خادم اختبار SMTP محليًا مضبوطًا أو fixture للبروتوكول لمحاكاة إخفاقات التحية وغياب STARTTLS وفشل الشهادة وأخطاء المصادقة وقبول RCPT الجزئي وردود DATA من 4xx و5xx وقطع الاتصال والاستجابات المتأخرة. لا تستخدم خدمات تصحيح قديمة غير مصادَق عليها لأسرار شبيهة ببيئة الإنتاج أو محتوى العملاء. ينبغي أن تستخدم اختبارات التكامل حسابات مخصصة ومستلِمين مضبوطين مع حصص وتنظيف صريحين. تحقق من الرسالة الخام المستلمة ونتائج المصادقة والترويسات الظاهرة وسلوك الرد وارتباط الأحداث. شغّل فحص الأسرار عبر fixtures والسجلات.

كيف ينسجم SendHQ

يوثق SendHQ واجهة API للبريد الإلكتروني على مستوى مساحة العمل لإرسال النطاقات الموثقة وأحداث التسليم وقوائم المنع. يغطي هذا الدليل عميل SMTP في المكتبة القياسية لـ Python؛ استخدم وثائق SendHQ لأساليب التكامل الحالية وعقد API الخاص به.

الأسئلة الشائعة

هل ينبغي وضع بيانات اعتماد Python SMTP في شيفرة العميل؟

لا. أبقِها في مدير أسرار على الخادم بنطاق ضيق للبيئة وعبء العمل، مع وصول مدقَّق وتدوير روتيني ودون تسجيل.

متى ينبغي أن تستخدم Python الدالة SMTP_SSL؟

استخدم SMTP_SSL عندما يكون TLS مطلوبًا منذ بدء الاتصال. واستخدم SMTP مع starttls فقط لسير عمل ترقية صريحة موثَّق يفشل بشكل مغلق.

هل ينبغي استدعاء EHLO مرة أخرى بعد starttls؟

نعم. تنص وثائق smtplib في Python على استدعاء ehlo مرة أخرى بعد starttls ليُعاد اكتشاف الإمكانات داخل الاتصال المحمي.

هل تعني send_message أن كل المستلِمين قُبلوا؟

لا. يمكن أن تعود Python بشكل طبيعي عندما يُقبل مستلِم واحد على الأقل، وتعيد المستلِمين المرفوضين على حدة. احفظ نتائج المستلِمين وعالجها باستقلال.

ماذا ينبغي أن يحدث بعد SMTPAuthenticationError؟

أوقف الإعداد المتأثر مؤقتًا وافحص TLS والخادم والحساب والسر والآليات المعلن عنها. قد تضخّم إعادات محاولة بيانات الاعتماد العمياء حالات القفل أو إشارات الاختراق.

هل ينبغي إعادة محاولة كل SMTPDataError؟

لا. احتفظ بالحالة والتشخيص بدقة، ثم ميّز بين حالات 4xx المؤقتة وإخفاقات 5xx الدائمة في السياسة أو المحتوى أو الحصة أو الإعداد.

هل يحدد قبول SMTP الوصول إلى صندوق الوارد؟

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

هل يغطي هذا الدليل تكاملًا خاصًا بـ SendHQ؟

لا. يغطي عميل SMTP في المكتبة القياسية لـ Python. راجع وثائق SendHQ لأساليب التكامل الحالية وعقد API الخاص به.

المصادر