
PHP-Lücke „php2shell“ unter Linux & Docker patchen
21. Oktober 2026
Hochsicherheits-Komplettanleitung: WordPress-Absicherung mit Authentik (SSO), Code Snippets & Fail2Ban unter ISPConfig
WordPress ist das meistgenutzte CMS der Welt und standardmäßig das Hauptziel für automatisierte Bot-Netzwerke, Brute-Force-Angriffe und Schwachstellen-Scans. Wenn du deine WordPress-Instanzen auf ein neues Sicherheitsniveau heben willst, reicht ein einfaches Sicherheits-Plugin oft nicht aus.
In dieser Anleitung bauen wir eine Enterprise-Infrastruktur im kleinen Rahmen: Wir verlagern das Identitätsmanagement vollständig zu Authentik, härten WordPress über performante PHP-Snippets, unterbinden API-Missbrauch und sperren Angreifer mittels Fail2Ban direkt auf Serverebene (OS-Ebene) aus – optimiert für Server, die mit ISPConfig betrieben werden.
🛠 Benötigte Komponenten & Plugins
Um diese Sicherheitsarchitektur umzusetzen, verzichten wir bewusst auf überladene All-in-One-Security-Suiten. Stattdessen setzen wir auf zwei schlanke, hochspezialisierte Plugins, die keine nennenswerte Serverlast erzeugen.
Installiere vorab die folgenden zwei Plugins über das WordPress-Repository (Plugins ➔ Installieren):
1. Code Snippets (von Code Snippets Pro)
• Zweck: Dieses Plugin dient als sauberer Container für unsere PHP-Hardening-Skripte (REST-API-Sperre, Rollen-Mapping und Logout-Redirect).
• Warum dieses Plugin? Es verhindert, dass wir den Code direkt in die functions.php deines Themes schreiben müssen. Bei einem Theme-Update würde dein Schutz andernfalls überschrieben und gelöscht. Zudem isoliert das Plugin fehlerhaften Code automatisch, sodass deine Seite bei einem Tippfehler nicht im White Screen of Death (Sitzungsabsturz) endet.
• Alternative: WPCode (ehemals Insert Headers and Footers).
2. OpenID Connect Generic Client (von daggerhart)
• Zweck: Das Herzstück für die Verbindung zwischen WordPress und deinem Identity Provider (IAM). Es bringt das standardisierte OpenID-Connect-Protokoll (OIDC) in dein WordPress.
• Warum dieses Plugin? Es ist komplett Open-Source, schlank, verzichtet auf künstliche Limitierungen bei den Benutzerzahlen und stellt genau die Programmierschnittstellen (Hooks & Filter) bereit, die wir für unseren automatischen Rollen-Abgleich mit Authentik benötigen.
Teil 1: Schnittstellen-Härtung per PHP-Snippet
Öffne nach der Installation das WordPress-Dashboard, navigiere zu Snippets ➔ Hinzufügen (Add New). Erstelle ein neues Snippet mit dem Namen Security Hardening: REST-API & XML-RPC Schutz, wähle Snippet überall ausführen und füge folgenden Code ein:
<?php
/**
* Security Hardening
* 1. REST-API für nicht angemeldete Benutzer sperren
* 2. XML-RPC Schnittstelle komplett deaktivieren
*/
// 1. REST-API Schutz
add_filter('rest_authentication_errors', function($result) {
if (!empty($result)) {
return $result;
}
if (!is_user_logged_in()) {
return new WP_Error(
'rest_not_logged_in',
__('Zugriff verweigert. Diese API ist nur für angemeldete Benutzer zugänglich.', 'textdomain'),
array('status' => 401)
);
}
return $result;
});
// 2. XML-RPC Schutz gegen Brute-Force-Attacken
add_filter('xmlrpc_enabled', '__return_false');
Was bewirkt das? Ungemeldete Bots, die /wp-json/ nach Benutzernamen durchsuchen, erhalten fortan einen HTTP 401 (Unauthorized). Die veraltete XML-RPC-Schnittstelle wird komplett deaktiviert.
Teil 2: Authentik OIDC-Anbindung & Rollen-Synchronisation
Navigiere zu Einstellungen ➔ OpenID Connect Client und konfiguriere die Optionen anhand deiner Authentik-Instanz.
1. Plugin-Konfiguration
Client-Einstellungen & Scopes
• Anmeldetyp: Automatische Anmeldung - SSO (Leitet ungemeldete User direkt zu Authentik um)
• Login Button Text: Login with Single Sign-On - SSO
• Client-ID / Secret: Trage hier die Daten deines Authentik-OIDC-Providers ein.
• OpenID Scope: openid email profile groups (Wichtig: Das Wort groups ist zwingend erforderlich!)
Endpunkt-URLs
Ersetze die Platzhalter durch die tatsächlichen URLs deiner Infrastruktur:
• Endpunkt-URL zur Anmeldung: https://auth.deine-domain.at/application/o/authorize/
• Endpunkt-URL zur Benutzerinfo: https://auth.deine-domain.at/application/o/userinfo/
• Endpunkt-URL zur Token-Verifizierung: https://auth.deine-domain.at/application/o/token/
• Endpunkt-URL zum Sitzungsende: https://auth.deine-domain.at/application/o/webseite-ihre-wordpressseite/end-session/
• JWKS URI: https://auth.deine-domain.at/application/o/webseite-ihre-wordpressseite/jwks/
• Issuer: https://auth.deine-domain.at/application/o/webseite-ihre-wordpressseite/
• Identitäts- & Spitznamen-Schlüssel: preferred_username
• Email-Format: {email}
WordPress-Benutzer-Einstellungen
• Existierende Benutzer verknüpfen: [X] Aktivieren (Verknüpft bestehende lokale Admin-Konten anhand der E-Mail)
• Benutzer erstellen, wenn er nicht existiert: [X] Aktivieren
• Zur Anmeldeansicht weiterleiten, wenn die Sitzung abgelaufen ist: [X] Aktivieren
2. Snippet: Automatischer Rollen-Sync (IAM)
Damit du WordPress-Rollen zentral über Authentik-Gruppen steuern kannst, füge ein zweites PHP-Snippet mit dem Namen Authentik OIDC: Automatischer Rollen-Sync hinzu. Wähle Snippet überall ausführen und setze die Ausführungspriorität auf 1:
<?php
/**
* Priorität 1: Klinkt sich direkt in den Datenstrom des OIDC-Plugins ein,
* BEVOR Wordfence oder andere Sicherheitsfilter den Login analysieren.
*/
add_filter('openid-connect-generic-user-creation-data', 'sync_authentik_before_security_filters', 1, 2);
add_filter('openid-connect-generic-user-update-data', 'sync_authentik_before_security_filters', 1, 2);
function sync_authentik_before_security_filters($user_data, $user_claim) {
if (!isset($user_claim['groups']) || !is_array($user_claim['groups'])) {
return $user_data;
}
$groups = array_map('strtolower', $user_claim['groups']);
// Mapping: Authentik-Gruppe (muss im System KLEIN geschrieben sein) => WordPress-Rolle
$role_mapping = [
'wp_admins' => 'administrator',
'wp_editors' => 'editor',
'wp_authors' => 'author',
'wp_contributors' => 'contributor',
'wp_subscribers' => 'subscriber'
];
$new_role = '';
foreach ($role_mapping as $auth_group => $wp_role) {
if (in_array($auth_group, $groups, true)) {
$new_role = $wp_role;
break;
}
}
if (empty($new_role)) {
$new_role = 'subscriber'; // Fallback-Schutz
}
$user_data['role'] = $new_role;
return $user_data;
}
Authentik-Voraussetzung: In Authentik muss im Provider das Standard-Scope-Mapping für Gruppen (authentik default OAuth Mapping: OpenID 'groups') aktiv sein, damit der groups-Claim im OIDC-Token übertragen wird.
3. Snippet: Single Logout (SLO)
Damit beim Klick auf "Abmelden" in WordPress auch die übergeordnete Authentik-Sitzung zerstört wird, lege ein drittes Snippet (Authentik OIDC: Single Logout Redirect) an:
<?php
add_filter('logout_redirect', 'redirect_to_authentik_after_logout', 10, 3);
function redirect_to_authentik_after_logout($redirect_to, $requested_redirect_to, $user) {
// Trage hier die exakte End-Session-URL aus deinen Authentik-Provider-Details ein
$authentik_logout_url = 'https://auth.deine-domain.at/application/o/webseite-ihre-wordpressseite/end-session/';
return $authentik_logout_url;
}
Ergänzung in Authentik: „WordPress Groups“ Property Mapping anlegen
Damit Authentik die Gruppen-Zugehörigkeit eines Benutzers überhaupt an WordPress übergibt, müssen wir ein benutzerdefiniertes Scope Property Mapping (Umfang-Zuordnung) in Authentik erstellen. Ohne diesen Schritt weiß der OIDC-Provider nicht, wie er das Feld groups befüllen soll.
Gehe in deiner Authentik-Administration auf Customization (Anpassung) ➔ Property Mappings (Eigenschaften-Zuordnung) und klicke auf Create (Erstellen). Wähle als Typ Scope Mapping (Umfang-Zuordnung) aus.
Konfiguriere das Mapping exakt wie im bereitgestellten Screenshot zu sehen:
• Mapping Name: WordPress Groups
• Bereichtsname (Scope Name): groups
(Das ist der Bezeichner, den wir später im WordPress-Plugin bei den Scopes eintragen!)
• Beschreibung: Kann optional befüllt oder leer gelassen werden.
• Ausdruck (Expression): Kopiere den folgenden Python-Code exakt in das Textfeld:
return {
"groups": [group.name for group in request.user.ak_groups.all()]
}
Klicke anschließend auf Save Changes (Änderungen speichern).
🔗 Wichtig: Das Mapping dem Provider zuweisen
Nachdem du das Mapping WordPress Groups erfolgreich erstellt hast, musst du es noch deinem WordPress-OIDC-Provider zuweisen:
1. Navigiere zu Applications ➔ Providers und bearbeite deinen WordPress-Provider.
2. Scrolle hinunter zu den Advanced Protocol Settings (Erweiterte Protokolleinstellungen).
3. Suche das Feld Property Mappings (Eigenschaften-Zuordnungen).
4. Stelle sicher, dass neben den Standard-Mappings (wie Profile und Email) nun auch dein neu angelegtes Mapping WordPress Groups ausgewählt ist.
Erst durch diese Verknüpfung liefert Authentik das groups-Array im Datenstrom mit, welches von unserem WordPress-Rollen-Sync-Snippet abgefangen und verarbeitet wird!
Ergänzung in Authentik: Gruppen im Verzeichnis anlegen
Damit das Rollen-Mapping funktioniert, musst du die entsprechenden Gruppen in Authentik anlegen. Das WordPress-Snippet sucht im OIDC-Token nach exakt definierten Gruppennamen.
Navigiere in deinem Authentik-Dashboard im linken Menü zu Verzeichnis (Directory) ➔ Gruppen (Groups) und klicke auf Erstellen (Create).
Lege die folgenden Gruppen exakt so an, wie im bereitgestellten Screenshot zu sehen:
• wp_admins – Mitglieder dieser Gruppe werden in WordPress zu Administratoren gemappt.
• wp_editors – Mitglieder dieser Gruppe werden in WordPress zu Redakteuren (Editors) gemappt.
• wp_authors – Mitglieder dieser Gruppe werden in WordPress zu Autoren (Authors) gemappt.
• wp_contributors – Mitglieder dieser Gruppe werden in WordPress zu Mitarbeitern (Contributors) gemappt.
• wp_subscribers – Mitglieder dieser Gruppe werden in WordPress zu Abonnenten (Subscribers) gemappt.
💡 Wichtiger Praxis-Tipp: Achte penibel auf die Kleinschreibung der Gruppennamen. Das WordPress-PHP-Snippet wandelt alle eingehenden Claims zur Sicherheit in Kleinbuchstaben um, um Fehler durch Tippweisen zu vermeiden. Die Namen müssen exakt mit den Definitionen im $role_mapping-Array deines WordPress-Snippets übereinstimmen.
Teil 3: Serverseitige Sperre mit Fail2Ban (ISPConfig)
Bots versuchen oft weiterhin stumpf, die wp-login.php direkt per POST-Request zu attackieren, obwohl der SSO-Login aktiv ist und sie abgewiesen werden. Da unsere Snippets diese Anfragen mit eindeutigen Fehlermeldungen blockieren, nutzen wir nun Fail2Ban, um diese IP-Adressen direkt auf OS-Ebene wegzusperren.
1. Fail2Ban Filter erstellen
Lege die Filterdatei auf deinem Linux-Server via SSH an:
sudo nano /etc/fail2ban/filter.d/wordpress.conf
Füge folgende Definition ein, welche die Nginx-Zugriffslogs scannt:
[Definition]
# Erkennt Bots, die trotz SSO auf wp-login zugreifen oder APIs missbrauchen
failregex = ^<HOST> \S+ \S+ \[.*\] "POST /wp-login\.php HTTP/\d\.\d" (503|401|403|404)
^<HOST> \S+ \S+ \[.*\] "GET /wp-login\.php HTTP/\d\.\d" (503|401|403|404)
^<HOST> \S+ \S+ \[.*\] "POST /xmlrpc\.php HTTP/\d\.\d" (200|503|401|403|404)
^<HOST> \S+ \S+ \[.*\] "GET /wp-json/.* HTTP/\d\.\d" (401|403)
ignoreregex =
2. Jail aktivieren
Öffne deine lokale Jail-Konfigurationsdatei:
sudo nano /etc/fail2ban/jail.local
Füge am Ende den folgenden Überwachungsblock hinzu. Das Wildcard * in der Pfadangabe ist für ISPConfig optimiert und sorgt dafür, dass die Logs aller angelegten WordPress-Instanzen gleichzeitig überwacht werden:
[wordpress] enabled = true port = http,https filter = wordpress logpath = /var/www/web*/log/access.log maxretry = 5 findtime = 600 bantime = 3600
3. Konfiguration testen und Service neu starten
Vor dem Aktivieren solltest du prüfen, ob deine Regex die Protokolldateien fehlerfrei einliest:
sudo fail2ban-regex /var/www/web1/log/access.log /etc/fail2ban/filter.d/wordpress.conf
(Hinweis für den Artikel: Die Leser müssen web1 durch die tatsächliche Web-ID ihrer WordPress-Instanz aus ISPConfig ersetzen.)
Verläuft der Test ohne Fehler, lade Fail2Ban neu und starte den Dienst zur Sicherheit einmal komplett frisch:
sudo fail2ban-client reload sudo systemctl restart fail2ban
Praxis-Empfehlungen: Best Practices für Wordfence & XML-RPC
1. Koexistenz mit Wordfence Security
Falls du das populäre Plugin Wordfence parallel für Firewall-Scans und Datei-Integritätsprüfungen einsetzt, sind durch das Authentik-Setup zwei Dinge zu beachten:
• Brute-Force-Schutz in Wordfence deaktivieren: Da Authentik die Benutzer-Authentifizierung vollständig übernimmt und Fail2Ban fehlerhafte API-Aufrufe blockiert, solltest du die Login Security und die Ratenbegrenzung für Anmeldeversuche in Wordfence abschalten. Andernfalls kann es zu Konflikten kommen, falls Wordfence berechtigte, schnelle SSO-Sitzungsaktualisierungen fälschlicherweise als Angriff einstuft.
• Die Priorität des Rollen-Syncs: Unser OIDC-Rollen-Sync-Snippet läuft mit Priorität 1. Das stellt sicher, dass der Benutzer seine korrekte Berechtigung zugewiesen bekommt, bevor die Wordfence-Firewall den Request analysiert. Dadurch kommt es zu keinen Fehlalarmen im Wordfence-Aktivitäten-Protokoll.
2. XML-RPC im System hart abschalten
Obwohl unser PHP-Snippet die XML-RPC-Schnittstelle innerhalb von WordPress über den Filter __return_false blockiert, verarbeitet PHP den Request dennoch für einen kurzen Moment. Wenn dein Server unter massivem XML-RPC-Dauerfeuer steht, kann dies die CPU-Last unnötig in die Höhe treiben.
Empfehlung: Blockiere die Datei xmlrpc.php zusätzlich direkt in den vhost-Einstellungen von ISPConfig.
• Bei der Nutzung von Nginx: Füge in den Direktiven der Webseite in ISPConfig folgenden Block hinzu:
location = /xmlrpc.php {
deny all;
access_log off;
log_not_found off;
}
Bei der Nutzung von Apache: Füge dies in die .htaccess-Datei im Stammverzeichnis deiner WordPress-Installation ein:
<Files xmlrpc.php>
Order Allow,Deny
Deny from all
</Files>
Durch diese serverseitige Sperre bricht der Webserver die Verbindung sofort ab, ohne PHP zu bemühen. Fail2Ban fängt den resultierenden HTTP 403 (Forbidden) ab und bannt die IP des Angreifers konsequent.
⚠️ Wichtiger Praxis-Tipp zu Cloudflare / Reverse Proxies:
Falls du Cloudflare vor deiner ISPConfig-Instanz nutzt, musst du sicherstellen, dass das Nginx-Modul real_ip aktiv ist (in ISPConfig vorkonfiguriert). Andernfalls sieht Fail2Ban nur die IP-Bereiche von Cloudflare und sperrt im schlimmsten Fall echte Besucher oder Cloudflare-Server aus!
Mit dieser Kombination schützt du deine WordPress-Infrastruktur effektiv vor Brute-Force-Attacken, während du gleichzeitig von einem modernen, zentralen Identitätsmanagement profitierst.



