อีเมลขาเข้า · 22 กันยายน 2026

วิธีรับอีเมลด้วย Amazon SES: S3 และ Lambda

สร้างไปป์ไลน์อีเมลขาเข้าพร้อมใช้งานจริงด้วย Amazon SES, S3 และ Lambda รวมถึงความปลอดภัยของ MIME การกำหนดเส้นทางตามผู้เช่า idempotency การลองใหม่ และการจัดเธรด

เส้นทางขาเข้าของ Amazon SES ที่สั้นและเชื่อถือได้ที่สุดคือ ยืนยันโดเมน ชี้เรคคอร์ด MX ไปยัง endpoint รับของ SES เก็บทุกข้อความที่ยอมรับลง S3 แล้วเรียก Lambda แบบอะซิงโครนัสเพื่อแยกวิเคราะห์และบันทึกข้อความ ให้วาง action ของ S3 ไว้ก่อน action ของ Lambda ใช้ SES message ID เป็น idempotency key ใช้ผู้รับใน SMTP envelope สำหรับการกำหนดเส้นทาง และกักกันเนื้อหาที่ไม่ปลอดภัยแทนที่จะเชื่อเฮดเดอร์หรือไฟล์แนบ

สถาปัตยกรรมที่ควรสร้าง

ใช้ซับโดเมนเฉพาะ เช่น inbound.example.com เว้นแต่ SES ควรรับอีเมลทั้งหมดของโดเมนหลักของคุณ วิธีนี้แยกอีเมลของแอปพลิเคชันออกจากกล่องจดหมายของพนักงาน และทำให้การย้อนกลับเป็นแค่การเปลี่ยน DNS แทนที่จะเป็นการย้ายระบบอีเมล

ขั้นตอนของระบบจริงคือ

sender -> SES inbound SMTP endpoint -> active SES receipt rule -> S3 raw-message object -> asynchronous Lambda action -> MIME parser and policy checks -> application database and private attachment storage

receipt rule ของ Amazon SES ทำงานตาม action ตามลำดับ AWS ระบุรูปแบบ S3 ก่อน Lambda ทีหลังไว้ในเอกสารอย่างชัดเจนสำหรับกรณีที่โค้ดต้องใช้เนื้อหาข้อความ action Lambda โดยตรงได้รับเมทาดาทาและเฮดเดอร์บางส่วน ไม่ใช่เนื้อหาทั้งหมด เนื้อหายังอยู่ในออบเจ็กต์ MIME ดิบบน S3 (แนวคิดการรับอีเมลของ AWS SES)

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

1. เลือกภูมิภาคที่รองรับและยืนยันโดเมน

การรับอีเมลของ SES มีให้เฉพาะบางภูมิภาคของ AWS เลือกหนึ่งภูมิภาคจากรายการ endpoint รับของ SESปัจจุบัน แล้วเก็บทรัพยากร SES, Lambda, SNS และ KMS ไว้ในภูมิภาคนั้น เว้นแต่เอกสาร AWS ที่เกี่ยวข้องจะอนุญาตเป็นอย่างอื่นอย่างชัดเจน

สร้าง SES domain identity สำหรับโดเมนหลักหรือซับโดเมนที่จะรับอีเมลอย่างตรงตัว การยืนยันโดเมนต้องเผยแพร่เรคคอร์ด DNS ที่ SES ให้มา การยืนยันสำหรับการรับพิสูจน์ว่าคุณควบคุมโดเมน ซึ่งแยกจากการตั้งค่าเส้นทาง MX ที่ส่งทราฟฟิกขาเข้าไปยัง SES (คู่มือการยืนยันโดเมนของ AWS)

สำหรับซับโดเมนเฉพาะ เรคคอร์ด DNS จะมีลักษณะคร่าว ๆ ดังนี้

inbound.example.com. MX 10 inbound-smtp.us-east-1.amazonaws.com.

แทนที่ us-east-1 ด้วยภูมิภาคที่คุณเลือก AWS ระบุค่า MX เป็น 10 inbound-smtp.<region>.amazonaws.com (คู่มือเรคคอร์ด MX ของ AWS) อย่าชี้โดเมนหลักของคุณมาที่ SES หากยังมีคนรับอีเมลที่นั่นผ่าน Google Workspace, Microsoft 365 หรือผู้ให้บริการกล่องจดหมายรายอื่น

