Datei hochladen
  1. base64hub
  2. Base64 verstehen
  3. Base64 in E-Mails
Anleitung

Base64 in E-Mails: MIME, Anhänge und Header

Jeder E-Mail-Anhang reist als Base64-Text, umgebrochen nach 76 Zeichen. Wer die MIME-Struktur drumherum lesen kann, holt die Datei in einer Minute aus einer Rohnachricht heraus.

Warum E-Mail Base64 braucht

Internet-Mail wurde für 7-Bit-ASCII-Text in Zeilen begrenzter Länge gebaut. RFC 5322 begrenzt eine Zeile bis heute auf 998 Zeichen, und SMTP garantiert die sichere Zustellung nur für 7-Bit-Daten, sofern der empfangende Server nicht die Erweiterung 8BITMIME ankündigt. Selbst bei 8-Bit-Transport darf eine Nachricht keine NUL-Bytes und keine einzelnen Carriage Returns und Line Feeds enthalten, und die kommen in jeder Binärdatei früher oder später vor. Rohe Bytes zu senden erfordert die Erweiterung BINARYMIME, und darauf kann sich ein Absender auf dem ganzen Weg von Server zu Server nicht verlassen.

MIME (RFC 2045) löst das mit dem Header Content-Transfer-Encoding. Er teilt dem Empfänger mit, wie der Inhalt eines Teils in sicheren Text verwandelt wurde. Es gibt fünf Werte:

WertBedeutung
7bitReine ASCII-Zeilen, keine Kodierung
8bit8-Bit-Text in kurzen Zeilen, braucht 8BITMIME
binaryRohe Bytes, braucht BINARYMIME
quoted-printableWeitgehend lesbarer Text, Nicht-ASCII-Bytes als =XX maskiert
base64Base64 in Zeilen mit höchstens 76 Zeichen

Für Anhänge wählt Mail-Software fast immer base64. Den Algorithmus selbst erklärt So funktioniert Base64.

Base64 vs. Quoted-printable

Quoted-printable lässt ASCII-Zeichen unverändert und schreibt jedes andere Byte als = plus zwei Hex-Ziffern. Ein é in UTF-8 (Bytes C3 A9) wird zu =C3=A9. Zeilen über 76 Zeichen werden mit einem weichen Zeilenumbruch getrennt, einem = am Zeilenende.

Das ist ideal für englischen Text mit gelegentlichem Akzent und verschwenderisch für alles andere, denn jedes Nicht-ASCII-Byte kostet 3 Zeichen. Base64 kostet pauschal 4/3, unabhängig vom Inhalt. Wir haben 20 Wiederholungen eines Satzes in drei Sprachen mit Pythons quopri.encodestring und base64.encodebytes kodiert (beide mit LF als Zeilenende):

Beispiel (UTF-8)BytesQuoted-printableBase64
Englisches Pangramm9009261.216
Französischer Satz mit Akzenten1.0601.8301.435
Russisches Pangramm1.6004.6022.165

Mail-Clients treffen diese Wahl meist für Sie: Quoted-printable oder 7bit für Textinhalte mit überwiegend ASCII, Base64 für Anhänge und Text in nicht lateinischen Schriften. Wenn Sie Quoted-printable von Hand lesen oder erzeugen müssen, wandelt das Quoted-printable-Tool in beide Richtungen um.

Anhänge in einer Rohnachricht

Eine Nachricht mit Anhang hat einen Inhalt vom Typ multipart/mixed. Jeder Teil hat eigene Header, eine Leerzeile und dann seinen Inhalt. Hier eine vollständige, minimale Nachricht, erzeugt mit dem Python-Paket email (Boundary zur besseren Lesbarkeit gekürzt):

From: Ana <ana@example.com>
To: bo@example.org
Subject: =?utf-8?b?Q2Fmw6kgbWVudSDigJMgR3LDtsOfZQ==?=
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="b1"

--b1
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

Hi Bo,

The caf=C3=A9 menu is attached.

--b1
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="menu.pdf"

JVBERi0xLjcKJeLjz9MKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyA+PgplbmRvYmoKdHJhaWxl
cgo8PCAvUm9vdCAxIDAgUiA+PgolJUVPRgo=

--b1--

Worauf Sie achten sollten:

  • Content-Type nennt den Dateityp, Content-Disposition den Dateinamen.
  • Content-Transfer-Encoding: base64 bestätigt, dass der Inhalt Base64 ist.
  • Das Base64 beginnt nach der ersten Leerzeile des Teils und endet vor der nächsten Zeile --boundary.
  • Die Zeilen haben genau 76 Zeichen, außer der letzten. Jede volle Zeile enthält 57 Bytes der Datei.
  • JVBERi0 am Anfang ist das Base64 von %PDF-, es handelt sich also um ein PDF. Weitere Signaturen stehen unter Häufige Base64-Präfixe.

In HTML-Mails eingebettete Bilder nutzen denselben Mechanismus in einem Teil vom Typ multipart/related. Jedes Bild erhält einen Header Content-ID, und das HTML verweist darauf mit <img src="cid:...">. Bei der Übertragung enden diese Zeilen mit CRLF, sodass je 76 Zeichen Base64 78 Bytes kosten; die vollständige Rechnung steht unter Base64-Größenzuwachs. Um selbst umbrochene Ausgabe zu erzeugen, verwenden Sie den MIME-Base64-Encoder.

Kodierte Header: =?UTF-8?B?…?=

Header wie Subject, Anzeigenamen in From und manche Dateinamen müssen ASCII sein. RFC 2047 kodiert anderen Text als Encoded-Words:

=?charset?encoding?encoded-text?=

