Eingehende E-Mails · 22. September 2026

E-Mails mit Amazon SES empfangen: S3 und Lambda

Bauen Sie eine produktionsreife Pipeline für eingehende E-Mails mit Amazon SES, S3 und Lambda – inklusive MIME-Sicherheit, Mandanten-Routing, Idempotenz, Wiederholungsversuchen und Threading.

Der kürzeste zuverlässige Weg für eingehende E-Mails mit Amazon SES: Domain verifizieren, einen MX-Eintrag auf einen SES-Empfangsendpunkt richten, jede angenommene Nachricht in S3 speichern und dann Lambda asynchron aufrufen, um sie zu parsen und zu persistieren. Setzen Sie die S3-Aktion vor die Lambda-Aktion. Behandeln Sie die SES-Nachrichten-ID als Idempotenzschlüssel, routen Sie anhand des Empfängers aus dem SMTP-Envelope und stellen Sie unsichere Inhalte unter Quarantäne, statt Headern oder Anhängen zu vertrauen.

Die Zielarchitektur

Verwenden Sie eine eigene Subdomain wie inbound.example.com, sofern SES nicht alle E-Mails für Ihre Stammdomain empfangen soll. So bleiben Anwendungs-E-Mails von den Postfächern Ihrer Mitarbeitenden getrennt, und ein Rollback ist eine DNS-Änderung statt einer Mail-Migration.

Der Ablauf im Produktivbetrieb:

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

Empfangsregeln (Receipt Rules) von Amazon SES führen ihre Aktionen der Reihe nach aus. AWS dokumentiert ausdrücklich das Muster „erst S3, dann Lambda“, wenn Code den Nachrichteninhalt benötigt. Eine direkte Lambda-Aktion erhält Metadaten und ausgewählte Header, nicht aber den vollständigen Body. Der Body bleibt als unverändertes MIME-Objekt in S3 (AWS: Konzepte zum Empfang mit SES).

Diese Trennung ist nützlich. Der SMTP-seitige Schritt speichert die Originalnachricht schnell, während Parsing, Indexierung, Benachrichtigungen und Geschäftslogik nach der Annahme stattfinden. Ein vorübergehender Datenbankfehler sollte den Mailserver des Absenders nicht zwingen, die SMTP-Transaktion zu wiederholen.

1. Unterstützte Region wählen und Domain verifizieren

Der E-Mail-Empfang mit SES ist nur in ausgewählten AWS-Regionen verfügbar. Wählen Sie eine aus der aktuellen Liste der SES-Empfangsendpunkte und halten Sie SES-, Lambda-, SNS- und KMS-Ressourcen in dieser Region, sofern die jeweilige AWS-Dokumentation nicht ausdrücklich etwas anderes erlaubt.

Legen Sie eine SES-Domainidentität für genau die Stammdomain oder Subdomain an, die E-Mails empfangen soll. Für die Domain-Verifizierung müssen Sie die DNS-Einträge veröffentlichen, die SES vorgibt. Die Verifizierung für den Empfang belegt die Kontrolle über die Domain; sie ist unabhängig von der Konfiguration der MX-Route, die eingehenden Verkehr an SES leitet (AWS-Leitfaden zur Domain-Verifizierung).

Für eine eigene Subdomain sehen die DNS-Einträge konzeptionell so aus:

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

Ersetzen Sie us-east-1 durch die gewählte Region. AWS dokumentiert den MX-Wert als 10 inbound-smtp.<region>.amazonaws.com (AWS-Leitfaden zum MX-Eintrag). Richten Sie Ihre Stammdomain nicht auf SES, wenn dort noch Personen E-Mails über Google Workspace, Microsoft 365 oder einen anderen Postfachanbieter empfangen.

Prüfen Sie den veröffentlichten Eintrag vor dem Testen über mehr als einen Resolver:

dig MX inbound.example.com +short

DNS-Sichtbarkeit belegt nur, dass die Route veröffentlicht ist. Senden Sie eine kontrollierte Nachricht an eine Testadresse und prüfen Sie, ob SES sie gespeichert hat, bevor Sie die Einrichtung als abgeschlossen betrachten.

