Zum Hauptinhalt springen
  1. Meine Projekte & Posts/

Mission: E-Mail-Tarnung

·502 Wörter·3 min
E-Mail-Verstecken für Fortgeschrittene: Rot18 trifft auf Mausakrobatik
TL;DR: Kombination aus Rot18 und Nutzer Interaktion zur E-Mail Verschleierung: Die E-Mail-Adresse wird vorab chiffriert und erst bei einer Mausbewegung wird die verschlüsselte Zeichenkette im href-Attribut zur richtigen E-Mail-Adresse dechiffriert.

Erst nachdem ich einen Endpunkt zur Newsletter-Anmeldung auf der Webseite unseres Vereins bereitgestellt hatte, wurde mir klar wie viele Bots bzw. Crawler im Internet herumirren und jede noch so unbekannte Webseite nach Daten absuchen und missbrauchen. Bereits eine Stunde nach dem Release begann mein Mailserver, mir in Dauerschleife Benachrichtigungen über unzustellbare E-Mails zu senden – verursacht durch zuvor generierte Adressen, die ein Bot in das Eingabefeld eingefügt und abgesendet hatte. Erst mit der Integration eines Captchas konnte ich dem ganzen Abhilfe verschaffen.

Da stellte sich bei der Erstellung meiner eigenen Webseite die Frage: Wie verstecke ich meine E-Mail vor diesen Bots?

Nach kurzer Recherche stieß ich auf diesen Artikel: https://spencermortensen.com/articles/email-obfuscation

Dort werden mehrere Methoden zur E-Mail Verschleierung vorgestellt. Hinsichtlich der Kompatibilität zum hier genutzten Hugo-Theme entschied ich mich für die Rot18-Methode. Dabei wird im Vorfeld die E-Mail in eine Zeichenkette verschlüsselt und wie üblich dem href-Attribut zugewiesen. Erst beim Laden der Seite wird diese wieder entschlüsselt.
So wird zum Beispiel <a class="email" href="znvygb:nd@rznvy.fcraprezbegrafra.pbz">email</a> zu <a class="email" href="mailto:aq@email.spencermortensen.com">email</a> umgewandelt.

Besonders interessant erschien mir außerdem die User-Interaction-Obfuscation, bei der die E-Mail-Adresse erst bei einer Mausinteraktion hinzugefügt wird.

Warum also nicht beides kombinieren? Gesagt, getan - hier das Ergebnis:

document.addEventListener('DOMContentLoaded', function () {
  const rot18 = new Rot18();
  const coder = new LinkCoder(rot18);
  const listener = new Listener();

  listener.decode = function () {
    document.querySelectorAll('.email').forEach(coder.decode.bind(coder));
  };

  listener.on();
});

// Rot18

function Rot18() {
  this.lowercase = new Rotater('a', 'z', 21);
  this.uppercase = new Rotater('A', 'Z', 21);
  this.digits = new Rotater('0', '9', 8);
}

Rot18.prototype.encode = function (text) {
  text = this.lowercase.encode(text);
  text = this.uppercase.encode(text);
  text = this.digits.encode(text);

  return text;
};

Rot18.prototype.decode = function (text) {
  text = this.lowercase.decode(text);
  text = this.uppercase.decode(text);
  text = this.digits.decode(text);

  return text;
};

// Rotater

function Rotater(c0, cN, offset) {
  const code0 = c0.charCodeAt(0);
  const codeN = cN.charCodeAt(0);
  const length = codeN - code0 + 1;

  this.re = new RegExp('[' + c0 + '-' + cN + ']', 'g');
  this.forward = Rotater.rotate.bind(Rotater, code0, length, offset);
  this.backward = Rotater.rotate.bind(Rotater, code0, length, length - offset);
}

Rotater.rotate = function (code0, length, offset, ci) {
  const iBefore = ci.charCodeAt(0) - code0;
  const iAfter = (iBefore + offset) % length;

  return String.fromCharCode(iAfter + code0);
};

Rotater.prototype.encode = function (text) {
  return text.replace(this.re, this.forward);
};

Rotater.prototype.decode = function (text) {
  return text.replace(this.re, this.backward);
};

// LinkCoder

function LinkCoder(coder) {
  this.coder = coder;
}

LinkCoder.prototype.encode = function (a) {
  const text = a.getAttribute('href');
  const code = this.coder.encode(text);

  a.setAttribute('href', code);
};

LinkCoder.prototype.decode = function (a) {
  const code = a.getAttribute('href');
  const text = this.coder.decode(code);

  a.setAttribute('href', text);
};

// Listener

function Listener() { }

Listener.prototype.decode = null;

Listener.prototype.on = function () {
  this.listener = this.__onInteraction.bind(this);

  document.addEventListener('mouseenter', this.listener, true);
  document.addEventListener('focus', this.listener, true);
};

Listener.prototype.off = function () {
  document.removeEventListener('mouseenter', this.listener, true);
  document.removeEventListener('focus', this.listener, true);

  delete this.listener;
};

Listener.prototype.__onInteraction = function () {
  this.off();
  this.decode();
};