คู่มือ · python3 smtp

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

ใช้ Python 3 SMTP ผ่าน worker ฝั่งเซิร์ฟเวอร์ที่ได้รับอนุญาต ไม่ใช่ในเบราว์เซอร์หรือโค้ดที่ผู้ใช้ควบคุม สร้างข้อความด้วย EmailMessage แยกผู้รับใน envelope ออกจากเฮดเดอร์ที่มองเห็น สร้าง SSL context ที่ตรวจสอบแล้ว ตั้งค่าหมดเวลาการเชื่อมต่อแบบจำกัด และใช้ SMTP_SSL เพื่อใช้ TLS ตั้งแต่เริ่มเชื่อมต่อ หรือ SMTP.starttls() ตามด้วย EHLO สำหรับการอัปเกรดแบบชัดเจน โหลดข้อมูลรับรองจาก secret manager เรียก send_message() ตรวจสอบผลผู้รับที่ถูกปฏิเสธ และบันทึกผลของความพยายามที่แน่นอน ลองใหม่เฉพาะความล้มเหลวชั่วคราวด้วย backoff แบบมีขอบเขต และอย่าถือว่าการที่ SMTP ยอมรับเป็นหลักฐานการเข้ากล่องจดหมาย

กำหนดการดำเนินการอีเมลที่ได้รับอนุญาตหนึ่งอย่าง

เริ่มจากอีเวนต์ของผลิตภัณฑ์ที่ได้รับอนุมัติ เช่น การยืนยันบัญชี ใบเสร็จ การแจ้งเตือนที่ร้องขอ หรือการแจ้งเตือนด้านความปลอดภัย จัดเก็บงานขาออกที่คงทนก่อนเปิดการเชื่อมต่อ SMTP งานนั้นควรมี business-event key ที่คงที่ ผู้เช่า ประเภทข้อความ revision ของเทมเพลต ผู้ส่งและผู้รับใน envelope ที่ได้รับอนุมัติ ตัวตน From ที่มองเห็น ฐานความยินยอมหรือความจำเป็น และผลการระงับการส่งปัจจุบัน อินพุตจากเบราว์เซอร์ มือถือ เทมเพลต และผู้ใช้ต้องไม่เลือกโฮสต์ SMTP ข้อมูลรับรอง ผู้ส่งใน envelope เฮดเดอร์ตามอำเภอใจ หรือผู้รับที่ไม่จำกัด อนุญาตผู้เรียกและผู้เช่า ตรวจสอบที่อยู่ จำกัดจำนวนผู้รับและไฟล์แนบ และป้องกัน newline injection อ้างสิทธิ์งานเพียงครั้งเดียวและเก็บประวัติความพยายามแบบ append-only smtplib ของ Python เป็นไคลเอนต์โปรโตคอล ไม่ได้ให้ idempotency ทางธุรกิจ การแยกผู้เช่า ความยินยอม การระงับการส่ง หรือคิวที่คงทน การควบคุมเหล่านั้นเป็นหน้าที่ของแอปพลิเคชันที่ครอบอยู่

สร้างข้อความแบบมีโครงสร้างด้วย EmailMessage