2. Die Rohnachricht vor der Verarbeitung speichern

Legen Sie einen privaten S3-Bucket mit blockiertem öffentlichem Zugriff, einer Lifecycle-Richtlinie und möglichst engen IAM-Berechtigungen an. Erstellen Sie dann eine SES-Empfangsregel, deren Empfängerbedingung zu Ihrer Inbound-Domain oder zu bestimmten Adressen passt.

Die erste Aktion sollte die Rohnachricht in S3 ablegen. Ein Objektpräfix wie inbound/ erleichtert es, Aufbewahrungsregeln und Zugriffsrichtlinien einzugrenzen. SES speichert unveränderte MIME-Rohdaten. AWS dokumentiert derzeit standardmäßig maximal 40 MB beim Speichern in S3, während die SNS-Aktion mit der vollständigen Nachricht auf deutlich kleinere 150 KB begrenzt ist (AWS: S3-Aktion für Empfangsregeln). Dieser Größenunterschied macht S3 zum sichereren Standard für echte Antworten und Anhänge.

Wenn Sie die optionale SES-KMS-Einstellung für die Empfangsaktion aktivieren, lesen Sie die Verschlüsselungsdetails genau. SES nutzt für diese Funktion clientseitige Verschlüsselung, nicht die übliche serverseitige S3-Verschlüsselung. Ihr Leseprozess muss das Objekt daher mit einem kompatiblen Client entschlüsseln. Aktivieren Sie die Option nicht beiläufig, nur um während eines Incidents festzustellen, dass Ihr Node-Parser die gespeicherten Bytes nicht lesen kann.

Erlauben Sie SES nur das Schreiben in den vorgesehenen Bucket und das vorgesehene Präfix. Geben Sie Lambda s3:GetObject nur für denselben Speicherort. Die Funktion braucht keine Berechtigungen zur Bucket-Verwaltung.

3. Eine asynchrone Lambda-Aktion hinzufügen

Setzen Sie die Lambda-Aktion in der Empfangsregel hinter die S3-Aktion und nutzen Sie asynchronen Aufruf, es sei denn, die Funktion muss entscheiden, ob SES die Regel weiter auswerten soll. AWS empfiehlt asynchrone Ausführung für die normale Verarbeitung und synchrone Ausführung nur für Entscheidungen über den Mailfluss (AWS: Lambda-Aktion für Empfangsregeln).

Die von SES vergebene mail.messageId ist zugleich der S3-Objektschlüssel, wenn kein Präfix konfiguriert ist. Mit Präfix stellen Sie es voran. Das folgende Node.js-Grundgerüst lädt die Rohnachricht und parst sie. Packen Sie @aws-sdk/client-s3 und einen gepflegten MIME-Parser wie mailparser in das Deployment-Artefakt und pinnen Sie deren Versionen.

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; } } }

Die Platzhalterfunktionen stehen für anwendungsspezifischen Speicher, aber ihr Vertrag ist wichtig. claimOnce muss einen Unique-Constraint der Datenbank oder einen bedingten Schreibvorgang auf die Nachrichten-ID des Providers nutzen. Ein Lesen mit anschließendem Einfügen ist anfällig für Race Conditions. Speichern Sie den Verarbeitungsstatus, damit ein Operator zwischen processing, complete, quarantined und failed unterscheiden kann.

Nach dem Envelope-Empfänger routen, nicht nach dem To-Header

Die sichtbaren Felder To und Cc sind vom Absender gelieferter Nachrichteninhalt. Sie können das tatsächliche Ziel auslassen – durch BCC, Weiterleitung oder gezielte Manipulation. SES-Empfangsbedingungen nutzen die Empfänger aus dem SMTP-Envelope, und AWS weist nachgelagerte Verarbeiter an, die Empfänger aus der SES-Benachrichtigung zu verwenden, um zu bestimmen, wohin die Nachricht zugestellt wurde (AWS: Konzepte zum Empfang).

