คู่มือ · smtp python

ทีมผลิตภัณฑ์ควรนำ SMTP ด้วย Python ไปใช้อย่างปลอดภัยได้อย่างไร

นำ SMTP ใน Python ไปใช้จาก background worker ที่ได้รับอนุญาต ไม่ใช่โดยตรงจากคำขอเว็บ สร้างข้อความด้วย EmailMessage ใช้ SMTP_SSL สำหรับ implicit TLS หรืออัปเกรดอย่างชัดเจนด้วย STARTTLS เมื่อสัญญาปัจจุบันของผู้ให้บริการกำหนด ยืนยันตัวตนด้วยความลับฝั่งเซิร์ฟเวอร์ และเรียก send_message พร้อม timeout ที่มีขอบเขต บันทึกงานก่อนเชื่อมต่อ เก็บหลักฐานการปฏิเสธรายผู้รับ กระทบยอดการตัดการเชื่อมต่อที่กำกวม และแยกการที่ SMTP ยอมรับออกจากการส่งถึงและการเข้ากล่องจดหมายในภายหลัง

อนุญาตและบันทึกการส่งก่อนใช้ SMTP

เริ่มจากอีเวนต์ที่ถูกต้องของแอปพลิเคชัน เช่น ใบเสร็จ การแจ้งเตือนความปลอดภัย การยืนยันที่ร้องขอ หรือประกาศบัญชี ยืนยันตัวตนของผู้เรียก และอนุญาต tenant ประเภทข้อความ ตัวตน From ที่มองเห็น ผู้รับ และ revision ของเทมเพลต เขียนงานขาออกที่ทนทานพร้อม event key ทางธุรกิจที่คงที่ ก่อนเปิดการเชื่อมต่อ SMTP ใด ๆ key นั้นควรป้องกันไม่ให้ worker สองตัวสร้างข้อความเชิงตรรกะเดียวกันอย่างอิสระ อินพุตจากเบราว์เซอร์ต้องไม่กำหนดโฮสต์ SMTP พอร์ต ชื่อผู้ใช้ envelope sender ผู้รับตามอำเภอใจ เฮดเดอร์ หรือนโยบาย TLS ให้เก็บค่าเหล่านั้นไว้ในการตั้งค่าฝั่งเซิร์ฟเวอร์ที่ผ่านการทบทวน queue worker ควรอ้างสิทธิ์งานหนึ่งรายการ ตรวจสอบการระงับการส่งและการอนุญาตอีกครั้ง ณ เวลาส่ง บันทึกแต่ละความพยายาม และปล่อยหรือปิดงานผ่านสถานะที่ชัดเจน ไลบรารี SMTP ของ Python ขนส่งข้อความที่เตรียมไว้ ไม่ได้ให้การอนุญาต tenant ความยินยอม idempotency หรือนโยบายการระงับการส่ง

สร้างข้อความด้วย EmailMessage

ใช้ email.message.EmailMessage แทนการต่อเฮดเดอร์และเนื้อหาดิบเข้าด้วยกัน ตั้งค่า From, To, Subject, Date และ Message-ID ที่สร้างขึ้นตามโมเดลที่แอปพลิเคชันอนุมัติ จากนั้นใช้ set_content สำหรับข้อความล้วนและ add_alternative สำหรับ HTML เมื่อจำเป็น ตรวจสอบออบเจ็กต์ที่อยู่ จำกัดจำนวนผู้รับและไฟล์แนบ ปฏิเสธการแทรกบรรทัดใหม่ในค่า และ escape ข้อมูลเทมเพลตให้เหมาะกับบริบทของผลลัพธ์ สร้างทั้งข้อความล้วนและ HTML จาก revision ของเทมเพลตที่ไม่เปลี่ยนแปลงเดียวกัน เก็บความลับและข้อมูลส่วนบุคคลที่ไม่จำเป็นออกจากหัวเรื่อง เฮดเดอร์กำหนดเอง ชื่อไฟล์ ฟิลด์วินิจฉัย และล็อก แยกเฮดเดอร์ From ที่มองเห็นออกจาก envelope sender ของ SMTP อย่างตั้งใจ เพราะการยืนยันตัวตนและการประมวลผล bounce อาจขึ้นกับตัวตนที่ต่างกัน เก็บ revision ของเนื้อหาหรือ hash ที่ปลอดภัยต่อความเป็นส่วนตัวไว้เพื่อตรวจสอบย้อนหลัง แทนการเก็บเนื้อหาข้อความทั้งหมดโดยไม่มีความจำเป็นที่กำหนดไว้