ใช้ email.message.EmailMessage แทนการต่อสตริงเฮดเดอร์และ MIME ตั้งค่า From, To, Subject และเฮดเดอร์เชื่อมโยงของแอปพลิเคชันที่คงที่จากค่าที่ตรวจสอบแล้ว จากนั้นเรียก set_content สำหรับข้อความล้วน add_alternative สำหรับส่วน HTML เมื่อจำเป็น และ add_attachment เฉพาะประเภทและขนาดไฟล์ที่รองรับอย่างชัดเจน สร้างทั้งข้อความและ HTML จาก revision ของเทมเพลตที่ได้รับอนุมัติเดียวกัน escape ค่าที่ไม่น่าเชื่อถือตามบริบทของเอาต์พุต และหลีกเลี่ยงการเรนเดอร์ HTML ดิบของผู้ใช้ อย่าใส่ความลับ access token ข้อมูลส่วนบุคคลที่ไม่จำเป็น หรือคีย์ฐานข้อมูลภายในในเฮดเดอร์ หัวเรื่อง ฟิลด์ติดตาม หรือชื่อไฟล์แนบ แพ็กเกจ email serialize ตามนโยบายของมัน และอาจสร้าง MIME boundary ระหว่างการ flatten จึงควรลงลายเซ็นหรือแฮชรูปแบบที่ serialize สุดท้าย หากการควบคุมความสมบูรณ์ในภายหลังขึ้นกับไบต์ที่ตรงตัว แยก envelope ของ SMTP ออกต่างหาก เฮดเดอร์ To และ Cc ที่มองเห็นสื่อสารกับผู้อ่าน ส่วนรายชื่อผู้รับของ transport ควบคุมคำสั่ง RCPT TO

เลือก implicit TLS หรือ STARTTLS อย่างตั้งใจ

ใช้ SMTP_SSL เมื่อเซิร์ฟเวอร์ต้องการ TLS ตั้งแต่เริ่มการเชื่อมต่อ ใช้ SMTP สำหรับการเชื่อมต่อแบบ cleartext เฉพาะเมื่อขั้นตอนของเซิร์ฟเวอร์ที่มีเอกสารกำหนดให้ต้องอัปเกรดเป็น STARTTLS ทันที เอกสาร smtplib ของ Python ระบุว่า starttls ทำให้คำสั่ง SMTP ถัดไปอยู่ภายใน TLS และไคลเอนต์ควรเรียก ehlo อีกครั้งหลังจากนั้น อย่ายืนยันตัวตนก่อนการอัปเกรด TLS ที่จำเป็น สร้าง context ด้วย ssl.create_default_context เพื่อให้การตรวจสอบใบรับรองและการตรวจสอบชื่อโฮสต์ใช้ค่าเริ่มต้นฝั่งไคลเอนต์ที่ปลอดภัย และส่งชื่อโฮสต์เซิร์ฟเวอร์ที่คาดไว้ผ่านการเชื่อมต่อของไลบรารีตามปกติ ให้ถือว่าไม่รองรับ STARTTLS ใบรับรองล้มเหลว ชื่อโฮสต์ไม่ตรง หรือการเจรจา TLS ล้มเหลว เป็นเหตุให้หยุดทันทีเมื่อต้องเข้ารหัส อย่าปิดการตรวจสอบหรือใช้ context ที่ไม่ตรวจสอบแทนเพื่อให้ระบบจริงใช้งานได้ TLS ระดับ hop ปกป้องการเชื่อมต่อ SMTP ไม่ได้ปกป้องเนื้อหาข้อความที่จัดเก็บ การประมวลผลของผู้ให้บริการ การจัดเก็บของผู้รับ หรือกล่องจดหมายสุดท้าย

เก็บข้อมูลรับรองไว้ฝั่งเซิร์ฟเวอร์และจำกัดขอบเขต

โหลดชื่อผู้ใช้ รหัสผ่าน หรือโทเค็น SMTP ขณะรันไทม์จากบริการความลับที่มีการจัดการ ห้ามใส่ในระบบควบคุมซอร์ส เลเยอร์ Docker การตั้งค่าที่คอมมิตลง Git URL อาร์กิวเมนต์บรรทัดคำสั่ง เอาต์พุตดีบัก ระบบวิเคราะห์ รายงานข้อยกเว้น สแนปช็อตการทดสอบ โน้ตบุ๊ก ตั๋ว หรือ prompt ควรใช้ข้อมูลรับรองที่จำกัดขอบเขตเฉพาะสภาพแวดล้อม โดเมนผู้ส่ง หรือปริมาณงานที่ได้รับอนุญาต แทนความลับผู้ดูแลระดับบัญชี แยกระบบจริงออกจากการพัฒนาและ CI ทำให้การหมุนเวียนเป็นเรื่องปกติ คือเตรียมของทดแทน อัปเดต worker ทดสอบการส่งแบบควบคุม ยืนยันหลักฐานการยืนยันตัวตนและผลลัพธ์ แล้วจึงเพิกถอนข้อมูลรับรองเก่า จำกัดการเข้าถึงความลับให้เฉพาะกระบวนการส่ง และตรวจสอบการอ่านของผู้ดูแลระบบ เมธอด login ของ Python ลองกลไกการยืนยันตัวตนที่เซิร์ฟเวอร์ประกาศ แอปพลิเคชันยังต้องตัดสินใจว่าเซิร์ฟเวอร์ ความปลอดภัยของการเชื่อมต่อ บัญชี และกลไกนั้นยอมรับได้หรือไม่ การยืนยันตัวตนล้มเหลวซ้ำ ๆ ควรหยุดกลุ่มนั้นชั่วคราวและเริ่มการสืบสวน แทนการลองรหัสผ่านซ้ำอย่างรวดเร็ว