encoding ist B für Base64 oder Q für eine Quoted-printable-Variante, in der _ für ein Leerzeichen steht. Bei beiden Buchstaben spielt die Groß- und Kleinschreibung keine Rolle, weshalb Python oben =?utf-8?b? geschrieben hat. Einige echte Beispiele, alle mit Pythons email.header.decode_header geprüft:

KodiertDekodiert
=?UTF-8?B?0J/RgNC40LLQtdGC?=Привет
=?utf-8?q?Caf=C3=A9_menu?=Café menu
=?ISO-8859-1?Q?Caf=E9_menu?=Café menu
=?utf-8?b?Q2Fmw6kgbWVudSDigJMgR3LDtsOfZQ==?=Café menu – Größe

Ein Encoded-Word darf höchstens 75 Zeichen lang sein, lange Betreffzeilen werden daher auf mehrere aufgeteilt, eines pro gefalteter Headerzeile. Laut RFC 2047 wird Leerraum zwischen benachbarten Encoded-Words ignoriert, und jedes Wort muss ganze Zeichen enthalten, ein UTF-8-Zeichen aus mehreren Bytes wird also nie auf zwei Wörter verteilt. Ein russischer Betreff mit 44 Zeichen kam aus Python so heraus:

Subject: =?utf-8?b?0KHRh9GR0YIg0LfQsCDQvtC60YLRj9Cx0YDRjCDQuCDQsNC60YIg0YE=?=
 =?utf-8?b?0LLQtdGA0LrQuCDQtNC70Y8g0LHRg9GF0LPQsNC70YLQtdGA0LjQuA==?=

Jedes Wort ist ein vollständiger Base64-String mit eigenem Padding. Einzeln dekodiert ergibt das erste Счёт за октябрь и акт с und das zweite верки для бухгалтерии; ein Decoder fügt sie zum ursprünglichen Betreff zusammen.

Für Dateinamen von Anhängen gibt es einen zweiten Standard, RFC 2231, der so aussieht: filename*=UTF-8''%D0%BE%D1%82%D1%87%D1%91%D1%82.pdf. Viele Mail-Programme setzen außerdem Encoded-Words nach RFC 2047 in filename="...", ein Parser sollte also beides verarbeiten.

So extrahieren Sie einen Anhang aus einer .eml-Datei

Im Browser. Öffnen Sie die .eml im EML-Decoder, um die Nachrichteninhalte zu lesen und die Anhänge zu speichern. Er läuft lokal in der Seite, was zählt, wenn die Nachricht Rechnungen oder personenbezogene Daten enthält.

Mit Python. Die Standardbibliothek parst MIME und dekodiert sowohl Base64-Inhalte als auch Header nach RFC 2047:

import sys
from email import policy
from email.parser import BytesParser

with open(sys.argv[1], "rb") as f:
    msg = BytesParser(policy=policy.default).parse(f)

print("Subject:", msg["Subject"])        # encoded-words decoded
for part in msg.iter_attachments():
    name = part.get_filename()
    data = part.get_content()             # Base64 already decoded to bytes
    with open(name, "wb") as out:
        out.write(data)
    print("saved", name, len(data), "bytes")

Auf die Beispielnachricht oben angewandt, gab es Subject: Café menu – Größe und saved menu.pdf 83 bytes aus. In echtem Code sollten Sie name vor dem Schreiben bereinigen, denn der Wert stammt vom Absender.

Von Hand. Kopieren Sie die Base64-Zeilen des Teils in eine Datei und dekodieren Sie sie. Rohe .eml-Dateien verwenden CRLF als Zeilenende, und GNU base64 -d bricht beim ersten Carriage Return mit invalid input ab. Entfernen Sie die CRs vorher oder überspringen Sie Zeichen außerhalb des Alphabets mit -i:

tr -d '\r' < block.txt | base64 -d > menu.pdf

openssl base64 -d -in block.txt -out menu.pdf akzeptierte in unseren Tests die CRLF-Version unverändert. Haben Sie nur den Base64-Text, fügen Sie ihn in Base64 in Datei ein, das den Typ an den ersten Bytes erkennt, oder bei PDFs direkt in Base64 in PDF.

Eine .eml-Datei öffnen

Header, Inhalte und Anhänge einer Roh-E-Mail ansehen, lokal in Ihrem Browser dekodiert.

E-Mail-Decoder

Häufige Fragen

Wie öffne ich einen Base64-Anhang aus dem Quelltext einer E-Mail?

Kopieren Sie die Base64-Zeilen zwischen der Leerzeile des Teils und der nächsten Boundary und dekodieren Sie sie als Datei. Der EML-Decoder extrahiert Anhänge aus einer .eml-Datei für Sie.

Was bedeutet =?UTF-8?B? im Betreff einer E-Mail?

Damit beginnt ein Encoded-Word nach RFC 2047: Der Zeichensatz ist UTF-8, und B bedeutet, dass der Text zwischen dem nächsten ? und ?= Base64 ist. Ein Q statt B steht für eine Quoted-printable-Variante.

Warum sind Base64-Zeilen in E-Mails 76 Zeichen lang?

RFC 2045 begrenzt Base64-Zeilen in MIME auf 76 Zeichen. 76 Zeichen enthalten genau 57 Bytes, und die Grenze hält die Zeilen deutlich unter dem Maximum von 998 Zeichen, das E-Mail erlaubt.

Ist für E-Mail-Text Quoted-printable oder Base64 besser?

Quoted-printable für Text, der überwiegend ASCII ist, denn er bleibt lesbar und wächst kaum. Base64 für Binärdaten und Text in nicht lateinischen Schriften, wo Quoted-printable ihn fast dreimal so groß machen kann.

Weiterlesen