เลือก implicit TLS หรือ STARTTLS อย่างชัดเจน

Python ระบุ SMTP_SSL สำหรับการเชื่อมต่อที่เข้ารหัสตั้งแต่เริ่ม และ SMTP.starttls สำหรับการอัปเกรดการเชื่อมต่อที่สร้างแล้ว ให้ทำตามชื่อโฮสต์ พอร์ต ใบรับรอง และสัญญาการส่งเข้าปัจจุบันของผู้ให้บริการ แทนการเดาจากรายการพอร์ตทั่วไป สร้าง SSL context เริ่มต้นที่ยืนยันแล้ว และอย่าปิดการตรวจสอบใบรับรองหรือชื่อโฮสต์ สำหรับ STARTTLS ให้เชื่อมต่อ ส่ง EHLO ตามความจำเป็น เรียก starttls พร้อม context และส่ง EHLO อีกครั้ง เพราะส่วนขยายที่ประกาศอาจเปลี่ยนหลังการอัปเกรด ห้ามส่งข้อมูลรับรองหรือเนื้อหาข้อความของลูกค้าผ่านการเชื่อมต่อแบบ cleartext RFC 8314 แนะนำการส่งเข้าที่ป้องกันด้วย TLS และเลิกสนับสนุนการเข้าถึงแบบ cleartext ให้ถือว่าใบรับรองล้มเหลว ชื่อโฮสต์ไม่ตรง ไม่มี STARTTLS ที่จำเป็น หรือความสามารถเปลี่ยนโดยไม่คาดคิด เป็นความล้มเหลวเด็ดขาดที่ต้องสืบสวน แทนที่จะถอยกลับอย่างเงียบ ๆ

เก็บข้อมูลรับรอง SMTP ไว้ในขอบเขตความลับที่แคบ

โหลดชื่อผู้ใช้และรหัสผ่านหรือโทเค็นจากระบบจัดการความลับฝั่งเซิร์ฟเวอร์ขณะรัน ห้ามใส่ข้อมูลรับรองในซอร์สโค้ด bundle ฝั่งไคลเอนต์ การดัมพ์ตัวแปรสภาพแวดล้อม URL ร่องรอยข้อยกเว้น ระบบวิเคราะห์ โน้ตบุ๊ก ภาพหน้าจอ พรอมต์ หรือ fixture ที่คอมมิต จำกัดขอบเขตข้อมูลรับรองแต่ละชุดให้เล็กที่สุดตามสภาพแวดล้อมและ workload ที่ผู้ให้บริการรองรับ และแยกการพัฒนาออกจาก production ยืนยันตัวตนหลังสถานะ TLS ที่จำเป็นถูกสร้างขึ้นแล้วเท่านั้น ซ้อมการหมุนเวียนด้วยผู้รับที่ควบคุมได้ คือจัดเตรียมตัวแทนผ่านการบริหารที่อนุมัติ อัปเดต worker ยืนยันการยืนยันตัวตนและวงจรอีเวนต์ครบถ้วน แล้วเพิกถอนค่าเดิม การยืนยันตัวตนล้มเหลวซ้ำควรหยุดเส้นทางที่ได้รับผลกระทบ แทนที่จะเริ่มลูปลองใหม่ที่รวดเร็ว เมธอด login ของ Python เจรจาระหว่าง mechanism ที่เซิร์ฟเวอร์ประกาศ แต่ mechanism จริงของผู้ให้บริการ นโยบายบัญชี สิทธิ์ของโทเค็น และพฤติกรรมการหมุนเวียน ต้องมีหลักฐานปัจจุบัน

ใช้ฟังก์ชันส่งของ Python ที่มีขอบเขต