ใช้ timeout ที่ชัดเจนและจำกัดอายุการเชื่อมต่อ

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

ส่งหนึ่งข้อความและเก็บผลของผู้รับบางราย

SMTP.sendmail ใช้ from_addr และ to_addrs สำหรับ envelope ของ transport และไม่เขียนเฮดเดอร์ของข้อความใหม่ SMTP.send_message serialize EmailMessage และอนุมานค่าเริ่มต้น เว้นแต่ระบุค่า envelope อย่างชัดเจน สำหรับโค้ดระบบจริง ให้ส่งผู้ส่งและรายชื่อผู้รับใน envelope ที่ได้รับอนุมัติอย่างชัดเจน เพื่อให้การจัดการ Bcc และการอนุญาตผู้เช่าไม่กำกวม Python ระบุว่า sendmail คืนค่าตามปกติเมื่อมีผู้รับอย่างน้อยหนึ่งรายที่ได้รับการยอมรับ และคืนดิกชันนารีสำหรับผู้รับแต่ละรายที่ถูกปฏิเสธ ดังนั้นการไม่มีข้อยกเว้นไม่เท่ากับผู้รับทุกรายสำเร็จ จัดเก็บขอบเขตผู้รับที่ยอมรับและที่ปฏิเสธแยกกัน รวมรหัสสถานะและข้อมูลวินิจฉัยที่ทำความสะอาดแล้ว อย่าลองใหม่กับผู้รับที่ยอมรับแล้วเมื่อมีเพียงบางรายถูกปฏิเสธ ให้ถือว่าผู้รับแต่ละรายเป็นผลลัพธ์ที่ได้รับอนุญาตอย่างเป็นอิสระ โดยยังคงความพยายามส่งข้อความร่วมกัน ข้อยกเว้นในขั้น DATA ภายหลังต่างจากการปฏิเสธ RCPT และต้องมีการจัดประเภทของตัวเอง

จัดประเภทข้อยกเว้นตามขั้นตอนและความถาวร

จัดการข้อยกเว้นของ smtplib อย่างชัดเจน และเก็บรหัส SMTP กับข้อความจากเซิร์ฟเวอร์ที่ทำความสะอาดแล้ว SMTPConnectError และ timeout อาจเป็นชั่วคราว แต่ก็อาจเปิดเผยโฮสต์ พอร์ต ไฟร์วอลล์ที่ผิด หรือระบบล่ม SMTPNotSupportedError หลัง STARTTLS หรือ SMTPUTF8 ควรหยุดการตั้งค่าที่ต้องใช้ฟีเจอร์นั้น SMTPAuthenticationError ต้องสืบสวนข้อมูลรับรอง บัญชี กลไก และ TLS ไม่ใช่ลองใหม่แบบไม่ดูสาเหตุ SMTPSenderRefused และ SMTPRecipientsRefused ต้องมีการตัดสินใจตามขอบเขตของตัวตนหรือผู้รับ SMTPDataError อธิบายการตอบกลับ DATA ที่ไม่คาดคิด และอาจหมายถึงเนื้อหา นโยบาย โควตา หรือพฤติกรรมชั่วคราวของผู้รับ ขึ้นกับสถานะแบบขยาย จัดประเภทการตอบกลับ 4xx เป็นตัวเลือกสำหรับการลองใหม่แบบมีขอบเขต และ 5xx เป็นถาวรสำหรับความพยายามนั้น โดยเคารพเอกสารเฉพาะของผู้ให้บริการ ใช้ exponential backoff jitter เพดานจำนวนครั้งและอายุคิว และสถานะ dead-letter อย่าลองใหม่หลังมีหลักฐานการระงับการส่ง การร้องเรียน การยกเลิกการรับ การเพิกถอนการอนุญาต หรือผู้รับไม่ถูกต้อง

