kreativsoehne / sulu-email-obfuscator-bundle
Delivers e-mail addresses obfuscated on Sulu websites: ROT13 in the source, readable in the browser. Works on everything Sulu renders, without touching a template.
Package info
github.com/kreativsoehne/sulu-email-obfuscator-bundle
Type:symfony-bundle
pkg:composer/kreativsoehne/sulu-email-obfuscator-bundle
Requires
- php: ^8.2
- symfony/config: ^5.4 || ^6.4 || ^7.0
- symfony/dependency-injection: ^5.4 || ^6.4 || ^7.0
- symfony/http-foundation: ^5.4 || ^6.4 || ^7.0
- symfony/http-kernel: ^5.4 || ^6.4 || ^7.0
- twig/twig: ^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.40
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.2
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^10.5 || ^11.0
- rector/rector: ^2.0
- symfony/yaml: ^5.4 || ^6.4 || ^7.0
Suggests
- sulu/sulu: Registers the response listener in the website context only, so the administration interface stays untouched.
This package is auto-updated.
Last update: 2026-08-16 19:59:19 UTC
README
Liefert Mailadressen auf Sulu-Seiten verdreht aus: im Quelltext steht ROT13, lesbar wird die Adresse erst im Browser. Läuft mit Sulu 2.6 und Sulu 3, und ohne Sulu als gewöhnliches Symfony-Bundle.
Der Unterschied zu den üblichen Twig-Filtern: es gibt nichts einzubauen. Das Bundle schreibt die fertige Seite um, nicht einzelne Vorlagen. Eine Sulu-Seite trägt Adressen an Stellen, die keine Vorlage des Projekts kontrolliert — Fließtext aus dem Editor, Snippets, Artikel, Rechtstexte aus einer Fremd-API. Ein Filter erwischt immer nur die Stellen, an die jemand gedacht hat.
Funktionsweise
- Umgeschrieben wird die Antwort, spät im
kernel.responseund vor dem HTTP-Cache. Was im Cache landet, ist bereits verdreht. - Nur im Website-Kontext. Sulu betreibt Backend und Auftritt aus einer Codebasis und unterscheidet sie über
sulu.context; daran hängt die Registrierung. Das Backend bleibt unangetastet — ein Eingabefeld muss die Adresse zeigen, die die Redaktion getippt hat. - Die Vorschau zeigt die echte Adresse. Sulu rendert sie zwar mit einem Website-Kernel, markiert die Anfrage aber mit dem Attribut
preview; daran erkennt der Listener sie und lässt sie liegen. Zweimal richtig so: die Redaktion prüft Inhalte, und die Vorschau tauscht beim Tippen Teile des Dokuments aus — darin eingesetzte Skripte laufen nicht, verdrehte Adressen blieben also stehen. - Verdrehte Verweise tragen
data-obfuscated-email. Daran erkennt das Skript sie im Browser, und daran erkennt der Umschreiber, was er schon bearbeitet hat. Zweimal angewendet ändert also nichts. - Beschriftungen bleiben lesbar, außer sie sind selbst eine Adresse. „Schreib uns" bleibt „Schreib uns"; Browser und Server halten sich an dieselbe Regel.
- Skripte und Stile bleiben unberührt. Eine Adresse in einem Attribut wird nie angefasst, sonst zerfiele das Markup.
- Das Skript geht nur auf Seiten mit, auf denen es etwas zu tun gibt — knapp ein Kilobyte, inline, kein Build-Schritt, keine zusätzliche Anfrage.
Installation
composer require kreativsoehne/sulu-email-obfuscator-bundle
Bundle registrieren (config/bundles.php), falls Flex das nicht übernimmt:
KreativSoehne\SuluEmailObfuscatorBundle\SuluEmailObfuscatorBundle::class => ['all' => true],
Das war es. Ohne weitere Einstellung gehen ab dem nächsten Aufruf alle Adressen verdreht raus.
Einstellungen
Alle Werte sind Vorgaben, die Datei ist nur nötig, wenn davon abgewichen wird:
# config/packages/sulu_email_obfuscator.yaml sulu_email_obfuscator: enabled: true # schaltet das Bundle ganz ab automatic: true # schreibt die fertige Seite um link_plain_addresses: true # eine frei stehende Adresse wird zum Verweis script: auto # auto | manual | none excluded_routes: ['sulu_preview', 'sulu_admin']
automatic: falselässt nur die Filter übrig, für Projekte, die jede Stelle selbst bestimmen wollen.link_plain_addresses: falselässt Adressen im Fließtext stehen, statt sie zu verlinken. Sie bleiben dann lesbar — verdrehen lässt sich nur, was ein Verweis ist.script: manualheißt: die Vorlage setzt{{ sulu_email_obfuscator_script() }}selbst, vor</body>. Nötig bei einer strengen Content Security Policy, dann mit Nonce:{{ sulu_email_obfuscator_script(csp_nonce) }}.script: noneheißt: das Projekt bringt das Skript selbst mit. Es liegt nachbin/console assets:installunterpublic/bundles/suluemailobfuscator/email-obfuscator.jsund lässt sich genauso ausvendor/in den eigenen Build importieren.
Twig
Gebraucht nur, wo die Vorlage die Sache selbst in der Hand hat:
{# eine Adresse aus einem Feld, Verweis in der Vorlage #} <a href="mailto:{{ address|sulu_obfuscate_email }}" {{ sulu_email_obfuscator_attribute() }}> {{ address|sulu_obfuscate_email }} </a> {# fertiges HTML, in dem Adressen stecken können #} {{ content.text|sulu_obfuscate_emails }}
Beides ist neben dem automatischen Umschreiben unschädlich: was die Marke trägt, wird kein zweites Mal angefasst.
sulu_obfuscate_emails gibt HTML zurück und wird deshalb nicht mehr maskiert. Er gehört nur an Werte, die ohnehin als HTML ausgegeben werden — also dorthin, wo vorher |raw stand. An einer ungeprüften Eingabe steht er falsch: dann liefe der Text ungefiltert in die Seite. Für eine einzelne Adresse aus einem Textfeld ist sulu_obfuscate_email der richtige Filter, der maskiert wie jeder andere Wert.
Was das Bundle nicht kann
- Ohne JavaScript bleibt die Adresse unlesbar. Das ist der Preis des Verfahrens, nicht ein Fehler. Wo das nicht tragbar ist, gehört eine Rufnummer oder ein Kontaktformular daneben.
- Es ist Abschreckung, kein Schutz. Wer ROT13 zurückdreht, hat die Adresse. Gemeint sind die Sammler, die Quelltexte nach dem Klammeraffen durchsuchen.
- Strukturierte Daten bleiben lesbar. Inhalte in
<script type="application/ld+json">werden bewusst nicht angefasst — Suchmaschinen und KI-Systeme sollen sie lesen können, und abweichen darf die Auszeichnung vom Sichtbaren ohnehin nicht. Steht dieselbe Adresse in einer FAQ-Antwort, steht sie damit auch im JSON-LD. Solche Antworten gehören umformuliert, nicht verdreht. - Formulare fasst es nicht an. Ein Kontaktformular ist eine andere Aufgabe, dafür gibt es SuluRecaptchaBundle.
Lizenz
MIT, siehe LICENSE.