ทำ adapter ของผู้ให้บริการให้เล็ก และส่งหลักฐานแบบมีโครงสร้างกลับไปยัง state machine ของงาน ลำดับทั่วไปคือสร้าง SSL context เปิด SMTP_SSL(host, port, timeout=10) เป็น smtp สำหรับ implicit TLS เรียก smtp.login(username, secret) แล้วเรียก smtp.send_message(message, from_addr=envelope_from, to_addrs=recipients) สำหรับผู้ให้บริการที่ต้องอัปเกรดอย่างชัดเจน ให้ใช้ SMTP พร้อม timeout, ehlo, starttls(context=context), ehlo แล้วจึง login อย่านำชื่อโฮสต์หรือพอร์ตตัวอย่างมาเสนอเป็นค่าเริ่มต้นสากล ส่งรายการผู้รับที่ปรับให้เป็นมาตรฐานแล้ว แทนการพึ่งการแยกวิเคราะห์เฮดเดอร์ที่ไม่น่าเชื่อถือ เก็บคลาสข้อยกเว้น รหัสตอบกลับ SMTP และข้อความวินิจฉัยที่จำกัดเมื่อมี แต่ปกปิดที่อยู่ ข้อมูลรับรอง และเนื้อหาข้อความ วัดขั้นตอนการเชื่อมต่อ TLS การยืนยันตัวตน envelope data และ quit แยกกัน เพื่อให้ความล้มเหลวเชิงปฏิบัติการยังวินิจฉัยได้

ตีความผลรายผู้รับจาก send_message ให้แม่นยำ

Python ระบุว่า sendmail และ send_message คืนค่าตามปกติเมื่ออีเมลถูกยอมรับสำหรับผู้รับอย่างน้อยหนึ่งราย และคืน dictionary สำหรับผู้รับที่ถูกปฏิเสธ dictionary ว่างหมายความว่าไม่มีผู้รับถูกปฏิเสธในขั้นนั้น ให้เก็บผลรายผู้รับนั้นไว้ แทนการทำเครื่องหมายทั้งงานว่าส่งถึงแล้ว หากผู้รับทั้งหมดถูกปฏิเสธ ไลบรารีจะยกข้อยกเว้น SMTPRecipientsRefused ข้อยกเว้นอื่นแยกการปฏิเสธผู้ส่ง การปฏิเสธ DATA การยืนยันตัวตน การเชื่อมต่อ โปรโตคอล และข้อผิดพลาดที่เกี่ยวข้อง แมปหลักฐานที่แน่นอนเป็นสถานะของแอปพลิเคชัน คือ เซิร์ฟเวอร์ที่รับการส่งเข้ายอมรับ ปฏิเสธถาวร ปฏิเสธชั่วคราว หรือไม่ทราบ การคืนค่าตามปกติพิสูจน์เพียงผลการส่งเข้า SMTP ในขอบเขตนั้น ไม่ได้พิสูจน์การยอมรับของเซิร์ฟเวอร์ปลายทาง การเข้ากล่องจดหมายสุดท้าย การอ่าน หรือการมีส่วนร่วม ต้องเชื่อมโยง delivery status notification หรืออีเวนต์ของผู้ให้บริการในภายหลังแยกต่างหาก

ลองใหม่เมื่อควบคุมความเสี่ยงการส่งซ้ำได้เท่านั้น

จำแนกความล้มเหลวก่อนกำหนดความพยายามครั้งถัดไป ความล้มเหลวถาวรด้านที่อยู่ ผู้ส่ง การยืนยันตัวตน นโยบาย หรือเนื้อหา โดยทั่วไปต้องแก้ไขหรือระงับการส่ง ไม่ใช่ทำซ้ำอัตโนมัติ การตอบกลับ 4xx ชั่วคราวลองใหม่ได้ด้วย exponential backoff, jitter, เพดานจำนวนครั้ง การหมดอายุ และงบประมาณต่อปลายทาง การรีเซ็ตการเชื่อมต่อหรือ timeout หลังส่งข้อมูลข้อความอาจกำกวม เพราะเซิร์ฟเวอร์อาจยอมรับข้อความแล้วแต่ไคลเอนต์พลาดการตอบกลับสุดท้าย ให้คงความพยายามนั้นเป็นไม่ทราบ ตรวจสอบกิจกรรมของผู้ให้บริการหรืออีเวนต์ภายหลังผ่านการเชื่อมโยงที่ปลอดภัยต่อความเป็นส่วนตัว และหลีกเลี่ยงการส่งซ้ำทันทีแบบไม่ตรวจสอบ SMTP ไม่มี idempotency key ระดับแอปพลิเคชันที่เป็นสากล event key ทางธุรกิจที่ทนทานป้องกันความพยายามของแอปพลิเคชันที่เกิดพร้อมกันได้ แต่บังคับให้เซิร์ฟเวอร์ SMTP ระยะไกลตัดข้อมูลซ้ำของการส่งที่ยอมรับสองครั้งไม่ได้ ยกระดับผลลัพธ์กำกวมที่เกิดซ้ำ และเก็บหลักฐานที่ใช้ตัดสินใจไว้