กระทบยอดผลการส่งที่กำกวม

การหมดเวลาของเครือข่ายหรือการตัดการเชื่อมต่อหลังจากไคลเอนต์ส่งข้อมูลข้อความแล้วแต่ก่อนเห็นการตอบกลับสุดท้ายของเซิร์ฟเวอร์เป็นสถานะกำกวม เซิร์ฟเวอร์อาจรับผิดชอบข้อความไปแล้วแม้ Python จะยกข้อยกเว้น อย่าสร้างการส่งเชิงตรรกะใหม่ทันที ให้ทำเครื่องหมายความพยายามเป็นไม่ทราบ คงตัวระบุอีเวนต์และการติดตามที่คงที่ และค้นหาบันทึกของผู้ให้บริการหรืออีเวนต์การส่งถึงภายหลังเมื่อมี หากบริการ SMTP ไม่มี idempotency หรือการเชื่อมโยงที่ค้นหาได้ ให้กำหนดการตัดสินใจของผลิตภัณฑ์ตามประเภทข้อความ อายุ ความเสียหายจากการซ้ำ และประสบการณ์ผู้ใช้ การแจ้งเตือนความปลอดภัยและข้อความรีเซ็ตรหัสผ่านมีความเสี่ยงจากการซ้ำต่างจากใบเสร็จหรือประกาศทางการเงิน เก็บความพยายามเดิมและลิงก์การลองใหม่ไว้ในสมุดบัญชี อย่าอ้างว่าส่งถึงแบบ exactly-once เพราะ SMTP ไม่ได้ให้ end-to-end ทดสอบสาขานี้ด้วย fixture เซิร์ฟเวอร์แบบควบคุมที่ตัดการเชื่อมต่อในทุกขั้นตอนของโปรโตคอล รวมถึงก่อนและหลังการยอมรับ DATA

แยกการยอมรับของ SMTP ออกจากการส่งถึงและการมีส่วนร่วม

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

ทดสอบในเครื่องโดยไม่ส่งเมลลูกค้าจริง

ทำ unit test การสร้างข้อความ การปฏิเสธ header injection การอนุญาตผู้รับ การลบ Bcc เวอร์ชันข้อความล้วนและ HTML การจัดการ Unicode ขอบเขตไฟล์แนบ และการตรวจสอบการระงับการส่ง ใช้เซิร์ฟเวอร์ทดสอบ SMTP ภายในเครื่องแบบควบคุมหรือ protocol fixture เพื่อจำลอง greeting ล้มเหลว ไม่มี STARTTLS ใบรับรองล้มเหลว ข้อผิดพลาดการยืนยันตัวตน การยอมรับ RCPT บางส่วน การตอบกลับ DATA 4xx และ 5xx การตัดการเชื่อมต่อ และการตอบกลับล่าช้า อย่าใช้บริการ debug ที่ไม่ยืนยันตัวตนและเลิกใช้แล้วสำหรับความลับหรือเนื้อหาลูกค้าที่มีลักษณะเหมือนระบบจริง integration test ควรใช้บัญชีเฉพาะและผู้รับที่ควบคุมได้ พร้อมโควตาและการล้างข้อมูลอย่างชัดเจน ตรวจสอบข้อความดิบที่ได้รับ ผลการยืนยันตัวตน header ที่มองเห็น พฤติกรรมการตอบกลับ และความสัมพันธ์ของอีเวนต์ ทำ secret scanning กับ fixture และ log

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