ตรวจสอบเรคคอร์ดที่เผยแพร่จาก resolver มากกว่าหนึ่งตัวก่อนทดสอบ

dig MX inbound.example.com +short

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

2. เก็บข้อความดิบก่อนประมวลผล

สร้างบัคเก็ต S3 แบบส่วนตัวที่บล็อกการเข้าถึงสาธารณะ มี lifecycle policy และสิทธิ์ IAM ที่แคบที่สุดเท่าที่ทำได้ จากนั้นสร้าง receipt rule ของ SES ที่มีเงื่อนไขผู้รับตรงกับโดเมนขาเข้าหรือที่อยู่เฉพาะของคุณ

action แรกควรส่งข้อความดิบไปเก็บที่ S3 พรีฟิกซ์ของออบเจ็กต์ เช่น inbound/ ช่วยกำหนดขอบเขตกฎการเก็บรักษาและนโยบายการเข้าถึงได้ง่ายขึ้น SES เก็บเนื้อหา MIME ดิบที่ไม่ถูกแก้ไข ปัจจุบัน AWS ระบุค่าสูงสุดเริ่มต้น 40 MB เมื่อบันทึกข้อความลง S3 ขณะที่ action ของ SNS ที่รวมข้อความทั้งฉบับมีค่าสูงสุดน้อยกว่ามากที่ 150 KB (receipt action ของ S3 ใน AWS) ความต่างของขนาดนี้คือเหตุผลที่ S3 เป็นค่าเริ่มต้นที่ปลอดภัยกว่าสำหรับอีเมลตอบกลับและไฟล์แนบในโลกจริง

หากคุณเปิดการตั้งค่า KMS ของ SES แบบไม่บังคับบน receipt action ให้อ่านรายละเอียดการเข้ารหัสอย่างละเอียด SES ใช้การเข้ารหัสฝั่งไคลเอนต์สำหรับฟีเจอร์นั้น ไม่ใช่การเข้ารหัสฝั่งเซิร์ฟเวอร์ของ S3 ตามปกติ ดังนั้นตัวอ่านของคุณต้องถอดรหัสออบเจ็กต์ด้วยไคลเอนต์ที่เข้ากันได้ อย่าเปิดโดยไม่ไตร่ตรอง แล้วมาพบระหว่างเกิดเหตุว่าตัวแยกวิเคราะห์ Node ของคุณอ่านไบต์ที่เก็บไว้ไม่ได้

ให้สิทธิ์ SES เขียนได้เฉพาะบัคเก็ตและพรีฟิกซ์ที่ต้องการ ให้สิทธิ์ Lambda s3:GetObject เฉพาะตำแหน่งเดียวกันนั้น ฟังก์ชันไม่ต้องมีสิทธิ์ดูแลบัคเก็ต

3. เพิ่ม action Lambda แบบอะซิงโครนัส

วาง action Lambda ไว้หลัง action S3 ใน receipt rule และใช้การเรียกแบบอะซิงโครนัส เว้นแต่ฟังก์ชันต้องตัดสินว่า SES ควรประเมินกฎต่อหรือไม่ AWS แนะนำให้ประมวลผลแบบอะซิงโครนัสสำหรับงานปกติ และสงวนแบบซิงโครนัสไว้สำหรับการตัดสินใจเกี่ยวกับการไหลของอีเมล (receipt action ของ Lambda ใน AWS)

mail.messageId ที่ SES กำหนดยังเป็นคีย์ออบเจ็กต์ S3 เมื่อไม่ได้ตั้งพรีฟิกซ์ หากมีพรีฟิกซ์ ให้ใส่นำหน้า โครงตัวอย่าง Node.js ต่อไปนี้ดึงข้อความดิบมาและแยกวิเคราะห์ ให้แพ็กเกจ @aws-sdk/client-s3 และตัวแยกวิเคราะห์ MIME ที่ยังมีการดูแล เช่น mailparser ไปกับ artifact ที่ deploy และตรึงเวอร์ชันไว้