Diese Unterscheidung verhindert einen mandantenübergreifenden Fehler. Empfängt reply+tenant-a@inbound.example.com eine Nachricht, deren sichtbares To tenant-b@example.com lautet, routen Sie sie über die authentifizierte Anwendungszuordnung der Envelope-Adresse – niemals über den angezeigten Header.

Verwenden Sie ein zufälliges, nicht erratbares Antwort-Token, wenn eine Adresse einen Kunden oder eine Konversation identifiziert. Speichern Sie das Token nur als Hash, lassen Sie es bei Bedarf ablaufen und lehnen Sie Adressen ab, die keinem aktiven Workspace zugeordnet sind. Ein vorhersagbarer Local Part wie ticket-42 lädt geradezu dazu ein, Nachrichten in den Thread eines anderen Nutzers einzuschleusen.

MIME als feindliche Eingabe parsen

E-Mail ist ein verschachteltes, jahrzehntealtes Eingabeformat. RFC 5322 definiert Header und Body von Nachrichten, MIME ergänzt mehrteilige Inhalte und Transfer-Encodings (RFC 5322, RFC 2045). Nutzen Sie einen gepflegten Parser, statt selbst an Leerzeilen oder Boundaries zu trennen.

Setzen Sie Grenzen, bevor Inhalte dem Produkt zur Verfügung stehen:

  • Begrenzen Sie die gesamten dekodierten Bytes, die Anzahl der Anhänge, die Größe einzelner Anhänge, die MIME-Verschachtelungstiefe und die Parsing-Zeit.
  • Speichern Sie Anhänge privat unter generierten Objektnamen. Verwenden Sie den Dateinamen des Absenders niemals als Pfad.
  • Behandeln Sie deklarierten Content-Type und Dateinamen als Hinweise. Ermitteln Sie den Typ nach Möglichkeit anhand des Inhalts.
  • Führen Sie Inhalte von Anhängen niemals aus. Scannen Sie Anhänge oder stellen Sie sie vor dem Download unter Quarantäne.
  • Bereinigen Sie HTML mit einer strikten Allowlist, blockieren Sie externe Bilder standardmäßig und rendern Sie in einem isolierten Kontext. Verwenden Sie für automatisierte Analysen bevorzugt Plain Text.
  • Schreiben Sie keine Roh-Bodies, Adressen, Tokens oder Anhangsinhalte in gewöhnliche Anwendungslogs.

SES kann Ergebnisse für SPF, DKIM, DMARC, Spam und Viren melden, doch AWS weist darauf hin, dass SES diese Ergebnisse nur bereitstellt und Ihre Geschäftsrichtlinie nicht automatisch anwendet. Entscheiden Sie, ob Fehlschläge abgelehnt, unter Quarantäne gestellt oder mit einer Warnung angezeigt werden sollen. Eine bestandene Authentifizierung identifiziert eine Domain über einen bestimmten Mechanismus; sie macht den Inhalt weder sicher noch beweist sie, dass ein Mensch ihn verfasst hat.

Wiederholungsversuche langweilig machen

Bei asynchronem Lambda-Aufruf können fehlgeschlagene Funktionen wiederholt werden, und AWS warnt, dass doppelte Zustellung möglich ist, selbst wenn die Funktion keinen Fehler zurückgibt. Konfigurieren Sie ein On-Failure-Ziel oder eine Dead-Letter-Queue und richten Sie Alarme für Verarbeitungsfehler ein (AWS: Retry-Verhalten von Lambda).

Idempotenz sollte jeden nachgelagerten Seiteneffekt abdecken:

  1. Fügen Sie die SES-Nachrichten-ID unter einem Unique-Constraint ein.
  2. Persistieren Sie geparste Inhalte und Thread-Verknüpfungen nach Möglichkeit in einer einzigen Transaktion.
  3. Legen Sie Benachrichtigungen, Ticket-Erstellung oder Agentenarbeit in eine Outbox, deren Schlüssel aus Nachrichten-ID plus Aktionstyp besteht.
  4. Markieren Sie den Datensatz erst als abgeschlossen, wenn die dauerhaften Schreibvorgänge erfolgreich waren.
  5. Verarbeiten Sie erneut aus dem ursprünglichen S3-Objekt, nicht aus einem verlustbehafteten Logeintrag.