จัดการผู้รับบางส่วนและการระงับการส่ง

เมื่อข้อความมีผู้รับหลายราย SMTP อาจยอมรับบางรายและปฏิเสธรายอื่น เก็บการตอบกลับของผู้รับแต่ละรายและเลื่อนเฉพาะส่วนที่ถูกยอมรับไปยังสถานะถัดไป อย่าส่งรายการเดิมทั้งหมดซ้ำเพียงเพราะที่อยู่หนึ่งได้รับการปฏิเสธชั่วคราว ใช้การระงับการส่งจาก bounce ถาวร การร้องเรียน การยกเลิกการรับ ข้อกำหนดทางกฎหมาย tenant และผู้ดูแลระบบ ก่อนแต่ละความพยายาม รวมถึงการลองใหม่ แยกประเภทข้อความผ่านนโยบายที่มีเอกสารอย่างชัดเจนเท่านั้น การติดป้ายว่าข้อความเป็น transactional ไม่ได้ลบความปลอดภัยของผู้รับหรือข้อจำกัดของผู้ให้บริการ ควรใช้งานที่มีผู้รับหนึ่งรายสำหรับเวิร์กโฟลว์ที่อ่อนไหว เมื่อความเป็นส่วนตัวและสถานะเฉพาะรายสมเหตุสมผลกับต้นทุน หลีกเลี่ยงการเปิดเผยรายชื่อผู้รับผ่าน To หรือ Cc และอย่าใช้พฤติกรรมของ Bcc แทนการอนุญาต จำกัดและปกปิดข้อความวินิจฉัย เพราะการตอบกลับ SMTP อาจมีที่อยู่ผู้รับหรือรายละเอียดเฉพาะของผู้รับ

ทดสอบเส้นทางความล้มเหลวด้วยระบบที่ควบคุมได้

ทดสอบการสร้างข้อความ Unicode ข้อความล้วนและ HTML สำรอง ไฟล์แนบ การปฏิเสธเฮดเดอร์ การปรับผู้รับให้เป็นมาตรฐาน การตรวจสอบ TLS ไม่มี STARTTLS ข้อมูลรับรองไม่ถูกต้อง การปฏิเสธผู้ส่ง การปฏิเสธผู้รับหนึ่งรายและทุกราย การปฏิเสธ DATA timeout ก่อนและหลังที่อาจถูกยอมรับ การตัดการเชื่อมต่อ การตอบกลับด้านอัตรา การหมดอายุของการลองใหม่ worker ซ้ำ การเปลี่ยนการระงับการส่ง และการหมุนเวียนความลับ ใช้บริการ SMTP ทดสอบที่ควบคุมได้หรือ fake ในเครื่องสำหรับ unit และ integration test ที่กำหนดผลได้ อย่าส่งทราฟฟิกของสภาพแวดล้อมระดับล่างที่หลุดไปยังที่อยู่ของลูกค้า ใน canary บน production ให้ใช้ผู้รับที่ได้รับอนุญาตและตรวจสอบเฮดเดอร์ดิบสำหรับ From ที่มองเห็น เส้นทาง envelope Message-ID, DKIM, SPF, DMARC alignment และหลักฐานของผู้ให้บริการ ยืนยันว่าล็อกและเมตริกไม่มีข้อมูลรับรองหรือเนื้อหาข้อความรั่วไหล ให้การเปิดตัวไม่ผ่านหาก worker ข้ามการอนุญาต tenant ลดระดับ TLS ลองใหม่โดยไม่มีขอบเขต ละเลยการปฏิเสธบางส่วน หรือหยุดเส้นทางการส่งไม่ได้