import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { simpleParser } from "mailparser"; const s3 = new S3Client({}); const bucket = process.env.INBOUND_BUCKET; const prefix = process.env.INBOUND_PREFIX || "inbound/"; export async function handler(event) { for (const record of event.Records || []) { const ses = record.ses; const messageId = ses?.mail?.messageId; const recipients = ses?.receipt?.recipients || []; if (!messageId || !/^[A-Za-z0-9._-]+$/.test(messageId)) { throw new Error("Missing or invalid SES message ID"); } // claimOnce must be an atomic insert with a unique constraint. if (!(await claimOnce(messageId))) continue; try { const object = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: `${prefix}${messageId}`, })); const raw = Buffer.from(await object.Body.transformToByteArray()); const parsed = await simpleParser(raw, { skipHtmlToText: true, skipTextToHtml: true, }); await saveInboundMessage({ providerMessageId: messageId, envelopeRecipients: recipients, envelopeFrom: ses.mail.source, headerMessageId: parsed.messageId || null, inReplyTo: parsed.inReplyTo || null, references: parsed.references || [], subject: parsed.subject || "", text: parsed.text || "", html: parsed.html || null, attachments: parsed.attachments, receivedAt: ses.mail.timestamp, }); await markComplete(messageId); } catch (error) { await releaseOrMarkFailed(messageId, String(error)); throw error; } } }

ฟังก์ชัน placeholder แทนการจัดเก็บเฉพาะของแอปพลิเคชัน แต่สัญญาของมันสำคัญ claimOnce ต้องใช้ unique constraint ของฐานข้อมูลหรือการเขียนแบบมีเงื่อนไขบน provider message ID การอ่านแล้วค่อย insert เกิด race ได้ เก็บสถานะการประมวลผลเพื่อให้ผู้ดูแลแยกแยะ processing, complete, quarantined และ failed ได้

กำหนดเส้นทางด้วยผู้รับใน envelope ไม่ใช่เฮดเดอร์ To

ฟิลด์ To และ Cc ที่มองเห็นเป็นเนื้อหาข้อความที่ผู้ส่งกำหนดเอง อาจไม่ตรงกับปลายทางจริงเพราะ BCC การส่งต่อ หรือการบิดเบือนโดยตั้งใจ เงื่อนไข receipt ของ SES ใช้ผู้รับใน SMTP envelope และ AWS บอกให้ตัวประมวลผลปลายทางใช้ผู้รับจากการแจ้งเตือนของ SES เมื่อตัดสินว่าข้อความถูกส่งไปที่ใด (แนวคิดการรับอีเมลของ AWS)

ความต่างนี้ป้องกันบั๊กข้ามผู้เช่า หาก reply+tenant-a@inbound.example.com ได้รับข้อความที่ To ซึ่งมองเห็นระบุ tenant-b@example.com ให้กำหนดเส้นทางโดยใช้การแมปของแอปพลิเคชันที่ผ่านการยืนยันตัวตนสำหรับที่อยู่ใน envelope ไม่ใช่เฮดเดอร์ที่แสดง

ใช้โทเค็นตอบกลับแบบสุ่มที่เดาไม่ได้เมื่อที่อยู่ระบุตัวลูกค้าหรือบทสนทนา แฮชโทเค็นตอนเก็บ ทำให้หมดอายุเมื่อเหมาะสม และปฏิเสธที่อยู่ที่ไม่แมปกับเวิร์กสเปซที่ใช้งานอยู่ local part ที่เดาได้ เช่น ticket-42 เป็นการเชิญให้แทรกข้อความเข้าไปในเธรดของผู้ใช้อื่น

แยกวิเคราะห์ MIME เหมือนเป็นอินพุตที่ไม่ปลอดภัย

อีเมลเป็นรูปแบบอินพุตซ้อนกันที่มีมาหลายทศวรรษ RFC 5322 กำหนดเฮดเดอร์และเนื้อหาของข้อความ ขณะที่ MIME เพิ่มเนื้อหาแบบ multipart และการเข้ารหัสสำหรับการส่ง (RFC 5322, RFC 2045) ใช้ตัวแยกวิเคราะห์ที่ยังมีการดูแล แทนที่จะแยกที่บรรทัดว่างหรือ boundary เอง

กำหนดขีดจำกัดก่อนเปิดเนื้อหาให้ผลิตภัณฑ์ใช้

  • จำกัดจำนวนไบต์รวมหลังถอดรหัส จำนวนไฟล์แนบ ขนาดไฟล์แนบแต่ละไฟล์ ความลึกของการซ้อน MIME และเวลาในการแยกวิเคราะห์
  • เก็บไฟล์แนบเป็นส่วนตัวโดยใช้ชื่อออบเจ็กต์ที่สร้างขึ้นเอง ห้ามใช้ชื่อไฟล์ของผู้ส่งเป็นพาธ
  • มองประเภทเนื้อหาและชื่อไฟล์ที่ระบุเป็นเพียงคำใบ้ ตรวจจับประเภทจากเนื้อหาจริงเมื่อทำได้
  • ห้ามรันเนื้อหาของไฟล์แนบ สแกนหรือกักกันไฟล์แนบก่อนให้ดาวน์โหลด
  • ล้าง HTML ด้วย allowlist ที่เข้มงวด บล็อกรูปภาพระยะไกลโดยค่าเริ่มต้น และแสดงผลในบริบทที่แยกออกมา ควรใช้ข้อความล้วนสำหรับการวิเคราะห์อัตโนมัติ
  • อย่าใส่เนื้อหาดิบ ที่อยู่ โทเค็น หรือเนื้อหาไฟล์แนบลงใน log ของแอปพลิเคชันทั่วไป

SES รายงานผล SPF, DKIM, DMARC, สแปม และไวรัสได้ แต่ AWS ระบุว่า SES เปิดเผยผลเหล่านี้ ไม่ได้บังคับใช้นโยบายทางธุรกิจของคุณให้อัตโนมัติ ให้ตัดสินว่าความล้มเหลวควรถูกปฏิเสธ กักกัน หรือแสดงพร้อมคำเตือน ผลการยืนยันตัวตนที่ผ่านระบุโดเมนภายใต้กลไกเฉพาะ แต่ไม่ได้ทำให้เนื้อหาปลอดภัยและไม่ได้พิสูจน์ว่าคนเป็นผู้เขียน

ทำให้การลองใหม่เป็นเรื่องธรรมดา

การเรียก Lambda แบบอะซิงโครนัสลองใหม่ฟังก์ชันที่ล้มเหลวได้ และ AWS เตือนว่าอาจส่งซ้ำได้แม้ฟังก์ชันไม่ได้คืนข้อผิดพลาด ให้ตั้ง on-failure destination หรือ dead-letter queue และตั้งการเตือนเมื่อการประมวลผลล้มเหลว (พฤติกรรมการลองใหม่ของ AWS Lambda)

idempotency ควรครอบคลุมผลข้างเคียงปลายทางทุกอย่าง

  1. Insert SES message ID ภายใต้ unique constraint
  2. บันทึกเนื้อหาที่แยกวิเคราะห์แล้วและลิงก์เธรดในทรานแซกชันเดียวเมื่อเป็นไปได้
  3. ใส่การแจ้งเตือน การสร้างตั๋ว หรืองานของเอเจนต์ไว้ใน outbox ที่ใช้ message ID บวกประเภท action เป็นคีย์
  4. ทำเครื่องหมายว่าเสร็จสมบูรณ์หลังการเขียนแบบคงทนสำเร็จแล้วเท่านั้น
  5. ประมวลผลซ้ำจากออบเจ็กต์ S3 ต้นฉบับ ไม่ใช่จากรายการ log ที่สูญเสียข้อมูล

หากคุณเรียกการประมวลผลจากการแจ้งเตือนของ S3 แทน action Lambda ของ SES กฎเดียวกันก็ใช้ได้ การแจ้งเตือนของ Amazon S3 ออกแบบมาให้ส่งแบบ at-least-once และไม่รับประกันว่าจะมาตามลำดับ (การแจ้งเตือนอีเวนต์ของ AWS S3)

จัดเธรดข้อความโดยไม่เชื่อหัวเรื่อง

ใช้ฟิลด์ Message-ID, In-Reply-To และ References ที่แยกวิเคราะห์แล้วเพื่อเสนอเธรดที่ตรงกัน อย่าจัดเธรดโดยดูจากหัวเรื่องที่ขึ้นต้นด้วย Re: เพียงอย่างเดียว และยืนยันด้วยว่าที่อยู่ใน envelope หรือโทเค็นตอบกลับเป็นของเวิร์กสเปซและบทสนทนาเดียวกันก่อนเชื่อมโยงสิ่งใด

การตอบกลับอัตโนมัติต้องมีนโยบายแยกต่างหาก ตรวจจับสัญญาณอย่าง Auto-Submitted และหลีกเลี่ยงการสร้างลูปการตอบกลับ RFC 3834 แนะนำให้ระบุตัวตนให้ชัดเจนและทำงานอย่างระมัดระวังสำหรับการตอบกลับอัตโนมัติ (RFC 3834) หากเอเจนต์ AI ร่างคำตอบ ให้ถือว่าการส่งเป็นผลข้างเคียงที่ชัดเจนและ idempotent ต้องได้รับการอนุมัติจากผู้ใช้สำหรับผู้รับที่ไม่คาดคิด เนื้อหาที่ละเอียดอ่อน หรือการกระทำนอกเวิร์กโฟลว์ซัพพอร์ตหรือผลิตภัณฑ์เดิม การได้รับข้อความไม่ใช่ความยินยอมโดยรวมสำหรับการตลาดที่ไม่เกี่ยวข้อง

รายการตรวจสอบสำหรับระบบจริง

  • Region ที่ใช้รับอีเมลรองรับอีเมลขาเข้าของ SES
  • มีการยืนยันตัวตนโดเมนแล้ว และเรคคอร์ด MX resolve ได้ถูกต้อง
  • กฎการรับมีเงื่อนไขผู้รับที่จำกัด และชุดกฎที่ตั้งใจไว้ทำงานอยู่
  • การดำเนินการ S3 ทำงานก่อนการประมวลผล Lambda แบบอะซิงโครนัส
  • Bucket เป็นแบบ private การเข้าถึงเป็นไปตามหลักสิทธิ์น้อยที่สุด และมีการจัดทำเอกสาร retention
  • message ID ของ SES มี uniqueness constraint ในฐานข้อมูล
  • การกำหนดเส้นทางใช้ผู้รับใน envelope ไม่ใช่เฮดเดอร์ To หรือ Cc ที่มองเห็น
  • MIME, HTML, ลิงก์ และไฟล์แนบ ถูกจัดการเป็น input ที่ไม่น่าเชื่อถือ
  • อีเวนต์ที่ล้มเหลวไปถึงปลายทางที่มีการติดตาม และสามารถนำมาประมวลผลซ้ำได้
  • การจับคู่เธรดบังคับใช้ความเป็นเจ้าของเวิร์กสเปซ
  • การตอบกลับอัตโนมัติมีการป้องกัน loop ขอบเขตความยินยอม และ idempotency ของการส่ง
  • การทดสอบแบบควบคุมครอบคลุมข้อความล้วน HTML BCC การส่งซ้ำ ไฟล์แนบขนาดใหญ่ MIME ที่ผิดรูปแบบ และ parser ล้มเหลว

SES โดยตรงเหมาะเมื่อทีมของคุณต้องการการควบคุมแบบ AWS-native และพร้อมรับผิดชอบเรื่อง DNS, IAM, การแยกวิเคราะห์ MIME, การแยกผู้เช่า, การเก็บรักษา, การจัดการการลองใหม่ และการแจ้งเตือนเชิงปฏิบัติการ หากต้องการให้พื้นฐานเหล่านี้อยู่หลัง Email API ที่แคบลง SendHQ มีที่อยู่ขาเข้า ข้อความที่เก็บไว้ เธรด และการเข้าถึงระดับเวิร์กสเปซ ควบคู่กับอีเมล transactional ขาออก ไม่ว่าเลือกทางใด ให้เก็บข้อความดิบไว้กู้คืนได้ และทำให้ทุก action ปลายทางเล่นซ้ำได้อย่างปลอดภัย