Wenn Sie die Verarbeitung über S3-Benachrichtigungen statt über eine SES-Lambda-Aktion auslösen, gilt dieselbe Regel. Amazon-S3-Benachrichtigungen sind auf At-least-once-Zustellung ausgelegt und kommen nicht garantiert in der richtigen Reihenfolge an (AWS: S3-Event-Benachrichtigungen).

Nachrichten in Threads gruppieren, ohne dem Betreff zu vertrauen

Nutzen Sie die geparsten Felder Message-ID, In-Reply-To und References, um eine Thread-Zuordnung vorzuschlagen. Gruppieren Sie nicht allein anhand eines Betreffs, der mit Re: beginnt. Prüfen Sie außerdem, ob Envelope-Adresse oder Antwort-Token zum selben Workspace und zur selben Konversation gehören, bevor Sie etwas verknüpfen.

Automatische Antworten brauchen eine eigene Richtlinie. Erkennen Sie Signale wie Auto-Submitted und vermeiden Sie Antwortschleifen. RFC 3834 empfiehlt eine klare Kennzeichnung und zurückhaltendes Verhalten bei automatischen Antworten (RFC 3834). Wenn ein KI-Agent eine Antwort entwirft, bleibt der Versand ein expliziter, idempotenter Seiteneffekt. Verlangen Sie eine Freigabe durch den Nutzer bei unerwarteten Empfängern, sensiblen Inhalten oder Aktionen außerhalb des ursprünglichen Support- oder Produkt-Workflows. Der Empfang einer Nachricht ist keine pauschale Einwilligung in themenfremdes Marketing.

Checkliste für den Produktivbetrieb

  • Die Empfangsregion unterstützt SES-Inbound-E-Mails.
  • Die Domainidentität ist verifiziert und der MX-Eintrag wird korrekt aufgelöst.
  • Die Empfangsregel hat eine enge Empfängerbedingung und der vorgesehene Regelsatz ist aktiv.
  • Die S3-Aktion wird vor der asynchronen Lambda-Verarbeitung ausgeführt.
  • Der Bucket ist privat, der Zugriff folgt dem Prinzip geringster Rechte und die Aufbewahrung ist dokumentiert.
  • Die SES-Nachrichten-ID hat eine Eindeutigkeitsbeschränkung in der Datenbank.
  • Das Routing verwendet Envelope-Empfänger, nicht sichtbare To- oder Cc-Header.
  • MIME, HTML, Links und Anhänge werden als nicht vertrauenswürdige Eingabe behandelt.
  • Fehlgeschlagene Events erreichen ein überwachtes Ziel und können erneut abgespielt werden.
  • Thread-Abgleiche erzwingen die Workspace-Zugehörigkeit.
  • Automatische Antworten verfügen über Schleifenvermeidung, Einwilligungsgrenzen und Sende-Idempotenz.
  • Ein kontrollierter Test deckt Plain-Text, HTML, BCC, doppelte Zustellung, große Anhänge, fehlerhaftes MIME und Parserfehler ab.

Direktes SES passt gut, wenn Ihr Team AWS-native Kontrolle möchte und bereit ist, DNS, IAM, MIME-Parsing, Mandantentrennung, Aufbewahrung, Wiederholungsversuche und Betriebsalarme selbst zu verantworten. Wenn Sie diese Anwendungsbausteine lieber hinter einer schlankeren E-Mail-API haben möchten, bietet SendHQ Inbound-Adressen, gespeicherte Nachrichten, Threads und Zugriff mit Workspace-Berechtigungen zusammen mit ausgehenden transaktionalen E-Mails. So oder so: Halten Sie die Rohnachricht wiederherstellbar und machen Sie jede nachgelagerte Aktion sicher wiederholbar.