SendHQ เหมาะกับการใช้งานอย่างไร

SendHQ คือ Email API ระดับเวิร์กสเปซสำหรับการสื่อสารผลิตภัณฑ์ที่คาดหวัง เอกสารของ SendHQ ครอบคลุมการส่ง โดเมนที่ยืนยันแล้ว อีเวนต์การส่ง และการระงับการส่ง ใช้ HTTP API ที่จัดทำเอกสารไว้เมื่อนำ SendHQ ไปเชื่อมต่อกับ Python

คำถามที่พบบ่อย

Python ควรใช้ SMTP_SSL หรือ STARTTLS

ใช้โหมดที่สัญญาการส่งเข้าปัจจุบันของผู้ให้บริการกำหนด SMTP_SSL เข้ารหัสตั้งแต่เริ่มเชื่อมต่อ ส่วน STARTTLS อัปเกรดอย่างชัดเจนและต้องใช้ TLS ที่ยืนยันแล้วพร้อม EHLO ใหม่

send_message คืนค่าตามปกติพิสูจน์การส่งถึงหรือไม่

ไม่ หมายความเพียงว่ามีผู้รับอย่างน้อยหนึ่งรายถูกยอมรับในขั้นการส่งเข้า SMTP นั้น การยอมรับของปลายทาง การเข้ากล่องจดหมาย และการมีส่วนร่วม ต้องมีหลักฐานที่มีขอบเขตในภายหลัง

dictionary ที่ send_message คืนค่ามีความหมายว่าอะไร

เป็นการแมปผู้รับที่เซิร์ฟเวอร์ SMTP ปฏิเสธเข้ากับหลักฐานการตอบกลับ dictionary ว่างหมายความว่าไม่มีผู้รับถูกปฏิเสธในขั้นนั้น ไม่ได้หมายความว่าทุกข้อความเข้ากล่องจดหมาย

ลองใหม่ทันทีเมื่อ timeout ได้หรือไม่

ไม่ปลอดภัยเมื่อเกิดหลังจากที่อาจส่งเข้าไปแล้ว ให้เก็บความพยายามนั้นเป็นกำกวม กระทบยอดหลักฐานจากผู้ให้บริการหรืออีเวนต์ภายหลัง และส่งซ้ำภายใต้นโยบายความเสี่ยงการส่งซ้ำที่มีขอบเขตเท่านั้น

ควรเก็บรหัสผ่าน SMTP ไว้ที่ไหน

ใช้ระบบจัดการความลับฝั่งเซิร์ฟเวอร์ที่มีการเข้าถึงแคบตาม workload และสภาพแวดล้อม มีการตรวจสอบการดึงข้อมูล มีการหมุนเวียนที่ทดสอบแล้ว และไม่เปิดเผยต่อไคลเอนต์ ล็อก พรอมต์ หรือ fixture

ควรปิดการตรวจสอบใบรับรองใน production หรือไม่

ไม่ การที่ใบรับรองหรือชื่อโฮสต์ล้มเหลวเป็นหลักฐานของการตั้งค่าที่ไม่ปลอดภัยหรือไม่ถูกต้อง ให้หยุดเส้นทางและวินิจฉัย แทนการลดทอนการตรวจสอบ TLS อย่างเงียบ ๆ

ควรจัดการการปฏิเสธผู้รับบางส่วนอย่างไร

เก็บผลของผู้รับแต่ละราย เลื่อนเฉพาะส่วนที่ถูกยอมรับ และลองใหม่เฉพาะการปฏิเสธชั่วคราวที่เข้าเกณฑ์ อย่าส่งรายการเดิมทั้งหมดซ้ำไปยังผู้รับที่ถูกยอมรับแล้ว

หน้านี้พิสูจน์ว่า SendHQ รองรับ SMTP หรือไม่

ไม่ คู่มือนี้ครอบคลุม Python SMTP โดยทั่วไป ให้ใช้เอกสาร SendHQ ปัจจุบันสำหรับ Email API

แหล่งอ้างอิง