Einstufung von Emails als Spam mit DKIM vermeiden

DKIM ist kein muss, aber ein Meilenstein, wenn man nicht im Spam-Filter der bekannten E-Mail-Provider landen möchte. Der Empfänger hat sogar die Möglichkeit, die Email zu verweigern oder auszusortieren, wenn keine Signatur vorhanden ist oder diese nicht valide ist.

Was passiert da überhaupt?

Das Verfahren basiert auf asymmetrischer Verschlüsselung. Unser Server muss also einen öffentlichen Schlüssel bereitstellen und die Emails vor dem Versand mit dem privaten Teil verschlüsseln.

Zuerst wird ein Hash aus Header und Body der Email erzeugt. Dieser wird mit unserem privaten Schlüssel (+ zusätzlich mit Base64) kodiert und als neuer Header hinzugefügt. Der empfangende Server geht dann rückwärts vor: Erst Base64 enkodieren, den erhaltenen Wert mit dem öffentlichen Schlüssel des Servers entschlüsseln und dann selbst den Hashwert der Email ermittel.

Stimmt der entschlüsselte Hash mit dem eigens generierten überein, ist die Signatur valide.

Das Schlüsselpaar erzeugen

Da die Verschlüsselung mit RSA vonstatten geht, könnte man theoretisch das übliche sh-keygen -t rsa verwenden. Sollte man aber nicht!!! Aus verschiedensten Gründen werden diese Schlüssel nicht akzeptiert.

Man verwende hierfür besser openssl in einem Verzeichnis außerhalb des Projektes:

openssl genrsa -out dkim.priv 1024
openssl rsa -in dkim.priv -pubout > dkim.pub

Mit genrsa wird der private Schlüssel mit einer Länge von 1024 Bit erzeugt. Die 1024 Bit sind relativ sicher und passen gut in einen TXT Record. Mit rsa wird der öffentliche Schlüssel aus dem privaten erzeugt.

Während der private Key bleiben kann, muss der öffentliche Teil in ein TXT Record auf einer speziellen Subdomain gepresst werden. Lautet der Name meiner Domain „example.com“, muss eine Subdomain „Selektor._domainkey.example.com“ erstellt werden, die einen TXT Record der Form

v=DKIM1; p=MIG...P/abq2FKPJ6G3rgx5adsGKvnmkMadSwIDAQAB

enthält. Hierbei wird nur der Schlüssel, ohne weitere Informationen wie „ssh-rsa“ oder dem Domainnamen, benötigt. Zum „Selektor“ gleich mehr!

DKIM implementieren (SwiftMailer)

/** @var \Swift_Message $message */
$message = \Swift_Message::newInstance();
$message->setSubject("a subject");
$message->setFrom(['example@example.com' => 'example Name']);
$message->setReplyTo(['example@example.com' => 'example Name']);
$message->setTo('recipient@other-website.com');
$message->setBody('This is <strong>wonderful</strong> body!', 'text/html');

// build DKIM signer
$message->attachSigner(
    \Swift_Signers_DKIMSigner::newInstance(
        file_get_contents('/path/to/my/private/key'),
        'example.com',
        'this_is_the_selector_string'
    )
);

Wie in diesem kleinen Beispiel zu sehen, übernimmt der SwiftMailer die Aufgaben für uns. Alles was wir ihm geben müssen ist der private Schlüssel (mit Kommentarzeilen, so wie er erstellt wurde!), den Domainnamen und einen Selektor.

Der Selektor ist ein beliebiger String, der beim SwiftMailer angegeben und auf dem Server als Subdomain existieren muss! Hierdurch wird er von Empfangs-Server gefunden und identifiziert.

Fehlersuche

In einem unserer Projekte war die Identifizierung kein Problem, aber funktioniert hatte das Verfahren trotzdem nicht. Da DKIM sowohl Header, als auch Body hasht, ist es manchmal wichtig zu überprüfen, was da alles drin steckt…

Wir hatten zusätzliche Header in der Mail angebracht, allerdings erst, nachdem die Signatur schon vollständig erzeugt wurde. Somit hat der Empfangs-Server mit anderen Headern berechnet, als wir…

Statt die Reihenfolge zu ändern, kann man dem Signer aber auch sagen, welche Header-Felder denn konkret beachtet bzw. ignoriert werden sollen.


// add additional headers
$headers = $message->getHeaders();
$headers->addTextHeader('X-Mailer', 'PHP v'.phpversion());
$headers->addParameterizedHeader('Content-Type', 'text/html', ['charset' => 'utf-8']);

// create a signer
$signer = \Swift_Signers_DKIMSigner::newInstance(
    file_get_contents('/path/to/my/private/key'),
    'example.com',
    'this_is_the_selector_string'
);

// ignore the additional headers
$signer->ignoreHeader('Content-Transfer-Encoding');
$signer->ignoreHeader('X-Swift-Return-Path');
$signer->ignoreHeader('X-Mailer');

// add the signer
$message->attachSigner($signer);

// ...

Über Patrick Blawert 4 Artikel
Seit 2016 Entwickler bei der VCAT und im Speziellen mit Symfony unterwegs

Hinterlasse jetzt einen Kommentar

Kommentar hinterlassen

E-Mail Adresse wird nicht veröffentlicht.


*