SendHQ จัดทำเอกสาร Email API ระดับเวิร์กสเปซสำหรับการส่งจากโดเมนที่ยืนยันแล้ว อีเวนต์การส่ง และการระงับการส่ง คู่มือนี้ครอบคลุมไคลเอ็นต์ SMTP ใน standard library ของ Python ให้ใช้เอกสาร SendHQ สำหรับวิธีการเชื่อมต่อและ API contract ปัจจุบัน

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

ควรใส่ข้อมูลรับรอง Python SMTP ในโค้ดฝั่งไคลเอนต์หรือไม่

ไม่ ให้เก็บไว้ใน secret manager ฝั่งเซิร์ฟเวอร์ที่จำกัดขอบเขตสภาพแวดล้อมและปริมาณงาน มีการตรวจสอบการเข้าถึง หมุนเวียนเป็นประจำ และไม่บันทึกลงล็อก

เมื่อใดที่ Python ควรใช้ SMTP_SSL

ใช้ SMTP_SSL เมื่อต้องใช้ TLS ตั้งแต่เริ่มการเชื่อมต่อ ใช้ SMTP ร่วมกับ starttls เฉพาะเวิร์กโฟลว์การอัปเกรดแบบชัดเจนที่มีเอกสารและ fail closed

ควรเรียก EHLO อีกครั้งหลัง starttls หรือไม่

ใช่ เอกสาร smtplib ของ Python ระบุให้เรียก ehlo อีกครั้งหลัง starttls เพื่อให้ค้นพบความสามารถอีกครั้งภายในการเชื่อมต่อที่ได้รับการปกป้อง

send_message หมายความว่าผู้รับทุกรายได้รับการยอมรับหรือไม่

ไม่ Python อาจคืนค่าตามปกติเมื่อมีผู้รับอย่างน้อยหนึ่งรายที่ได้รับการยอมรับ และคืนผู้รับที่ถูกปฏิเสธแยกต่างหาก ให้บันทึกและจัดการผลลัพธ์ของผู้รับแต่ละรายอย่างเป็นอิสระ

ควรทำอย่างไรหลังเกิด SMTPAuthenticationError

หยุดการตั้งค่าที่ได้รับผลกระทบชั่วคราว และตรวจสอบ TLS เซิร์ฟเวอร์ บัญชี ความลับ และกลไกที่ประกาศไว้ การลองข้อมูลรับรองซ้ำโดยไม่ดูสาเหตุอาจขยายการล็อกบัญชีหรือสัญญาณการถูกบุกรุก

ควรลองใหม่กับ SMTPDataError ทุกรายการหรือไม่

ไม่ ให้เก็บสถานะและข้อมูลวินิจฉัยที่แน่นอน แล้วแยกสภาวะชั่วคราว 4xx ออกจากความล้มเหลวถาวร 5xx ด้านนโยบาย เนื้อหา โควตา หรือการตั้งค่า

การที่ SMTP ยอมรับกำหนดการเข้ากล่องจดหมายหรือไม่

ไม่ เป็นเพียงหลักฐานการขนส่งตามขอบเขต การรีเลย์ภายหลัง การกรองของผู้รับ อีเมลตีกลับ กฎกล่องจดหมาย การเข้าโฟลเดอร์ และการมีส่วนร่วมของมนุษย์ ยังเป็นผลลัพธ์แยกต่างหาก

คู่มือนี้ครอบคลุมการเชื่อมต่อเฉพาะ SendHQ หรือไม่

ไม่ คู่มือนี้ครอบคลุมไคลเอ็นต์ SMTP ใน standard library ของ Python โปรดดูเอกสาร SendHQ สำหรับวิธีการเชื่อมต่อและ API contract ปัจจุบัน

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