<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Joomla/Plesk Patches &amp; Notes</title>
    <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/</link>
    <description>Merten Giesen sammelt Informationen und stellt diese öffentlich bereit.</description>
    <pubDate>Thu, 17 Sep 2026 09:53:10 +0000</pubDate>
    <item>
      <title>Joomla nach Backup-Restore: „Call to undefined method MenuHelper::getItems()“ beheben</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-nach-backup-restore-call-to-undefined-method-menuhelper-getitems</link>
      <description>&lt;![CDATA[Nach einem fehlgeschlagenen Joomla-Update kann es naheliegend sein, einfach ein wenige Tage altes Backup zurückzuspielen. Das funktioniert jedoch nicht immer so sauber, wie man erwarten würde.&#xA;&#xA;In meinem Fall war zuvor ein Update von Joomla 5.2.5 auf Joomla 5.4.x erfolgt. Nachdem anschließend Probleme mit einer Erweiterung auftraten, wurde ein Backup des älteren Joomla-5.2.5-Stands wiederhergestellt.&#xA;&#xA;Danach funktionierte das Joomla-Backend wieder, das Frontend jedoch nicht. Stattdessen erschien:&#xA;&#xA;Call to undefined method Joomla\Module\Menu\Site\Helper\MenuHelper::getItems()&#xA;&#xA;Das Problem&#xA;&#xA;Der Fehler deutet zunächst auf das Joomla-Menümodul beziehungsweise dessen PHP-Klassen hin.&#xA;&#xA;Auffällig war jedoch der zeitliche Zusammenhang:&#xA;&#xA;Joomla 5.2.5 funktionierte.&#xA;Das System wurde auf Joomla 5.4.x aktualisiert.&#xA;Danach traten Probleme auf.&#xA;Ein Backup des vorherigen Joomla-5.2.5-Stands wurde über die bestehende Installation zurückgespielt.&#xA;Backend-Zugriff war wieder möglich, das Frontend erzeugte jedoch den MenuHelper::getItems()-Fehler.&#xA;&#xA;Damit lag der Verdacht nahe, dass nicht die komplette Installation wieder exakt auf den alten Zustand zurückgesetzt worden war.&#xA;&#xA;Ursache: Ein Restore löscht nicht automatisch alle vorhandenen Dateien&#xA;&#xA;Der wichtige Punkt ist, wie ein typischer Joomla-Backup-Restore funktioniert.&#xA;&#xA;Beim Wiederherstellen werden Dateien aus dem Backup auf den Server geschrieben. Vorhandene Dateien mit demselben Namen werden dabei ersetzt.&#xA;&#xA;Dateien, die bereits auf dem Server liegen, aber im Backup nicht vorkommen, werden dagegen nicht zwangsläufig gelöscht.&#xA;&#xA;Das ist grundsätzlich sinnvoll. Ein Restore kann schließlich nicht sicher wissen, ob zusätzliche Dateien:&#xA;&#xA;zu einer neueren Softwareversion gehören,&#xA;bewusst manuell abgelegt wurden,&#xA;von einer Erweiterung stammen,&#xA;oder aus anderen Gründen erhalten bleiben sollen.&#xA;&#xA;Ein Restore ist deshalb nicht automatisch mit einer vollständigen Synchronisation des Dateisystems auf den Zustand des Backups gleichzusetzen.&#xA;&#xA;Warum das bei Joomla-Versionen problematisch werden kann&#xA;&#xA;Bei einem Joomla-Update können sich nicht nur Inhalte bestehender Dateien ändern.&#xA;&#xA;Zwischen Joomla-Versionen können auch Dateien:&#xA;&#xA;neu hinzukommen,&#xA;verschoben werden,&#xA;umbenannt werden,&#xA;oder vollständig entfallen.&#xA;&#xA;Wird anschließend ein Backup einer älteren Joomla-Version über diesen neueren Stand zurückgespielt, entsteht deshalb möglicherweise ein Mischzustand:&#xA;&#xA;Joomla 5.2.5 aus dem Backup&#xA;übrig gebliebene Dateien aus Joomla 5.4.x&#xA;=&#xA;potenziell inkonsistente Installation&#xA;&#xA;Das war in diesem Fall die naheliegende Erklärung für den Methodenfehler im Frontend.&#xA;&#xA;Wichtig ist die Unterscheidung:&#xA;&#xA;Gesichert ist, dass ein Restore zusätzliche vorhandene Dateien nicht zwingend entfernt und dadurch nach einem Versionswechsel Dateireste bestehen bleiben können.&#xA;&#xA;Nicht abschließend nachgewiesen wurde, welche konkrete verbliebene Datei den MenuHelper::getItems()-Fehler ausgelöst hatte.&#xA;&#xA;Lösung: Joomla-Dateien vor dem Restore bereinigen&#xA;&#xA;Statt das ältere Backup noch einmal einfach über die vorhandene Installation zu schreiben, sollte zunächst der bestehende Joomla-Core entfernt werden.&#xA;&#xA;Vorher sollte selbstverständlich ein Rückfallpunkt vorhanden sein. Bei einem VPS kann dafür beispielsweise ein Snapshot verwendet werden.&#xA;&#xA;Ein Snapshot eignet sich gut, um den kompletten Serverzustand vor solchen Änderungen kurzfristig wiederherstellen zu können. Er ersetzt allerdings nicht grundsätzlich ein dauerhaftes und extern verfügbares Backup.&#xA;&#xA;Joomla-Core-Verzeichnisse entfernen&#xA;&#xA;Vor dem erneuten Restore können insbesondere die Joomla-Core-Verzeichnisse entfernt werden:&#xA;&#xA;administrator&#xA;api&#xA;cli&#xA;components&#xA;includes&#xA;language&#xA;layouts&#xA;libraries&#xA;media&#xA;modules&#xA;plugins&#xA;templates&#xA;&#xA;Außerdem gehören zentrale Joomla-Dateien dazu, beispielsweise:&#xA;&#xA;configuration.php&#xA;index.php&#xA;&#xA;Danach wird das Backup erneut in das nun bereinigte Webverzeichnis eingespielt.&#xA;&#xA;Achtung bei eigenen Dateien&#xA;&#xA;Die Liste darf nicht blind auf jede Installation angewendet werden.&#xA;&#xA;Vor dem Löschen muss geprüft werden, ob sich in diesen Verzeichnissen eigene Dateien oder nicht anderweitig gesicherte Inhalte befinden.&#xA;&#xA;Das gilt insbesondere für:&#xA;&#xA;media&#xA;templates&#xA;&#xA;Dort können je nach Installation beispielsweise eigene Medien, Template-Anpassungen oder zusätzliche Dateien liegen.&#xA;&#xA;Der entscheidende Punkt ist daher nicht:&#xA;&#xA;  Lösche immer exakt diese Verzeichnisse.&#xA;&#xA;Sondern:&#xA;&#xA;  Stelle sicher, dass vor dem Restore keine Dateien einer anderen Joomla-Version im Zielsystem zurückbleiben.&#xA;&#xA;Datenbank beim Restore&#xA;&#xA;Auch die Datenbank sollte zum Backup-Zustand passen.&#xA;&#xA;Beim Restore sollte derselbe Tabellenpräfix verwendet werden wie in der gesicherten Joomla-Installation.&#xA;&#xA;Wenn das verwendete Restore-Werkzeug die Möglichkeit bietet, vorhandene Tabellen mit demselben Präfix vor dem Import zu entfernen, verhindert das ebenfalls einen Mischstand aus alter und neuer Installation.&#xA;&#xA;Dateisystem und Datenbank sollten nach dem Restore gemeinsam zum selben Sicherungszeitpunkt gehören.&#xA;&#xA;Optional: Joomla-Autoload-Cache neu erzeugen&#xA;&#xA;Falls anschließend weiterhin Fehler auftreten, bei denen Namespaces oder Joomla-Klassen offenbar nicht zum Dateibestand passen, kann zusätzlich der Joomla-Autoload-Cache relevant sein.&#xA;&#xA;Die entsprechende Cache-Datei liegt unter:&#xA;&#xA;administrator/cache/autoload_psr4.php&#xA;&#xA;Sie kann gelöscht werden, damit Joomla sie neu erzeugt.&#xA;&#xA;Das ist jedoch ein zusätzlicher Reparaturschritt und ersetzt nicht die Bereinigung eines tatsächlich inkonsistenten Dateibestands.&#xA;&#xA;Warum einfaches „Backup drüberspielen“ nicht genügt&#xA;&#xA;Der entscheidende Unterschied ist:&#xA;&#xA;Restore:&#xA;Backup-Dateien auf das vorhandene Dateisystem schreiben&#xA;&#xA;ist nicht zwingend dasselbe wie:&#xA;&#xA;Rollback:&#xA;Das gesamte Dateisystem exakt auf einen früheren Zustand zurücksetzen&#xA;&#xA;Ein VPS-Snapshot kommt dem zweiten Fall deutlich näher, weil dabei der komplette gespeicherte Systemzustand zurückgesetzt wird.&#xA;&#xA;Ein Joomla-Backup-Restore arbeitet dagegen innerhalb des vorhandenen Dateisystems. Deshalb können Dateien überleben, die zum Zeitpunkt des Backups noch gar nicht existierten.&#xA;&#xA;Ergebnis&#xA;&#xA;Für fehlgeschlagene Joomla-Updates ergibt sich daraus ein wichtiger Grundsatz:&#xA;&#xA;Wenn ein Backup einer älteren Joomla-Version nach einem Update wiederhergestellt werden soll, sollte man vermeiden, es einfach über den neueren Dateibestand zu entpacken.&#xA;&#xA;Sicherer ist:&#xA;&#xA;Rückfallpunkt erstellen.&#xA;Eigene Dateien und Anpassungen sichern beziehungsweise identifizieren.&#xA;Vorhandenen Joomla-Core bereinigen.&#xA;Backup wiederherstellen.&#xA;Datenbank auf denselben Sicherungsstand bringen.&#xA;Frontend und Backend testen.&#xA;Bei Namespace-Problemen gegebenenfalls den Joomla-Autoload-Cache neu erzeugen.&#xA;&#xA;Ein Fehler wie&#xA;&#xA;Call to undefined method Joomla\Module\Menu\Site\Helper\MenuHelper::getItems()&#xA;&#xA;muss damit nicht zwangsläufig bedeuten, dass das Menümodul selbst defekt ist.&#xA;&#xA;Nach einem Restore über eine andere Joomla-Version hinweg sollte auch geprüft werden, ob schlicht Dateien unterschiedlicher Joomla-Versionen miteinander vermischt wurden.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Nach einem fehlgeschlagenen Joomla-Update kann es naheliegend sein, einfach ein wenige Tage altes Backup zurückzuspielen. Das funktioniert jedoch nicht immer so sauber, wie man erwarten würde.</p>

<p>In meinem Fall war zuvor ein Update von Joomla 5.2.5 auf Joomla 5.4.x erfolgt. Nachdem anschließend Probleme mit einer Erweiterung auftraten, wurde ein Backup des älteren Joomla-5.2.5-Stands wiederhergestellt.</p>

<p>Danach funktionierte das Joomla-Backend wieder, das Frontend jedoch nicht. Stattdessen erschien:</p>

<pre><code class="language-text">Call to undefined method Joomla\Module\Menu\Site\Helper\MenuHelper::getItems()
</code></pre>

<h2 id="das-problem">Das Problem</h2>

<p>Der Fehler deutet zunächst auf das Joomla-Menümodul beziehungsweise dessen PHP-Klassen hin.</p>

<p>Auffällig war jedoch der zeitliche Zusammenhang:</p>
<ol><li>Joomla 5.2.5 funktionierte.</li>
<li>Das System wurde auf Joomla 5.4.x aktualisiert.</li>
<li>Danach traten Probleme auf.</li>
<li>Ein Backup des vorherigen Joomla-5.2.5-Stands wurde über die bestehende Installation zurückgespielt.</li>
<li>Backend-Zugriff war wieder möglich, das Frontend erzeugte jedoch den <code>MenuHelper::getItems()</code>-Fehler.</li></ol>

<p>Damit lag der Verdacht nahe, dass nicht die komplette Installation wieder exakt auf den alten Zustand zurückgesetzt worden war.</p>

<h2 id="ursache-ein-restore-löscht-nicht-automatisch-alle-vorhandenen-dateien">Ursache: Ein Restore löscht nicht automatisch alle vorhandenen Dateien</h2>

<p>Der wichtige Punkt ist, wie ein typischer Joomla-Backup-Restore funktioniert.</p>

<p>Beim Wiederherstellen werden Dateien aus dem Backup auf den Server geschrieben. Vorhandene Dateien mit demselben Namen werden dabei ersetzt.</p>

<p>Dateien, die bereits auf dem Server liegen, aber <strong>im Backup nicht vorkommen</strong>, werden dagegen nicht zwangsläufig gelöscht.</p>

<p>Das ist grundsätzlich sinnvoll. Ein Restore kann schließlich nicht sicher wissen, ob zusätzliche Dateien:</p>
<ul><li>zu einer neueren Softwareversion gehören,</li>
<li>bewusst manuell abgelegt wurden,</li>
<li>von einer Erweiterung stammen,</li>
<li>oder aus anderen Gründen erhalten bleiben sollen.</li></ul>

<p>Ein Restore ist deshalb nicht automatisch mit einer vollständigen Synchronisation des Dateisystems auf den Zustand des Backups gleichzusetzen.</p>

<h2 id="warum-das-bei-joomla-versionen-problematisch-werden-kann">Warum das bei Joomla-Versionen problematisch werden kann</h2>

<p>Bei einem Joomla-Update können sich nicht nur Inhalte bestehender Dateien ändern.</p>

<p>Zwischen Joomla-Versionen können auch Dateien:</p>
<ul><li>neu hinzukommen,</li>
<li>verschoben werden,</li>
<li>umbenannt werden,</li>
<li>oder vollständig entfallen.</li></ul>

<p>Wird anschließend ein Backup einer älteren Joomla-Version über diesen neueren Stand zurückgespielt, entsteht deshalb möglicherweise ein Mischzustand:</p>

<pre><code class="language-text">Joomla 5.2.5 aus dem Backup
+
übrig gebliebene Dateien aus Joomla 5.4.x
=
potenziell inkonsistente Installation
</code></pre>

<p>Das war in diesem Fall die naheliegende Erklärung für den Methodenfehler im Frontend.</p>

<p>Wichtig ist die Unterscheidung:</p>

<p><strong>Gesichert ist</strong>, dass ein Restore zusätzliche vorhandene Dateien nicht zwingend entfernt und dadurch nach einem Versionswechsel Dateireste bestehen bleiben können.</p>

<p><strong>Nicht abschließend nachgewiesen wurde</strong>, welche konkrete verbliebene Datei den <code>MenuHelper::getItems()</code>-Fehler ausgelöst hatte.</p>

<h2 id="lösung-joomla-dateien-vor-dem-restore-bereinigen">Lösung: Joomla-Dateien vor dem Restore bereinigen</h2>

<p>Statt das ältere Backup noch einmal einfach über die vorhandene Installation zu schreiben, sollte zunächst der bestehende Joomla-Core entfernt werden.</p>

<p>Vorher sollte selbstverständlich ein Rückfallpunkt vorhanden sein. Bei einem VPS kann dafür beispielsweise ein Snapshot verwendet werden.</p>

<p>Ein Snapshot eignet sich gut, um den kompletten Serverzustand vor solchen Änderungen kurzfristig wiederherstellen zu können. Er ersetzt allerdings nicht grundsätzlich ein dauerhaftes und extern verfügbares Backup.</p>

<h3 id="joomla-core-verzeichnisse-entfernen">Joomla-Core-Verzeichnisse entfernen</h3>

<p>Vor dem erneuten Restore können insbesondere die Joomla-Core-Verzeichnisse entfernt werden:</p>

<pre><code class="language-text">administrator
api
cli
components
includes
language
layouts
libraries
media
modules
plugins
templates
</code></pre>

<p>Außerdem gehören zentrale Joomla-Dateien dazu, beispielsweise:</p>

<pre><code class="language-text">configuration.php
index.php
</code></pre>

<p>Danach wird das Backup erneut in das nun bereinigte Webverzeichnis eingespielt.</p>

<h3 id="achtung-bei-eigenen-dateien">Achtung bei eigenen Dateien</h3>

<p>Die Liste darf nicht blind auf jede Installation angewendet werden.</p>

<p>Vor dem Löschen muss geprüft werden, ob sich in diesen Verzeichnissen eigene Dateien oder nicht anderweitig gesicherte Inhalte befinden.</p>

<p>Das gilt insbesondere für:</p>

<pre><code class="language-text">media
templates
</code></pre>

<p>Dort können je nach Installation beispielsweise eigene Medien, Template-Anpassungen oder zusätzliche Dateien liegen.</p>

<p>Der entscheidende Punkt ist daher nicht:</p>

<blockquote><p>Lösche immer exakt diese Verzeichnisse.</p></blockquote>

<p>Sondern:</p>

<blockquote><p>Stelle sicher, dass vor dem Restore keine Dateien einer anderen Joomla-Version im Zielsystem zurückbleiben.</p></blockquote>

<h2 id="datenbank-beim-restore">Datenbank beim Restore</h2>

<p>Auch die Datenbank sollte zum Backup-Zustand passen.</p>

<p>Beim Restore sollte derselbe Tabellenpräfix verwendet werden wie in der gesicherten Joomla-Installation.</p>

<p>Wenn das verwendete Restore-Werkzeug die Möglichkeit bietet, vorhandene Tabellen mit demselben Präfix vor dem Import zu entfernen, verhindert das ebenfalls einen Mischstand aus alter und neuer Installation.</p>

<p>Dateisystem und Datenbank sollten nach dem Restore gemeinsam zum selben Sicherungszeitpunkt gehören.</p>

<h2 id="optional-joomla-autoload-cache-neu-erzeugen">Optional: Joomla-Autoload-Cache neu erzeugen</h2>

<p>Falls anschließend weiterhin Fehler auftreten, bei denen Namespaces oder Joomla-Klassen offenbar nicht zum Dateibestand passen, kann zusätzlich der Joomla-Autoload-Cache relevant sein.</p>

<p>Die entsprechende Cache-Datei liegt unter:</p>

<pre><code class="language-text">administrator/cache/autoload_psr4.php
</code></pre>

<p>Sie kann gelöscht werden, damit Joomla sie neu erzeugt.</p>

<p>Das ist jedoch ein zusätzlicher Reparaturschritt und ersetzt nicht die Bereinigung eines tatsächlich inkonsistenten Dateibestands.</p>

<h2 id="warum-einfaches-backup-drüberspielen-nicht-genügt">Warum einfaches „Backup drüberspielen“ nicht genügt</h2>

<p>Der entscheidende Unterschied ist:</p>

<pre><code class="language-text">Restore:
Backup-Dateien auf das vorhandene Dateisystem schreiben
</code></pre>

<p>ist nicht zwingend dasselbe wie:</p>

<pre><code class="language-text">Rollback:
Das gesamte Dateisystem exakt auf einen früheren Zustand zurücksetzen
</code></pre>

<p>Ein VPS-Snapshot kommt dem zweiten Fall deutlich näher, weil dabei der komplette gespeicherte Systemzustand zurückgesetzt wird.</p>

<p>Ein Joomla-Backup-Restore arbeitet dagegen innerhalb des vorhandenen Dateisystems. Deshalb können Dateien überleben, die zum Zeitpunkt des Backups noch gar nicht existierten.</p>

<h2 id="ergebnis">Ergebnis</h2>

<p>Für fehlgeschlagene Joomla-Updates ergibt sich daraus ein wichtiger Grundsatz:</p>

<p><strong>Wenn ein Backup einer älteren Joomla-Version nach einem Update wiederhergestellt werden soll, sollte man vermeiden, es einfach über den neueren Dateibestand zu entpacken.</strong></p>

<p>Sicherer ist:</p>
<ol><li>Rückfallpunkt erstellen.</li>
<li>Eigene Dateien und Anpassungen sichern beziehungsweise identifizieren.</li>
<li>Vorhandenen Joomla-Core bereinigen.</li>
<li>Backup wiederherstellen.</li>
<li>Datenbank auf denselben Sicherungsstand bringen.</li>
<li>Frontend und Backend testen.</li>
<li>Bei Namespace-Problemen gegebenenfalls den Joomla-Autoload-Cache neu erzeugen.</li></ol>

<p>Ein Fehler wie</p>

<pre><code class="language-text">Call to undefined method Joomla\Module\Menu\Site\Helper\MenuHelper::getItems()
</code></pre>

<p>muss damit nicht zwangsläufig bedeuten, dass das Menümodul selbst defekt ist.</p>

<p>Nach einem Restore über eine andere Joomla-Version hinweg sollte auch geprüft werden, ob schlicht <strong>Dateien unterschiedlicher Joomla-Versionen miteinander vermischt wurden</strong>.</p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-nach-backup-restore-call-to-undefined-method-menuhelper-getitems</guid>
      <pubDate>Wed, 16 Sep 2026 13:16:25 +0000</pubDate>
    </item>
    <item>
      <title>Joomla aufräumen: Datenbank, alte Erweiterungen, Discover und tmp-Verzeichnis</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-aufraeumen-datenbank-alte-erweiterungen-discover-und-tmp-verzeichnis</link>
      <description>&lt;![CDATA[Eine Joomla-Installation, die über viele Jahre betrieben und immer wieder erweitert wurde, sammelt mit der Zeit einiges an Ballast an. Erweiterungen werden installiert, ausprobiert und irgendwann wieder deinstalliert. Manche hinterlassen Datenbanktabellen, andere Dateien im Dateisystem. Gleichzeitig können Tabellen wie die Session-Tabelle über Jahre massiv anwachsen, wenn die automatische Bereinigung nicht zuverlässig funktioniert.&#xA;&#xA;Dieser Artikel beschreibt einen praktischen Weg, um eine gewachsene Joomla-Installation schrittweise zu untersuchen und aufzuräumen.&#xA;&#xA;Wichtig: Vor Änderungen an der Datenbank sollte immer ein aktuelles Backup vorhanden sein.&#xA;&#xA;Das Problem&#xA;&#xA;Bei einer älteren Joomla-Installation fiel auf, dass die Datenbank ungewöhnlich groß geworden war. Gleichzeitig zeigte der Joomla-Bereich System → Discover mehr als hundert Erweiterungen an, obwohl viele davon längst nicht mehr bewusst genutzt wurden.&#xA;&#xA;Die Fragen waren deshalb:&#xA;&#xA;Welche Tabellen belegen besonders viel Speicher?&#xA;Welche Tabellen gehören zum Joomla-Core?&#xA;Welche stammen von noch installierten Erweiterungen?&#xA;Welche sind wahrscheinlich Reste längst deinstallierter Erweiterungen?&#xA;Was bedeutet die lange Liste unter „Discover“?&#xA;Können temporäre Dateien im Joomla-Verzeichnis gelöscht werden?&#xA;&#xA;Statt sofort Tabellen zu löschen, lohnt sich zunächst eine Bestandsaufnahme.&#xA;&#xA;---&#xA;&#xA;1. Datenbanktabellen nach Größe untersuchen&#xA;&#xA;Für eine erste Analyse braucht man nicht zwingend einen kompletten SQL-Dump mit allen Inhalten. Oft genügt eine Übersicht der Tabellen einschließlich Größe und ungefährer Zeilenzahl.&#xA;&#xA;In phpMyAdmin kann innerhalb der betreffenden Datenbank folgende Abfrage ausgeführt werden:&#xA;&#xA;SELECT&#xA;  tablename AS Tabelle,&#xA;  engine AS Engine,&#xA;  tablerows AS Zeilen,&#xA;  ROUND((datalength + indexlength) / 1024 / 1024, 2) AS GroesseMB,&#xA;  createtime AS Erstellt,&#xA;  updatetime AS LetzteAenderung&#xA;FROM informationschema.tables&#xA;WHERE tableschema = DATABASE()&#xA;ORDER BY (datalength + indexlength) DESC;&#xA;&#xA;Das Ergebnis lässt sich beispielsweise als CSV exportieren.&#xA;&#xA;Die Abfrage liefert unter anderem:&#xA;&#xA;Tabellenname&#xA;verwendete Storage Engine&#xA;ungefähre Anzahl der Datensätze&#xA;Größe von Daten und Indizes&#xA;Erstellungs- und Änderungszeitpunkte, soweit verfügbar&#xA;&#xA;Bei InnoDB ist die angegebene Zeilenzahl nicht zwingend exakt. Für eine erste Größenanalyse reicht sie aber aus.&#xA;&#xA;---&#xA;&#xA;2. Eine extrem große Joomla-Session-Tabelle&#xA;&#xA;Besonders auffällig kann die Joomla-Tabelle&#xA;&#xA;_session&#xA;&#xA;werden.&#xA;&#xA;In dem untersuchten Fall enthielt sie mehr als vier Millionen Datensätze und belegte mehrere Gigabyte.&#xA;&#xA;Dabei handelt es sich nicht um einen Datei- oder Bildcache.&#xA;&#xA;Die Session-Tabelle speichert Sitzungsinformationen. Dazu gehören beispielsweise anonyme Besucher-Sessions und Sitzungen eingeloggter Benutzer beziehungsweise Administratoren.&#xA;&#xA;Bilder, CSS-Dateien oder andere Mediendateien liegen dort nicht.&#xA;&#xA;Alte Sessions zählen&#xA;&#xA;Zunächst kann geprüft werden, wie viele Einträge älter als sieben Tage sind:&#xA;&#xA;SELECT COUNT() AS aelter7tage&#xA;FROM #session&#xA;WHERE time &lt; UNIXTIMESTAMP(NOW() - INTERVAL 7 DAY);&#xA;&#xA;Der Präfix # muss dabei durch den tatsächlichen Tabellenpräfix der Joomla-Installation ersetzt werden.&#xA;&#xA;Zusätzlich kann die Gesamtzahl ermittelt werden:&#xA;&#xA;SELECT COUNT() AS gesamt&#xA;FROM #session;&#xA;&#xA;Im Praxisfall waren nahezu sämtliche Einträge älter als sieben Tage. Die Tabelle bestand damit fast vollständig aus alten Sessions.&#xA;&#xA;---&#xA;&#xA;3. Alte Session-Daten löschen&#xA;&#xA;Eine vorsichtige Möglichkeit besteht darin, alte Einträge portionsweise zu löschen:&#xA;&#xA;DELETE FROM #_session&#xA;WHERE time &lt; UNIXTIMESTAMP(NOW() - INTERVAL 7 DAY)&#xA;ORDER BY time&#xA;LIMIT 100000;&#xA;&#xA;Bei mehreren Millionen Datensätzen kann das allerdings sehr lange dauern.&#xA;&#xA;Im konkreten Fall benötigte bereits das Löschen von 100.000 Datensätzen deutlich mehr als zwei Minuten. Auch größere Blöcke machten die Bereinigung entsprechend langwierig.&#xA;&#xA;Alternative: Session-Tabelle vollständig leeren&#xA;&#xA;Wenn die Website keine angemeldeten Frontend-Benutzer hat und es akzeptabel ist, dass momentan angemeldete Administratoren ihre Sitzung verlieren, kann die Session-Tabelle auch vollständig geleert werden:&#xA;&#xA;TRUNCATE TABLE #session;&#xA;&#xA;Das ist erheblich schneller als Millionen einzelne Datensätze mit DELETE zu entfernen.&#xA;&#xA;Die Folge ist:&#xA;&#xA;bestehende Sessions gehen verloren,&#xA;Administratoren müssen sich gegebenenfalls neu anmelden,&#xA;Dateien, Bilder und Inhalte werden nicht gelöscht.&#xA;&#xA;Für eine reine Session-Tabelle kann das bei einer Wartung eine sinnvolle Lösung sein.&#xA;&#xA;---&#xA;&#xA;4. Warum bleiben nach einer Joomla-Deinstallation Tabellen übrig?&#xA;&#xA;Bei älteren Joomla-Installationen finden sich häufig Tabellen von Erweiterungen, die längst nicht mehr verwendet werden.&#xA;&#xA;Eine Deinstallation entfernt nicht zwangsläufig alle Daten.&#xA;&#xA;Ob Datenbanktabellen beim Entfernen einer Erweiterung ebenfalls gelöscht werden, hängt davon ab, wie die jeweilige Erweiterung ihre Deinstallation implementiert.&#xA;&#xA;Deshalb können über Jahre komplette Tabellenfamilien zurückbleiben.&#xA;&#xA;Typisch sind Namen wie:&#xA;&#xA;extensionname&#xA;_extensionnamefiles&#xA;_extensionnamestatistics&#xA;_extensionnamecategories&#xA;_extensionnameconfig&#xA;&#xA;Solche Tabellen sollte man allerdings nicht allein aufgrund ihres Namens löschen.&#xA;&#xA;Zunächst muss geklärt werden, ob die zugehörige Erweiterung noch installiert ist.&#xA;&#xA;---&#xA;&#xA;5. Prüfen, ob eine Erweiterung noch installiert ist&#xA;&#xA;Joomla führt installierte Erweiterungen in der Tabelle&#xA;&#xA;_extensions&#xA;&#xA;Eine einfache Abfrage kann beispielsweise so aussehen:&#xA;&#xA;SELECT&#xA;  extensionid,&#xA;  name,&#xA;  type,&#xA;  element,&#xA;  folder,&#xA;  enabled&#xA;FROM #_extensions&#xA;ORDER BY name, type;&#xA;&#xA;Bei einer bestimmten Erweiterung kann gezielter gesucht werden:&#xA;&#xA;SELECT&#xA;  extensionid,&#xA;  name,&#xA;  type,&#xA;  element,&#xA;  folder,&#xA;  enabled&#xA;FROM #_extensions&#xA;WHERE LOWER(CONCATWS(&#39; &#39;, name, type, element, folder))&#xA;      REGEXP &#39;name-der-erweiterung&#39;;&#xA;&#xA;Das ermöglicht eine wichtige Unterscheidung:&#xA;&#xA;Erweiterung noch installiert&#xA;&#xA;Dann sollten die zugehörigen Tabellen nicht einfach gelöscht werden.&#xA;&#xA;Auch eine deaktivierte Erweiterung kann weiterhin vollständig installiert sein.&#xA;&#xA;Erweiterung nicht mehr installiert&#xA;&#xA;Existieren trotzdem noch passende Datenbanktabellen, handelt es sich sehr wahrscheinlich um Altlasten.&#xA;&#xA;Vor dem Löschen sollte trotzdem geprüft werden, ob die Daten noch benötigt werden.&#xA;&#xA;Manche Tabellen enthalten nicht nur Konfiguration, sondern tatsächliche Inhalte, Formulareingaben, Statistiken oder Dokumentinformationen.&#xA;&#xA;---&#xA;&#xA;6. Tabellen einer alten Erweiterung löschen&#xA;&#xA;Ist eindeutig geklärt, dass eine Erweiterung nicht mehr verwendet wird und ihre Daten ebenfalls nicht mehr benötigt werden, können die verbliebenen Tabellen entfernt werden.&#xA;&#xA;Die vorhandenen Tabellen lassen sich zunächst anzeigen:&#xA;&#xA;SHOW TABLES LIKE &#39;%extensionname%&#39;;&#xA;&#xA;Eine nützliche Variante erzeugt zunächst nur die passenden DROP TABLE-Befehle:&#xA;&#xA;SELECT CONCAT(&#xA;  &#39;DROP TABLE `&#39;,&#xA;  tablename,&#xA;  &#39;`;&#39;&#xA;) AS dropsql&#xA;FROM informationschema.tables&#xA;WHERE tableschema = DATABASE()&#xA;  AND tablename LIKE &#39;%extensionname%&#39;;&#xA;&#xA;Die erzeugten Befehle können anschließend überprüft werden.&#xA;&#xA;Erst danach sollte tatsächlich gelöscht werden.&#xA;&#xA;Beispiel:&#xA;&#xA;DROP TABLE IF EXISTS&#xA;  prefixextensionname,&#xA;  prefixextensionnamefiles,&#xA;  prefixextensionnamestatistics;&#xA;&#xA;Das ist sicherer, als Tabellen anhand eines vermuteten Namensschemas automatisch zu entfernen.&#xA;&#xA;---&#xA;&#xA;7. Nicht jede große Tabelle ist eine Altlast&#xA;&#xA;Eine wichtige Erkenntnis bei solchen Aufräumarbeiten:&#xA;&#xA;Große Tabellen sind nicht automatisch überflüssig.&#xA;&#xA;Zu Joomla selbst gehören beispielsweise Tabellenfamilien für Funktionen wie:&#xA;&#xA;_finder&#xA;_redirectlinks&#xA;_actionlogs&#xA;_workflow&#xA;_scheduler&#xA;guidedtours*&#xA;history&#xA;&#xA;Auch installierte Drittanbieter-Erweiterungen können große Tabellen besitzen, etwa für:&#xA;&#xA;Statistiken&#xA;Revisionen&#xA;Suchindizes&#xA;Formulardaten&#xA;Dateiverwaltung&#xA;Protokolle&#xA;&#xA;Solche Tabellen sollten nicht entfernt werden, solange die zugehörige Funktion oder Erweiterung noch verwendet wird.&#xA;&#xA;Hier ist gegebenenfalls eine Bereinigung der Inhalte sinnvoller als das Löschen der gesamten Tabelle.&#xA;&#xA;---&#xA;&#xA;8. Was bedeutet „Discover“ in Joomla?&#xA;&#xA;Unter&#xA;&#xA;System → Discover&#xA;&#xA;kann Joomla Erweiterungen anzeigen, die im Dateisystem gefunden werden, aber nicht regulär als installierte Erweiterungen registriert sind.&#xA;&#xA;Das ist etwas anderes als eine übrig gebliebene Datenbanktabelle.&#xA;&#xA;Ein Discover-Eintrag bedeutet im Kern:&#xA;&#xA;Joomla findet Dateien einer Erweiterung, aber der entsprechende Installationszustand passt nicht dazu.&#xA;&#xA;Solche Reste können beispielsweise in Verzeichnissen liegen wie:&#xA;&#xA;administrator/components/&#xA;components/&#xA;plugins/&#xA;modules/&#xA;administrator/modules/&#xA;&#xA;Das erklärt auch, warum eine längst deinstallierte Erweiterung weiterhin unter „Discover“ auftauchen kann.&#xA;&#xA;Ihre Datenbanktabellen können bereits verschwunden sein, während Teile der Erweiterung noch auf dem Webserver liegen.&#xA;&#xA;---&#xA;&#xA;9. Discover-Einträge bereinigen&#xA;&#xA;Ein Discover-Eintrag sollte nicht einfach blind entfernt werden.&#xA;&#xA;Zunächst muss geklärt werden:&#xA;&#xA;Wird die Erweiterung noch verwendet?&#xA;Ist sie regulär installiert?&#xA;Handelt es sich tatsächlich nur um übrig gebliebene Dateien?&#xA;&#xA;Wenn eindeutig feststeht, dass die Erweiterung nicht mehr gebraucht wird, können die zugehörigen Dateireste entfernt werden.&#xA;&#xA;Dabei reicht es nicht immer, nur&#xA;&#xA;administrator/components/combeispiel&#xA;&#xA;zu löschen.&#xA;&#xA;Eine Erweiterung kann zusätzlich Dateien in weiteren Bereichen besitzen, beispielsweise:&#xA;&#xA;components/combeispiel&#xA;plugins/...&#xA;modules/...&#xA;administrator/modules/...&#xA;&#xA;Nach dem Entfernen der verbliebenen Erweiterungsdateien sollte Joomla sie bei einer erneuten Discover-Suche nicht mehr erkennen.&#xA;&#xA;Datenbankreste und Discover-Einträge sind damit zwei getrennte Baustellen:&#xA;&#xA;alte Tabellen → Datenbank&#xA;Discover-Einträge → meist übrig gebliebene Dateien beziehungsweise ein inkonsistenter Installationszustand&#xA;&#xA;---&#xA;&#xA;10. Was kann aus dem Joomla-tmp-Verzeichnis gelöscht werden?&#xA;&#xA;Auch im Joomla-Verzeichnis&#xA;&#xA;tmp/&#xA;&#xA;können sich über Jahre Dateien ansammeln.&#xA;&#xA;Joomla verwendet dieses Verzeichnis als temporären Arbeitsbereich, unter anderem bei Installationen und Updates von Erweiterungen.&#xA;&#xA;Dort können beispielsweise liegen:&#xA;&#xA;heruntergeladene Installationsarchive,&#xA;entpackte Erweiterungen,&#xA;Reste abgebrochener Installationen,&#xA;andere temporäre Dateien.&#xA;&#xA;Alte Inhalte dieses Verzeichnisses können in der Regel entfernt werden, wenn gerade&#xA;&#xA;kein Joomla-Update,&#xA;keine Erweiterungsinstallation,&#xA;kein Restore&#xA;und kein anderer Wartungsvorgang&#xA;&#xA;läuft.&#xA;&#xA;Der Ordner tmp selbst sollte dagegen bestehen bleiben.&#xA;&#xA;Auch sollte der konfigurierte tmp-Pfad nicht verändert werden, wenn die Installation korrekt funktioniert.&#xA;&#xA;---&#xA;&#xA;11. Sinnvolle Reihenfolge beim Aufräumen&#xA;&#xA;Für eine ältere Joomla-Installation hat sich folgende Reihenfolge als übersichtlich erwiesen:&#xA;&#xA;1. Backup erstellen&#xA;&#xA;Vor Änderungen an Datenbank oder Dateisystem.&#xA;&#xA;2. Tabellen nach Größe analysieren&#xA;&#xA;Mit information_schema.tables.&#xA;&#xA;3. Extrem große technische Tabellen untersuchen&#xA;&#xA;Zum Beispiel #session.&#xA;&#xA;4. Installationsstatus von Erweiterungen prüfen&#xA;&#xA;Über #extensions.&#xA;&#xA;5. Verwaiste Datenbanktabellen identifizieren&#xA;&#xA;Nur löschen, wenn die Erweiterung sicher nicht mehr benötigt wird.&#xA;&#xA;6. Joomla „Discover“ kontrollieren&#xA;&#xA;Übrig gebliebene Erweiterungsdateien identifizieren.&#xA;&#xA;7. Dateireste entfernen&#xA;&#xA;Dabei alle zugehörigen Erweiterungspfade berücksichtigen.&#xA;&#xA;8. tmp bereinigen&#xA;&#xA;Nur alte temporäre Inhalte entfernen, nicht den Ordner selbst.&#xA;&#xA;---&#xA;&#xA;Ergebnis&#xA;&#xA;Bei einer über Jahre gewachsenen Joomla-Installation lohnt sich ein systematisches Aufräumen.&#xA;&#xA;Der größte Speicherverbrauch muss dabei nicht einmal von alten Erweiterungen kommen. Im beschriebenen Praxisfall war zunächst die Session-Tabelle das deutlichste Problem: Millionen alter Sessions hatten sich angesammelt.&#xA;&#xA;Gleichzeitig zeigte die Analyse, dass zwischen verschiedenen Arten von Altlasten unterschieden werden muss:&#xA;&#xA;alte Session-Daten,&#xA;Tabellen deinstallierter Erweiterungen,&#xA;Tabellen weiterhin installierter Erweiterungen,&#xA;Dateireste im Joomla-Dateisystem,&#xA;Discover-Einträge,&#xA;temporäre Dateien.&#xA;&#xA;Wer diese Bereiche getrennt betrachtet und Änderungen jeweils vorher überprüft, kann eine alte Joomla-Installation erheblich aufräumen, ohne dabei versehentlich noch verwendete Komponenten oder Daten zu entfernen.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Eine Joomla-Installation, die über viele Jahre betrieben und immer wieder erweitert wurde, sammelt mit der Zeit einiges an Ballast an. Erweiterungen werden installiert, ausprobiert und irgendwann wieder deinstalliert. Manche hinterlassen Datenbanktabellen, andere Dateien im Dateisystem. Gleichzeitig können Tabellen wie die Session-Tabelle über Jahre massiv anwachsen, wenn die automatische Bereinigung nicht zuverlässig funktioniert.</p>

<p>Dieser Artikel beschreibt einen praktischen Weg, um eine gewachsene Joomla-Installation schrittweise zu untersuchen und aufzuräumen.</p>

<p>Wichtig: Vor Änderungen an der Datenbank sollte immer ein aktuelles Backup vorhanden sein.</p>

<h2 id="das-problem">Das Problem</h2>

<p>Bei einer älteren Joomla-Installation fiel auf, dass die Datenbank ungewöhnlich groß geworden war. Gleichzeitig zeigte der Joomla-Bereich <strong>System → Discover</strong> mehr als hundert Erweiterungen an, obwohl viele davon längst nicht mehr bewusst genutzt wurden.</p>

<p>Die Fragen waren deshalb:</p>
<ul><li>Welche Tabellen belegen besonders viel Speicher?</li>
<li>Welche Tabellen gehören zum Joomla-Core?</li>
<li>Welche stammen von noch installierten Erweiterungen?</li>
<li>Welche sind wahrscheinlich Reste längst deinstallierter Erweiterungen?</li>
<li>Was bedeutet die lange Liste unter „Discover“?</li>
<li>Können temporäre Dateien im Joomla-Verzeichnis gelöscht werden?</li></ul>

<p>Statt sofort Tabellen zu löschen, lohnt sich zunächst eine Bestandsaufnahme.</p>

<hr>

<h2 id="1-datenbanktabellen-nach-größe-untersuchen">1. Datenbanktabellen nach Größe untersuchen</h2>

<p>Für eine erste Analyse braucht man nicht zwingend einen kompletten SQL-Dump mit allen Inhalten. Oft genügt eine Übersicht der Tabellen einschließlich Größe und ungefährer Zeilenzahl.</p>

<p>In phpMyAdmin kann innerhalb der betreffenden Datenbank folgende Abfrage ausgeführt werden:</p>

<pre><code class="language-sql">SELECT
  table_name AS Tabelle,
  engine AS Engine,
  table_rows AS Zeilen,
  ROUND((data_length + index_length) / 1024 / 1024, 2) AS Groesse_MB,
  create_time AS Erstellt,
  update_time AS Letzte_Aenderung
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY (data_length + index_length) DESC;
</code></pre>

<p>Das Ergebnis lässt sich beispielsweise als CSV exportieren.</p>

<p>Die Abfrage liefert unter anderem:</p>
<ul><li>Tabellenname</li>
<li>verwendete Storage Engine</li>
<li>ungefähre Anzahl der Datensätze</li>
<li>Größe von Daten und Indizes</li>
<li>Erstellungs- und Änderungszeitpunkte, soweit verfügbar</li></ul>

<p>Bei InnoDB ist die angegebene Zeilenzahl nicht zwingend exakt. Für eine erste Größenanalyse reicht sie aber aus.</p>

<hr>

<h2 id="2-eine-extrem-große-joomla-session-tabelle">2. Eine extrem große Joomla-Session-Tabelle</h2>

<p>Besonders auffällig kann die Joomla-Tabelle</p>

<pre><code class="language-text">#__session
</code></pre>

<p>werden.</p>

<p>In dem untersuchten Fall enthielt sie mehr als vier Millionen Datensätze und belegte mehrere Gigabyte.</p>

<p>Dabei handelt es sich nicht um einen Datei- oder Bildcache.</p>

<p>Die Session-Tabelle speichert Sitzungsinformationen. Dazu gehören beispielsweise anonyme Besucher-Sessions und Sitzungen eingeloggter Benutzer beziehungsweise Administratoren.</p>

<p>Bilder, CSS-Dateien oder andere Mediendateien liegen dort nicht.</p>

<h3 id="alte-sessions-zählen">Alte Sessions zählen</h3>

<p>Zunächst kann geprüft werden, wie viele Einträge älter als sieben Tage sind:</p>

<pre><code class="language-sql">SELECT COUNT(*) AS aelter_7_tage
FROM #__session
WHERE `time` &lt; UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY);
</code></pre>

<p>Der Präfix <code>#__</code> muss dabei durch den tatsächlichen Tabellenpräfix der Joomla-Installation ersetzt werden.</p>

<p>Zusätzlich kann die Gesamtzahl ermittelt werden:</p>

<pre><code class="language-sql">SELECT COUNT(*) AS gesamt
FROM #__session;
</code></pre>

<p>Im Praxisfall waren nahezu sämtliche Einträge älter als sieben Tage. Die Tabelle bestand damit fast vollständig aus alten Sessions.</p>

<hr>

<h2 id="3-alte-session-daten-löschen">3. Alte Session-Daten löschen</h2>

<p>Eine vorsichtige Möglichkeit besteht darin, alte Einträge portionsweise zu löschen:</p>

<pre><code class="language-sql">DELETE FROM #__session
WHERE `time` &lt; UNIX_TIMESTAMP(NOW() - INTERVAL 7 DAY)
ORDER BY `time`
LIMIT 100000;
</code></pre>

<p>Bei mehreren Millionen Datensätzen kann das allerdings sehr lange dauern.</p>

<p>Im konkreten Fall benötigte bereits das Löschen von 100.000 Datensätzen deutlich mehr als zwei Minuten. Auch größere Blöcke machten die Bereinigung entsprechend langwierig.</p>

<h3 id="alternative-session-tabelle-vollständig-leeren">Alternative: Session-Tabelle vollständig leeren</h3>

<p>Wenn die Website keine angemeldeten Frontend-Benutzer hat und es akzeptabel ist, dass momentan angemeldete Administratoren ihre Sitzung verlieren, kann die Session-Tabelle auch vollständig geleert werden:</p>

<pre><code class="language-sql">TRUNCATE TABLE #__session;
</code></pre>

<p>Das ist erheblich schneller als Millionen einzelne Datensätze mit <code>DELETE</code> zu entfernen.</p>

<p>Die Folge ist:</p>
<ul><li>bestehende Sessions gehen verloren,</li>
<li>Administratoren müssen sich gegebenenfalls neu anmelden,</li>
<li>Dateien, Bilder und Inhalte werden nicht gelöscht.</li></ul>

<p>Für eine reine Session-Tabelle kann das bei einer Wartung eine sinnvolle Lösung sein.</p>

<hr>

<h2 id="4-warum-bleiben-nach-einer-joomla-deinstallation-tabellen-übrig">4. Warum bleiben nach einer Joomla-Deinstallation Tabellen übrig?</h2>

<p>Bei älteren Joomla-Installationen finden sich häufig Tabellen von Erweiterungen, die längst nicht mehr verwendet werden.</p>

<p>Eine Deinstallation entfernt nicht zwangsläufig alle Daten.</p>

<p>Ob Datenbanktabellen beim Entfernen einer Erweiterung ebenfalls gelöscht werden, hängt davon ab, wie die jeweilige Erweiterung ihre Deinstallation implementiert.</p>

<p>Deshalb können über Jahre komplette Tabellenfamilien zurückbleiben.</p>

<p>Typisch sind Namen wie:</p>

<pre><code class="language-text">#__extensionname
#__extensionname_files
#__extensionname_statistics
#__extensionname_categories
#__extensionname_config
</code></pre>

<p>Solche Tabellen sollte man allerdings nicht allein aufgrund ihres Namens löschen.</p>

<p>Zunächst muss geklärt werden, ob die zugehörige Erweiterung noch installiert ist.</p>

<hr>

<h2 id="5-prüfen-ob-eine-erweiterung-noch-installiert-ist">5. Prüfen, ob eine Erweiterung noch installiert ist</h2>

<p>Joomla führt installierte Erweiterungen in der Tabelle</p>

<pre><code class="language-text">#__extensions
</code></pre>

<p>Eine einfache Abfrage kann beispielsweise so aussehen:</p>

<pre><code class="language-sql">SELECT
  extension_id,
  name,
  type,
  element,
  folder,
  enabled
FROM #__extensions
ORDER BY name, type;
</code></pre>

<p>Bei einer bestimmten Erweiterung kann gezielter gesucht werden:</p>

<pre><code class="language-sql">SELECT
  extension_id,
  name,
  type,
  element,
  folder,
  enabled
FROM #__extensions
WHERE LOWER(CONCAT_WS(&#39; &#39;, name, type, element, folder))
      REGEXP &#39;name-der-erweiterung&#39;;
</code></pre>

<p>Das ermöglicht eine wichtige Unterscheidung:</p>

<h3 id="erweiterung-noch-installiert">Erweiterung noch installiert</h3>

<p>Dann sollten die zugehörigen Tabellen nicht einfach gelöscht werden.</p>

<p>Auch eine deaktivierte Erweiterung kann weiterhin vollständig installiert sein.</p>

<h3 id="erweiterung-nicht-mehr-installiert">Erweiterung nicht mehr installiert</h3>

<p>Existieren trotzdem noch passende Datenbanktabellen, handelt es sich sehr wahrscheinlich um Altlasten.</p>

<p>Vor dem Löschen sollte trotzdem geprüft werden, ob die Daten noch benötigt werden.</p>

<p>Manche Tabellen enthalten nicht nur Konfiguration, sondern tatsächliche Inhalte, Formulareingaben, Statistiken oder Dokumentinformationen.</p>

<hr>

<h2 id="6-tabellen-einer-alten-erweiterung-löschen">6. Tabellen einer alten Erweiterung löschen</h2>

<p>Ist eindeutig geklärt, dass eine Erweiterung nicht mehr verwendet wird und ihre Daten ebenfalls nicht mehr benötigt werden, können die verbliebenen Tabellen entfernt werden.</p>

<p>Die vorhandenen Tabellen lassen sich zunächst anzeigen:</p>

<pre><code class="language-sql">SHOW TABLES LIKE &#39;%extensionname%&#39;;
</code></pre>

<p>Eine nützliche Variante erzeugt zunächst nur die passenden <code>DROP TABLE</code>-Befehle:</p>

<pre><code class="language-sql">SELECT CONCAT(
  &#39;DROP TABLE `&#39;,
  table_name,
  &#39;`;&#39;
) AS drop_sql
FROM information_schema.tables
WHERE table_schema = DATABASE()
  AND table_name LIKE &#39;%extensionname%&#39;;
</code></pre>

<p>Die erzeugten Befehle können anschließend überprüft werden.</p>

<p>Erst danach sollte tatsächlich gelöscht werden.</p>

<p>Beispiel:</p>

<pre><code class="language-sql">DROP TABLE IF EXISTS
  `prefix_extensionname`,
  `prefix_extensionname_files`,
  `prefix_extensionname_statistics`;
</code></pre>

<p>Das ist sicherer, als Tabellen anhand eines vermuteten Namensschemas automatisch zu entfernen.</p>

<hr>

<h2 id="7-nicht-jede-große-tabelle-ist-eine-altlast">7. Nicht jede große Tabelle ist eine Altlast</h2>

<p>Eine wichtige Erkenntnis bei solchen Aufräumarbeiten:</p>

<p>Große Tabellen sind nicht automatisch überflüssig.</p>

<p>Zu Joomla selbst gehören beispielsweise Tabellenfamilien für Funktionen wie:</p>

<pre><code class="language-text">#__finder_*
#__redirect_links
#__action_logs*
#__workflow_*
#__scheduler_*
#__guidedtours*
#__history
</code></pre>

<p>Auch installierte Drittanbieter-Erweiterungen können große Tabellen besitzen, etwa für:</p>
<ul><li>Statistiken</li>
<li>Revisionen</li>
<li>Suchindizes</li>
<li>Formulardaten</li>
<li>Dateiverwaltung</li>
<li>Protokolle</li></ul>

<p>Solche Tabellen sollten nicht entfernt werden, solange die zugehörige Funktion oder Erweiterung noch verwendet wird.</p>

<p>Hier ist gegebenenfalls eine Bereinigung der Inhalte sinnvoller als das Löschen der gesamten Tabelle.</p>

<hr>

<h2 id="8-was-bedeutet-discover-in-joomla">8. Was bedeutet „Discover“ in Joomla?</h2>

<p>Unter</p>

<p><strong>System → Discover</strong></p>

<p>kann Joomla Erweiterungen anzeigen, die im Dateisystem gefunden werden, aber nicht regulär als installierte Erweiterungen registriert sind.</p>

<p>Das ist etwas anderes als eine übrig gebliebene Datenbanktabelle.</p>

<p>Ein Discover-Eintrag bedeutet im Kern:</p>

<p>Joomla findet Dateien einer Erweiterung, aber der entsprechende Installationszustand passt nicht dazu.</p>

<p>Solche Reste können beispielsweise in Verzeichnissen liegen wie:</p>

<pre><code class="language-text">administrator/components/
components/
plugins/
modules/
administrator/modules/
</code></pre>

<p>Das erklärt auch, warum eine längst deinstallierte Erweiterung weiterhin unter „Discover“ auftauchen kann.</p>

<p>Ihre Datenbanktabellen können bereits verschwunden sein, während Teile der Erweiterung noch auf dem Webserver liegen.</p>

<hr>

<h2 id="9-discover-einträge-bereinigen">9. Discover-Einträge bereinigen</h2>

<p>Ein Discover-Eintrag sollte nicht einfach blind entfernt werden.</p>

<p>Zunächst muss geklärt werden:</p>
<ol><li>Wird die Erweiterung noch verwendet?</li>
<li>Ist sie regulär installiert?</li>
<li>Handelt es sich tatsächlich nur um übrig gebliebene Dateien?</li></ol>

<p>Wenn eindeutig feststeht, dass die Erweiterung nicht mehr gebraucht wird, können die zugehörigen Dateireste entfernt werden.</p>

<p>Dabei reicht es nicht immer, nur</p>

<pre><code class="language-text">administrator/components/com_beispiel
</code></pre>

<p>zu löschen.</p>

<p>Eine Erweiterung kann zusätzlich Dateien in weiteren Bereichen besitzen, beispielsweise:</p>

<pre><code class="language-text">components/com_beispiel
plugins/...
modules/...
administrator/modules/...
</code></pre>

<p>Nach dem Entfernen der verbliebenen Erweiterungsdateien sollte Joomla sie bei einer erneuten Discover-Suche nicht mehr erkennen.</p>

<p>Datenbankreste und Discover-Einträge sind damit zwei getrennte Baustellen:</p>
<ul><li><strong>alte Tabellen</strong> → Datenbank</li>
<li><strong>Discover-Einträge</strong> → meist übrig gebliebene Dateien beziehungsweise ein inkonsistenter Installationszustand</li></ul>

<hr>

<h2 id="10-was-kann-aus-dem-joomla-tmp-verzeichnis-gelöscht-werden">10. Was kann aus dem Joomla-<code>tmp</code>-Verzeichnis gelöscht werden?</h2>

<p>Auch im Joomla-Verzeichnis</p>

<pre><code class="language-text">tmp/
</code></pre>

<p>können sich über Jahre Dateien ansammeln.</p>

<p>Joomla verwendet dieses Verzeichnis als temporären Arbeitsbereich, unter anderem bei Installationen und Updates von Erweiterungen.</p>

<p>Dort können beispielsweise liegen:</p>
<ul><li>heruntergeladene Installationsarchive,</li>
<li>entpackte Erweiterungen,</li>
<li>Reste abgebrochener Installationen,</li>
<li>andere temporäre Dateien.</li></ul>

<p>Alte Inhalte dieses Verzeichnisses können in der Regel entfernt werden, wenn gerade</p>
<ul><li>kein Joomla-Update,</li>
<li>keine Erweiterungsinstallation,</li>
<li>kein Restore</li>
<li>und kein anderer Wartungsvorgang</li></ul>

<p>läuft.</p>

<p>Der Ordner <code>tmp</code> selbst sollte dagegen bestehen bleiben.</p>

<p>Auch sollte der konfigurierte <code>tmp</code>-Pfad nicht verändert werden, wenn die Installation korrekt funktioniert.</p>

<hr>

<h2 id="11-sinnvolle-reihenfolge-beim-aufräumen">11. Sinnvolle Reihenfolge beim Aufräumen</h2>

<p>Für eine ältere Joomla-Installation hat sich folgende Reihenfolge als übersichtlich erwiesen:</p>

<h3 id="1-backup-erstellen">1. Backup erstellen</h3>

<p>Vor Änderungen an Datenbank oder Dateisystem.</p>

<h3 id="2-tabellen-nach-größe-analysieren">2. Tabellen nach Größe analysieren</h3>

<p>Mit <code>information_schema.tables</code>.</p>

<h3 id="3-extrem-große-technische-tabellen-untersuchen">3. Extrem große technische Tabellen untersuchen</h3>

<p>Zum Beispiel <code>#__session</code>.</p>

<h3 id="4-installationsstatus-von-erweiterungen-prüfen">4. Installationsstatus von Erweiterungen prüfen</h3>

<p>Über <code>#__extensions</code>.</p>

<h3 id="5-verwaiste-datenbanktabellen-identifizieren">5. Verwaiste Datenbanktabellen identifizieren</h3>

<p>Nur löschen, wenn die Erweiterung sicher nicht mehr benötigt wird.</p>

<h3 id="6-joomla-discover-kontrollieren">6. Joomla „Discover“ kontrollieren</h3>

<p>Übrig gebliebene Erweiterungsdateien identifizieren.</p>

<h3 id="7-dateireste-entfernen">7. Dateireste entfernen</h3>

<p>Dabei alle zugehörigen Erweiterungspfade berücksichtigen.</p>

<h3 id="8-tmp-bereinigen">8. <code>tmp</code> bereinigen</h3>

<p>Nur alte temporäre Inhalte entfernen, nicht den Ordner selbst.</p>

<hr>

<h2 id="ergebnis">Ergebnis</h2>

<p>Bei einer über Jahre gewachsenen Joomla-Installation lohnt sich ein systematisches Aufräumen.</p>

<p>Der größte Speicherverbrauch muss dabei nicht einmal von alten Erweiterungen kommen. Im beschriebenen Praxisfall war zunächst die Session-Tabelle das deutlichste Problem: Millionen alter Sessions hatten sich angesammelt.</p>

<p>Gleichzeitig zeigte die Analyse, dass zwischen verschiedenen Arten von Altlasten unterschieden werden muss:</p>
<ul><li>alte Session-Daten,</li>
<li>Tabellen deinstallierter Erweiterungen,</li>
<li>Tabellen weiterhin installierter Erweiterungen,</li>
<li>Dateireste im Joomla-Dateisystem,</li>
<li>Discover-Einträge,</li>
<li>temporäre Dateien.</li></ul>

<p>Wer diese Bereiche getrennt betrachtet und Änderungen jeweils vorher überprüft, kann eine alte Joomla-Installation erheblich aufräumen, ohne dabei versehentlich noch verwendete Komponenten oder Daten zu entfernen.</p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-aufraeumen-datenbank-alte-erweiterungen-discover-und-tmp-verzeichnis</guid>
      <pubDate>Wed, 16 Sep 2026 13:14:27 +0000</pubDate>
    </item>
    <item>
      <title>Joomla-Datenbank plötzlich mehrere Gigabyte groß: Session-Tabelle analysieren und bereinigen</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-datenbank-ploetzlich-mehrere-gigabyte-gross-session-tabelle-analysieren</link>
      <description>&lt;![CDATA[Eine Joomla-Datenbank mit mehreren Gigabyte Größe ist zumindest ein Anlass, genauer hinzusehen. In meinem Fall war eine Joomla-Installation mit MariaDB auf rund 3,65 GB angewachsen. Bei der Analyse fiel insbesondere die Session-Tabelle auf.&#xA;&#xA;Dieser Artikel dokumentiert die Schritte, mit denen sich eine solche Tabelle untersuchen und bereinigen lässt. Wichtig: Vor Änderungen an einer produktiven Datenbank sollte immer ein vollständiges Backup vorhanden sein.&#xA;&#xA;Problem: Eine ungewöhnlich große Joomla-Datenbank&#xA;&#xA;Der erste Schritt besteht nicht darin, auf Verdacht Tabellen oder Datensätze zu löschen. Zunächst sollte geklärt werden, welche Tabellen den Speicher tatsächlich belegen.&#xA;&#xA;Mit dieser Abfrage lassen sich die Tabellen einer Datenbank nach ihrer Größe sortieren:&#xA;&#xA;SELECT&#xA;    tablename,&#xA;    ROUND((datalength + indexlength) / 1024 / 1024, 2) AS sizeinmb&#xA;FROM informationschema.tables&#xA;WHERE tableschema = &#39;DATENBANKNAME&#39;&#xA;ORDER BY sizeinmb DESC;&#xA;&#xA;DATENBANKNAME muss durch den tatsächlichen Namen der Joomla-Datenbank ersetzt werden.&#xA;&#xA;In meinem Fall gehörte die Joomla-Session-Tabelle zu den auffälligen Tabellen.&#xA;&#xA;Die Joomla-Session-Tabelle untersuchen&#xA;&#xA;Joomla verwendet ein individuell konfiguriertes Tabellenpräfix. Aus einer Tabelle wie&#xA;&#xA;abcsession&#xA;&#xA;kann daher bei einer anderen Installation beispielsweise&#xA;&#xA;xyzsession&#xA;&#xA;werden.&#xA;&#xA;Die Struktur lässt sich mit DESCRIBE anzeigen:&#xA;&#xA;DESCRIBE abcsession;&#xA;&#xA;In der untersuchten Joomla-Installation hatte die Session-Tabelle unter anderem folgende Felder:&#xA;&#xA;sessionid&#xA;clientid&#xA;guest&#xA;time&#xA;data&#xA;userid&#xA;username&#xA;&#xA;Für die weitere Analyse sind vor allem time und data interessant.&#xA;&#xA;time ist ein Integer-Feld, über das sich alte Session-Einträge identifizieren lassen. data ist ein MEDIUMTEXT-Feld und kann entsprechend umfangreiche Session-Daten enthalten.&#xA;&#xA;Wie groß sind die eigentlichen Session-Daten?&#xA;&#xA;Um zunächst nur die gespeicherten Inhalte des Feldes data zu untersuchen, kann man deren Gesamtlänge ermitteln:&#xA;&#xA;SELECT&#xA;    SUM(CHARLENGTH(data)) AS totaldatasize&#xA;FROM abcsession;&#xA;&#xA;In meinem Fall ergab die Abfrage:&#xA;&#xA;687078044&#xA;&#xA;CHARLENGTH() liefert hier die Anzahl der Zeichen, nicht unmittelbar die physische Größe der Tabelle auf dem Datenträger. Bei den vorliegenden Session-Daten entsprach die Größenordnung dennoch bereits mehreren hundert Megabyte Inhalt allein in diesem Feld.&#xA;&#xA;Die tatsächliche Tabellengröße sollte deshalb zusätzlich über informationschema.tables bzw. die Tabelleninformationen von phpMyAdmin kontrolliert werden.&#xA;&#xA;Wie viele alte Sessions gibt es?&#xA;&#xA;Bevor Daten gelöscht werden, kann man zunächst prüfen, welche Datensätze von einer Löschbedingung betroffen wären.&#xA;&#xA;Beispielsweise für Sessions, deren time-Wert älter als zehn Tage ist:&#xA;&#xA;SELECT &#xA;FROM abcsession&#xA;WHERE time &lt; UNIXTIMESTAMP(NOW() - INTERVAL 10 DAY)&#xA;LIMIT 10;&#xA;&#xA;Noch übersichtlicher ist zunächst die reine Anzahl:&#xA;&#xA;SELECT COUNT()&#xA;FROM abcsession&#xA;WHERE time &lt; UNIXTIMESTAMP(NOW() - INTERVAL 10 DAY);&#xA;&#xA;Damit lässt sich kontrollieren, wie viele Datensätze die Bedingung überhaupt erfasst.&#xA;&#xA;Wichtig: Der Zeitraum von zehn Tagen ist hier ein Beispiel aus der konkreten Bereinigung und keine allgemeingültige Joomla-Empfehlung. Welche Sessions gelöscht werden können, sollte zur Session-Konfiguration der jeweiligen Installation passen.&#xA;&#xA;Alte Session-Einträge löschen&#xA;&#xA;Ist geklärt, dass die entsprechenden Datensätze entfernt werden sollen, lautet die Abfrage beispielsweise:&#xA;&#xA;DELETE FROM abcsession&#xA;WHERE time &lt; UNIXTIMESTAMP(NOW() - INTERVAL 10 DAY);&#xA;&#xA;Bei sehr vielen Datensätzen kann eine solche Abfrage über phpMyAdmin allerdings problematisch werden. Bei der untersuchten Installation kam es bei einer größeren Operation zu einem HTTP-500-Fehler bzw. einem Verbindungsabbruch.&#xA;&#xA;In einem solchen Fall kann es sinnvoll sein, nicht Hunderttausende Datensätze in einer einzigen Operation zu bearbeiten, sondern die Löschung in kleineren Portionen durchzuführen:&#xA;&#xA;DELETE FROM abcsession&#xA;WHERE time &lt; UNIXTIMESTAMP(NOW() - INTERVAL 10 DAY)&#xA;LIMIT 1000;&#xA;&#xA;Der Befehl kann wiederholt ausgeführt werden, bis keine entsprechenden Datensätze mehr vorhanden sind.&#xA;&#xA;In der konkreten Datenbank wurden schließlich rund 300.000 Session-Datensätze entfernt.&#xA;&#xA;Warum wird die Datenbank nach dem Löschen kaum kleiner?&#xA;&#xA;Das war der überraschende Teil: Obwohl rund 300.000 Datensätze gelöscht worden waren, sank die angezeigte Gesamtgröße der Datenbank zunächst nur geringfügig.&#xA;&#xA;Das bedeutet nicht, dass MariaDB die gelöschten Datensätze in einem „Papierkorb“ aufbewahrt.&#xA;&#xA;Bei InnoDB führt ein DELETE jedoch nicht zwangsläufig dazu, dass der zuvor von einer Tabelle belegte Speicherplatz unmittelbar als kleinere Tabellendatei sichtbar wird. Frei gewordener Platz kann zunächst innerhalb der Tabelle wiederverwendbar bleiben.&#xA;&#xA;Deshalb kann nach einer großen Löschaktion eine Neuorganisation der Tabelle sinnvoll sein.&#xA;&#xA;Tabelle mit OPTIMIZE TABLE neu organisieren&#xA;&#xA;Dazu kann man ausführen:&#xA;&#xA;OPTIMIZE TABLE abcsession;&#xA;&#xA;Bei der untersuchten InnoDB-Tabelle meldete MariaDB daraufhin:&#xA;&#xA;Table does not support optimize, doing recreate + ...&#xA;&#xA;Diese Meldung ist in diesem Zusammenhang nicht automatisch ein Fehler. MariaDB weist damit darauf hin, dass die Optimierung bei dieser Tabelle über einen Neuaufbau erfolgt.&#xA;&#xA;Gerade bei einer großen Tabelle sollte man berücksichtigen, dass ein solcher Vorgang erheblich aufwendiger sein kann als ein gewöhnliches DELETE.&#xA;&#xA;Größe anschließend erneut kontrollieren&#xA;&#xA;Nach der Bereinigung kann die Tabellengröße erneut abgefragt werden:&#xA;&#xA;SELECT&#xA;    tablename,&#xA;    ROUND(datalength / 1024 / 1024, 2) AS datamb,&#xA;    ROUND(indexlength / 1024 / 1024, 2) AS indexmb,&#xA;    ROUND((datalength + indexlength) / 1024 / 1024, 2) AS totalmb&#xA;FROM informationschema.tables&#xA;WHERE tableschema = &#39;DATENBANKNAME&#39;&#xA;ORDER BY (datalength + index_length) DESC;&#xA;&#xA;Das ist auch für die weitere Analyse nützlich: Statt wahllos Joomla-Tabellen zu bereinigen, sieht man sofort, welche Tabellen tatsächlich relevant für die Gesamtgröße sind.&#xA;&#xA;Ein zusätzlicher Joomla-Schalter: „Track Guest Session Metadata“&#xA;&#xA;Bei der Analyse fiel außerdem die Joomla-Einstellung „Track Guest Session Metadata“ auf.&#xA;&#xA;Da die untersuchte Installation gerade durch sehr viele Session-Datensätze auffällig geworden war, ist diese Einstellung ein naheliegender Punkt für die weitere Ursachenanalyse. Aus der hier beschriebenen Untersuchung allein lässt sich allerdings nicht belegen, dass diese Option das starke Wachstum verursacht hat.&#xA;&#xA;Deshalb sollte man sie nicht allein aufgrund der Datenbankgröße deaktivieren, sondern zunächst prüfen, ob und wofür die Installation Gast-Session-Metadaten benötigt.&#xA;&#xA;Was sich aus der Analyse ableiten lässt&#xA;&#xA;Die wichtigste Erkenntnis war weniger ein einzelner SQL-Befehl als die Reihenfolge der Fehlersuche:&#xA;&#xA;Zuerst die tatsächlichen Größen aller Tabellen ermitteln.&#xA;Bei einer auffälligen Session-Tabelle Struktur und Anzahl der Datensätze prüfen.&#xA;Den Umfang des Feldes data untersuchen.&#xA;Alte Datensätze zunächst nur per SELECT bzw. COUNT(*) identifizieren.&#xA;Erst danach gezielt löschen.&#xA;Bei sehr großen Datenmengen gegebenenfalls in kleineren Blöcken arbeiten.&#xA;Nicht erwarten, dass ein DELETE bei InnoDB die sichtbare Tabellengröße sofort entsprechend reduziert.&#xA;Nach größeren Löschaktionen prüfen, ob eine Neuorganisation der Tabelle erforderlich ist.&#xA;&#xA;Vor allem sollte man nicht anfangen, vermeintlich alte Joomla- oder Erweiterungstabellen auf Verdacht zu löschen. Dass eine Tabelle groß, deaktiviert oder historisch gewachsen ist, beweist noch nicht, dass sie für die aktuelle Installation entbehrlich ist.&#xA;&#xA;Die Session-Tabelle ist dagegen ein gutes Beispiel dafür, wie man eine auffällige Datenmenge zunächst nachvollziehbar analysieren kann, bevor man in die Struktur einer produktiven Joomla-Datenbank eingreift.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Eine Joomla-Datenbank mit mehreren Gigabyte Größe ist zumindest ein Anlass, genauer hinzusehen. In meinem Fall war eine Joomla-Installation mit MariaDB auf rund 3,65 GB angewachsen. Bei der Analyse fiel insbesondere die Session-Tabelle auf.</p>

<p>Dieser Artikel dokumentiert die Schritte, mit denen sich eine solche Tabelle untersuchen und bereinigen lässt. Wichtig: Vor Änderungen an einer produktiven Datenbank sollte immer ein vollständiges Backup vorhanden sein.</p>

<h2 id="problem-eine-ungewöhnlich-große-joomla-datenbank">Problem: Eine ungewöhnlich große Joomla-Datenbank</h2>

<p>Der erste Schritt besteht nicht darin, auf Verdacht Tabellen oder Datensätze zu löschen. Zunächst sollte geklärt werden, <strong>welche Tabellen den Speicher tatsächlich belegen</strong>.</p>

<p>Mit dieser Abfrage lassen sich die Tabellen einer Datenbank nach ihrer Größe sortieren:</p>

<pre><code class="language-sql">SELECT
    table_name,
    ROUND((data_length + index_length) / 1024 / 1024, 2) AS size_in_mb
FROM information_schema.tables
WHERE table_schema = &#39;DATENBANKNAME&#39;
ORDER BY size_in_mb DESC;
</code></pre>

<p><code>DATENBANKNAME</code> muss durch den tatsächlichen Namen der Joomla-Datenbank ersetzt werden.</p>

<p>In meinem Fall gehörte die Joomla-Session-Tabelle zu den auffälligen Tabellen.</p>

<h2 id="die-joomla-session-tabelle-untersuchen">Die Joomla-Session-Tabelle untersuchen</h2>

<p>Joomla verwendet ein individuell konfiguriertes Tabellenpräfix. Aus einer Tabelle wie</p>

<pre><code class="language-text">abc_session
</code></pre>

<p>kann daher bei einer anderen Installation beispielsweise</p>

<pre><code class="language-text">xyz_session
</code></pre>

<p>werden.</p>

<p>Die Struktur lässt sich mit <code>DESCRIBE</code> anzeigen:</p>

<pre><code class="language-sql">DESCRIBE `abc_session`;
</code></pre>

<p>In der untersuchten Joomla-Installation hatte die Session-Tabelle unter anderem folgende Felder:</p>

<pre><code class="language-text">session_id
client_id
guest
time
data
userid
username
</code></pre>

<p>Für die weitere Analyse sind vor allem <code>time</code> und <code>data</code> interessant.</p>

<p><code>time</code> ist ein Integer-Feld, über das sich alte Session-Einträge identifizieren lassen. <code>data</code> ist ein <code>MEDIUMTEXT</code>-Feld und kann entsprechend umfangreiche Session-Daten enthalten.</p>

<h2 id="wie-groß-sind-die-eigentlichen-session-daten">Wie groß sind die eigentlichen Session-Daten?</h2>

<p>Um zunächst nur die gespeicherten Inhalte des Feldes <code>data</code> zu untersuchen, kann man deren Gesamtlänge ermitteln:</p>

<pre><code class="language-sql">SELECT
    SUM(CHAR_LENGTH(data)) AS total_data_size
FROM `abc_session`;
</code></pre>

<p>In meinem Fall ergab die Abfrage:</p>

<pre><code class="language-text">687078044
</code></pre>

<p><code>CHAR_LENGTH()</code> liefert hier die Anzahl der Zeichen, nicht unmittelbar die physische Größe der Tabelle auf dem Datenträger. Bei den vorliegenden Session-Daten entsprach die Größenordnung dennoch bereits mehreren hundert Megabyte Inhalt allein in diesem Feld.</p>

<p>Die tatsächliche Tabellengröße sollte deshalb zusätzlich über <code>information_schema.tables</code> bzw. die Tabelleninformationen von phpMyAdmin kontrolliert werden.</p>

<h2 id="wie-viele-alte-sessions-gibt-es">Wie viele alte Sessions gibt es?</h2>

<p>Bevor Daten gelöscht werden, kann man zunächst prüfen, welche Datensätze von einer Löschbedingung betroffen wären.</p>

<p>Beispielsweise für Sessions, deren <code>time</code>-Wert älter als zehn Tage ist:</p>

<pre><code class="language-sql">SELECT *
FROM `abc_session`
WHERE `time` &lt; UNIX_TIMESTAMP(NOW() - INTERVAL 10 DAY)
LIMIT 10;
</code></pre>

<p>Noch übersichtlicher ist zunächst die reine Anzahl:</p>

<pre><code class="language-sql">SELECT COUNT(*)
FROM `abc_session`
WHERE `time` &lt; UNIX_TIMESTAMP(NOW() - INTERVAL 10 DAY);
</code></pre>

<p>Damit lässt sich kontrollieren, wie viele Datensätze die Bedingung überhaupt erfasst.</p>

<p><strong>Wichtig:</strong> Der Zeitraum von zehn Tagen ist hier ein Beispiel aus der konkreten Bereinigung und keine allgemeingültige Joomla-Empfehlung. Welche Sessions gelöscht werden können, sollte zur Session-Konfiguration der jeweiligen Installation passen.</p>

<h2 id="alte-session-einträge-löschen">Alte Session-Einträge löschen</h2>

<p>Ist geklärt, dass die entsprechenden Datensätze entfernt werden sollen, lautet die Abfrage beispielsweise:</p>

<pre><code class="language-sql">DELETE FROM `abc_session`
WHERE `time` &lt; UNIX_TIMESTAMP(NOW() - INTERVAL 10 DAY);
</code></pre>

<p>Bei sehr vielen Datensätzen kann eine solche Abfrage über phpMyAdmin allerdings problematisch werden. Bei der untersuchten Installation kam es bei einer größeren Operation zu einem HTTP-500-Fehler bzw. einem Verbindungsabbruch.</p>

<p>In einem solchen Fall kann es sinnvoll sein, nicht Hunderttausende Datensätze in einer einzigen Operation zu bearbeiten, sondern die Löschung in kleineren Portionen durchzuführen:</p>

<pre><code class="language-sql">DELETE FROM `abc_session`
WHERE `time` &lt; UNIX_TIMESTAMP(NOW() - INTERVAL 10 DAY)
LIMIT 1000;
</code></pre>

<p>Der Befehl kann wiederholt ausgeführt werden, bis keine entsprechenden Datensätze mehr vorhanden sind.</p>

<p>In der konkreten Datenbank wurden schließlich rund 300.000 Session-Datensätze entfernt.</p>

<h2 id="warum-wird-die-datenbank-nach-dem-löschen-kaum-kleiner">Warum wird die Datenbank nach dem Löschen kaum kleiner?</h2>

<p>Das war der überraschende Teil: Obwohl rund 300.000 Datensätze gelöscht worden waren, sank die angezeigte Gesamtgröße der Datenbank zunächst nur geringfügig.</p>

<p>Das bedeutet nicht, dass MariaDB die gelöschten Datensätze in einem „Papierkorb“ aufbewahrt.</p>

<p>Bei InnoDB führt ein <code>DELETE</code> jedoch nicht zwangsläufig dazu, dass der zuvor von einer Tabelle belegte Speicherplatz unmittelbar als kleinere Tabellendatei sichtbar wird. Frei gewordener Platz kann zunächst innerhalb der Tabelle wiederverwendbar bleiben.</p>

<p>Deshalb kann nach einer großen Löschaktion eine Neuorganisation der Tabelle sinnvoll sein.</p>

<h2 id="tabelle-mit-optimize-table-neu-organisieren">Tabelle mit OPTIMIZE TABLE neu organisieren</h2>

<p>Dazu kann man ausführen:</p>

<pre><code class="language-sql">OPTIMIZE TABLE `abc_session`;
</code></pre>

<p>Bei der untersuchten InnoDB-Tabelle meldete MariaDB daraufhin:</p>

<pre><code class="language-text">Table does not support optimize, doing recreate + ...
</code></pre>

<p>Diese Meldung ist in diesem Zusammenhang nicht automatisch ein Fehler. MariaDB weist damit darauf hin, dass die Optimierung bei dieser Tabelle über einen Neuaufbau erfolgt.</p>

<p>Gerade bei einer großen Tabelle sollte man berücksichtigen, dass ein solcher Vorgang erheblich aufwendiger sein kann als ein gewöhnliches <code>DELETE</code>.</p>

<h2 id="größe-anschließend-erneut-kontrollieren">Größe anschließend erneut kontrollieren</h2>

<p>Nach der Bereinigung kann die Tabellengröße erneut abgefragt werden:</p>

<pre><code class="language-sql">SELECT
    table_name,
    ROUND(data_length / 1024 / 1024, 2) AS data_mb,
    ROUND(index_length / 1024 / 1024, 2) AS index_mb,
    ROUND((data_length + index_length) / 1024 / 1024, 2) AS total_mb
FROM information_schema.tables
WHERE table_schema = &#39;DATENBANKNAME&#39;
ORDER BY (data_length + index_length) DESC;
</code></pre>

<p>Das ist auch für die weitere Analyse nützlich: Statt wahllos Joomla-Tabellen zu bereinigen, sieht man sofort, welche Tabellen tatsächlich relevant für die Gesamtgröße sind.</p>

<h2 id="ein-zusätzlicher-joomla-schalter-track-guest-session-metadata">Ein zusätzlicher Joomla-Schalter: „Track Guest Session Metadata“</h2>

<p>Bei der Analyse fiel außerdem die Joomla-Einstellung <strong>„Track Guest Session Metadata“</strong> auf.</p>

<p>Da die untersuchte Installation gerade durch sehr viele Session-Datensätze auffällig geworden war, ist diese Einstellung ein naheliegender Punkt für die weitere Ursachenanalyse. Aus der hier beschriebenen Untersuchung allein lässt sich allerdings <strong>nicht belegen</strong>, dass diese Option das starke Wachstum verursacht hat.</p>

<p>Deshalb sollte man sie nicht allein aufgrund der Datenbankgröße deaktivieren, sondern zunächst prüfen, ob und wofür die Installation Gast-Session-Metadaten benötigt.</p>

<h2 id="was-sich-aus-der-analyse-ableiten-lässt">Was sich aus der Analyse ableiten lässt</h2>

<p>Die wichtigste Erkenntnis war weniger ein einzelner SQL-Befehl als die Reihenfolge der Fehlersuche:</p>
<ol><li>Zuerst die tatsächlichen Größen aller Tabellen ermitteln.</li>
<li>Bei einer auffälligen Session-Tabelle Struktur und Anzahl der Datensätze prüfen.</li>
<li>Den Umfang des Feldes <code>data</code> untersuchen.</li>
<li>Alte Datensätze zunächst nur per <code>SELECT</code> bzw. <code>COUNT(*)</code> identifizieren.</li>
<li>Erst danach gezielt löschen.</li>
<li>Bei sehr großen Datenmengen gegebenenfalls in kleineren Blöcken arbeiten.</li>
<li>Nicht erwarten, dass ein <code>DELETE</code> bei InnoDB die sichtbare Tabellengröße sofort entsprechend reduziert.</li>
<li>Nach größeren Löschaktionen prüfen, ob eine Neuorganisation der Tabelle erforderlich ist.</li></ol>

<p>Vor allem sollte man nicht anfangen, vermeintlich alte Joomla- oder Erweiterungstabellen auf Verdacht zu löschen. Dass eine Tabelle groß, deaktiviert oder historisch gewachsen ist, beweist noch nicht, dass sie für die aktuelle Installation entbehrlich ist.</p>

<p>Die Session-Tabelle ist dagegen ein gutes Beispiel dafür, wie man eine auffällige Datenmenge zunächst nachvollziehbar analysieren kann, bevor man in die Struktur einer produktiven Joomla-Datenbank eingreift.</p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-datenbank-ploetzlich-mehrere-gigabyte-gross-session-tabelle-analysieren</guid>
      <pubDate>Wed, 16 Sep 2026 13:12:27 +0000</pubDate>
    </item>
    <item>
      <title>Joomla nach einem Webshell-Hack: Welche Passwörter und Sitzungen sollte man ändern?</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-nach-einem-webshell-hack-welche-passwoerter-und-sitzungen-sollte-man</link>
      <description>&lt;![CDATA[Nach einem bestätigten Webshell-Befall reicht es nicht, die schädlichen PHP-Dateien zu löschen und die betroffene Joomla-Erweiterung zu aktualisieren.&#xA;&#xA;Wenn fremder PHP-Code mit den Rechten des Webservers ausgeführt werden konnte, muss man davon ausgehen, dass dieser Code auch auf Dateien zugreifen konnte, die für den Webserver lesbar waren.&#xA;&#xA;Bei Joomla gehört dazu insbesondere die configuration.php.&#xA;&#xA;Damit können nach einem solchen Vorfall verschiedene Zugangsdaten als potentiell offengelegt gelten.&#xA;&#xA;Dieser Beitrag beschreibt, welche Zugangsdaten ich nach einem tatsächlichen Joomla-Webshell-Befall geändert habe – und in welcher Reihenfolge.&#xA;&#xA;Ausgangslage&#xA;&#xA;In meinem Fall waren über eine verwundbare Joomla-Erweiterung PHP-Webshells in den Webspace gelangt.&#xA;&#xA;Damit war bestätigt:&#xA;&#xA;  Fremder PHP-Code konnte mit den Rechten des Webspace-Benutzers ausgeführt werden.&#xA;&#xA;Das bedeutet noch nicht automatisch, dass der Server als root kompromittiert wurde.&#xA;&#xA;Aber alles, was der Webspace-Benutzer lesen konnte, musste als potentiell bekannt betrachtet werden.&#xA;&#xA;Dazu gehörte insbesondere:&#xA;&#xA;configuration.php&#xA;&#xA;In dieser Datei stehen unter anderem die Joomla-Datenbankverbindung und weitere Konfigurationswerte.&#xA;&#xA;Erst bereinigen, dann Passwörter ändern&#xA;&#xA;Bevor Zugangsdaten rotiert werden, sollte der offensichtliche Angriffsweg geschlossen sein.&#xA;&#xA;Sonst besteht die Gefahr, dass ein weiterhin vorhandener Webshell die neuen Zugangsdaten einfach erneut ausliest.&#xA;&#xA;Die Reihenfolge war deshalb grundsätzlich:&#xA;&#xA;verwundbare Erweiterung aktualisieren bzw. Angriffsweg blockieren,&#xA;bekannte Webshells und Backdoors sichern und aus dem Webroot entfernen,&#xA;nach weiteren verdächtigen Dateien und Persistenz suchen,&#xA;erst danach Zugangsdaten rotieren.&#xA;&#xA;Das bedeutet nicht, dass damit garantiert jede mögliche Hintertür ausgeschlossen ist. Aber neue Zugangsdaten in ein offensichtlich noch kompromittiertes System einzutragen wäre wenig sinnvoll.&#xA;&#xA;1. Joomla-Administratorpasswörter ändern&#xA;&#xA;Zunächst wurden die Passwörter aller aktiven Joomla-Administratoraccounts geändert.&#xA;&#xA;Dabei sollte man nicht nur den Hauptadministrator berücksichtigen.&#xA;&#xA;Im Joomla-Backend lohnt sich die Prüfung:&#xA;&#xA;Benutzer → Verwalten&#xA;&#xA;und dort insbesondere:&#xA;&#xA;Super Users&#xA;Administratoren&#xA;ältere oder kaum noch verwendete Accounts&#xA;&#xA;Nicht mehr benötigte privilegierte Konten sollten deaktiviert oder entfernt werden.&#xA;&#xA;Wichtig ist außerdem:&#xA;&#xA;  Das Passwort sollte neu sein und nicht lediglich eine Variante des bisherigen Passworts.&#xA;&#xA;Wenn Zugangsdaten möglicherweise abgeflossen sind, sollte das alte Passwort auch bei anderen Diensten nicht mehr verwendet werden.&#xA;&#xA;2. Bestehende Joomla-Sitzungen beenden&#xA;&#xA;Ein neues Passwort beendet nicht zwangsläufig jede bereits bestehende Sitzung.&#xA;&#xA;Ein Angreifer könnte theoretisch noch über eine gültige Session verfügen.&#xA;&#xA;Deshalb habe ich zusätzlich die Joomla-Sitzungstabelle geleert.&#xA;&#xA;Der Tabellenname hängt vom Präfix der jeweiligen Joomla-Installation ab und kann beispielsweise so aussehen:&#xA;&#xA;abcsession&#xA;&#xA;Vorher sollte man natürlich prüfen, welche Tabelle tatsächlich zur Installation gehört.&#xA;&#xA;Das Leeren der Session-Tabelle meldet alle aktuell angemeldeten Benutzer ab.&#xA;&#xA;Danach müssen sich auch legitime Administratoren erneut anmelden.&#xA;&#xA;Je nach Joomla-Version und verwendeten Funktionen können außerdem persistente Login-Schlüssel existieren. Auch diese sollten nach einem relevanten Sicherheitsvorfall geprüft bzw. invalidiert werden.&#xA;&#xA;3. Datenbankpasswort rotieren&#xA;&#xA;Dieser Schritt ist besonders wichtig, weil Joomla seine Datenbankzugangsdaten normalerweise in:&#xA;&#xA;configuration.php&#xA;&#xA;speichert.&#xA;&#xA;Wer diese Datei über eine Webshell lesen konnte, konnte damit unter Umständen auch Benutzername und Passwort der Joomla-Datenbank erfahren.&#xA;&#xA;Das Datenbankpasswort sollte deshalb geändert werden.&#xA;&#xA;Dabei gibt es allerdings eine kleine Falle:&#xA;&#xA;  Datenbankserver und Joomla müssen anschließend dasselbe neue Passwort kennen.&#xA;&#xA;Ändert man nur das Passwort des MySQL-/MariaDB-Benutzers, kann Joomla keine Datenbankverbindung mehr herstellen.&#xA;&#xA;Ändert man nur configuration.php, gilt dasselbe.&#xA;&#xA;Sinnvolle Reihenfolge beim DB-Passwort&#xA;&#xA;Vorher unbedingt ein Backup der Joomla-Konfiguration anlegen.&#xA;&#xA;Zum Beispiel:&#xA;&#xA;cp -a configuration.php /root/configuration.php-before-db-password-change&#xA;&#xA;Dabei ist zu beachten:&#xA;&#xA;Diese Sicherung enthält anschließend das alte Datenbankpasswort im Klartext.&#xA;&#xA;Sie sollte deshalb:&#xA;&#xA;außerhalb des Webroots liegen,&#xA;nur für root lesbar sein,&#xA;und nach erfolgreichem Abschluss später sicher gelöscht werden.&#xA;&#xA;Dann:&#xA;&#xA;neues Datenbankpasswort erzeugen,&#xA;Passwort des Datenbankbenutzers ändern,&#xA;Joomla-configuration.php unmittelbar auf dasselbe Passwort setzen,&#xA;Datenbankverbindung testen,&#xA;Webseite testen,&#xA;Joomla-Backend testen.&#xA;&#xA;Während dieses kurzen Übergangs kann die Webseite vorübergehend einen HTTP-500-Fehler liefern, wenn Datenbank und Joomla-Konfiguration noch nicht synchron sind.&#xA;&#xA;Das allein ist in diesem Moment noch kein Hinweis auf einen neuen Defekt.&#xA;&#xA;Datenbankverbindung direkt testen&#xA;&#xA;Nach der Änderung sollte nicht nur die Webseite getestet werden.&#xA;&#xA;Sinnvoll ist auch ein direkter Verbindungstest mit PHP oder dem MySQL-/MariaDB-Client.&#xA;&#xA;Bei PHP kann beispielsweise ein sehr kleiner Test verwendet werden, ohne das Passwort in der Shell-History zu hinterlassen.&#xA;&#xA;Wichtig ist vor allem:&#xA;&#xA;Verbindung erfolgreich&#xA;&#xA;und anschließend ein normaler Aufruf der Joomla-Seite.&#xA;&#xA;Bei meinem System wurden danach sowohl Startseite als auch Administratorbereich erneut kontrolliert.&#xA;&#xA;Beide mussten wieder HTTP 200 liefern.&#xA;&#xA;4. Passwort des Webspace-/Hosting-Benutzers ändern&#xA;&#xA;Wenn ein Angreifer Code mit den Rechten des Webspace-Benutzers ausführen konnte, ist auch dessen Zugang relevant.&#xA;&#xA;Bei Plesk gibt es dafür typischerweise einen Systembenutzer für die jeweilige Subscription bzw. Domain.&#xA;&#xA;Dieser Benutzer kann je nach Konfiguration beispielsweise für:&#xA;&#xA;SFTP,&#xA;SSH,&#xA;Dateizugriff,&#xA;Deployment&#xA;&#xA;verwendet werden.&#xA;&#xA;Auch dessen Passwort wurde deshalb geändert.&#xA;&#xA;Selbst wenn keine erfolgreiche SSH-Anmeldung dieses Benutzers nachgewiesen wurde, ist die Rotation sinnvoll, wenn dessen Zugangsdaten irgendwo für den kompromittierten Prozess erreichbar gewesen sein könnten.&#xA;&#xA;5. Private SSH-Schlüssel prüfen&#xA;&#xA;Ein Punkt, den man leicht übersieht:&#xA;&#xA;Im Home-Verzeichnis des Webspace-Benutzers können private SSH-Schlüssel liegen.&#xA;&#xA;Beispielsweise:&#xA;&#xA;~/.ssh/idrsa&#xA;&#xA;In meinem Fall existierte dort tatsächlich ein privater RSA-Schlüssel ohne Passphrase.&#xA;&#xA;Da der Webshell mit den Rechten dieses Benutzers lief, musste angenommen werden, dass der Schlüssel ausgelesen worden sein könnte.&#xA;&#xA;Der Schlüssel wurde deshalb nicht weiter verwendet.&#xA;&#xA;Bei einem kompromittierten privaten SSH-Schlüssel gilt:&#xA;&#xA;  Nicht nur die Datei löschen oder verschieben, sondern auch prüfen, wo der zugehörige öffentliche Schlüssel als authorizedkey hinterlegt wurde.&#xA;&#xA;Dort sollte der alte Schlüssel entfernt und gegebenenfalls durch einen neuen ersetzt werden.&#xA;&#xA;6. Muss das Root-Passwort ebenfalls geändert werden?&#xA;&#xA;Nicht zwangsläufig aufgrund der Webshell allein.&#xA;&#xA;Hier ist die Unterscheidung wichtig.&#xA;&#xA;Eine PHP-Webshell beweist zunächst die Kompromittierung des PHP-/Webspace-Kontexts.&#xA;&#xA;Sie beweist nicht automatisch:&#xA;&#xA;root wurde kompromittiert&#xA;&#xA;Das Root-Passwort sollte natürlich geändert werden, wenn es Hinweise gibt auf:&#xA;&#xA;erfolgreiche unbekannte Root-Logins,&#xA;manipulierte authorizedkeys,&#xA;verdächtige Systemdienste,&#xA;Root-Persistenz,&#xA;kompromittierte Administrationszugänge,&#xA;oder eine mögliche Privilege Escalation.&#xA;&#xA;In meinem Fall wurde das Root-Passwort im Zuge der Sicherheitsarbeiten ebenfalls erneuert.&#xA;&#xA;Für die Bewertung des Vorfalls blieb die Formulierung trotzdem:&#xA;&#xA;  Webspace-Kompromittierung bestätigt, Root-Kompromittierung nicht nachgewiesen.&#xA;&#xA;Diese Unterscheidung hilft, Beobachtungen und Vermutungen nicht miteinander zu vermischen.&#xA;&#xA;7. Plesk- und Provider-Zugang nicht vergessen&#xA;&#xA;Je nach Hostingumgebung können außerdem weitere Zugangsdaten relevant sein:&#xA;&#xA;Plesk&#xA;Hosting-Provider&#xA;DNS-Provider&#xA;Domain-Registrar&#xA;Backup-System&#xA;Deployment-Dienste&#xA;Git-Zugänge&#xA;&#xA;Ob sie tatsächlich geändert werden müssen, hängt davon ab, ob die entsprechenden Zugangsdaten für das kompromittierte System erreichbar waren.&#xA;&#xA;Ein im Browser gespeichertes Provider-Passwort ist beispielsweise durch eine PHP-Webshell auf dem Server nicht automatisch kompromittiert.&#xA;&#xA;Ein API-Key in einer für den Webserver lesbaren Konfigurationsdatei dagegen schon.&#xA;&#xA;Die bessere Frage lautet also:&#xA;&#xA;  Konnte der kompromittierte Benutzer dieses Geheimnis lesen?&#xA;&#xA;8. Auch Mail-Zugangsdaten und API-Keys berücksichtigen&#xA;&#xA;Viele Joomla-Installationen enthalten mehr Geheimnisse als nur die Datenbankverbindung.&#xA;&#xA;Mögliche Beispiele:&#xA;&#xA;SMTP-Passwörter&#xA;API-Keys&#xA;Cloud-Zugangsdaten&#xA;Backup-Credentials&#xA;OAuth-Secrets&#xA;externe Datenbankzugänge&#xA;FTP-/SFTP-Zugangsdaten&#xA;&#xA;Sie können in:&#xA;&#xA;configuration.php&#xA;.php-Dateien&#xA;.env-Dateien&#xA;Backup-Dateien&#xA;Erweiterungskonfigurationen&#xA;&#xA;liegen.&#xA;&#xA;Nach einer Webshell-Kompromittierung lohnt sich deshalb die Suche nach weiteren Secrets im Webroot.&#xA;&#xA;Nicht jedes gefundene Passwort muss automatisch geändert werden.&#xA;&#xA;Aber alles, was der kompromittierte Benutzer lesen konnte und noch gültig ist, sollte bewertet werden.&#xA;&#xA;9. Vorsicht mit alten Backups&#xA;&#xA;Während einer Reparatur entstehen schnell Dateien wie:&#xA;&#xA;configuration.php.bak&#xA;configuration.php.old&#xA;configuration.php-before-change&#xA;&#xA;Diese Backups enthalten häufig dieselben Passwörter wie das Original.&#xA;&#xA;Noch problematischer wird es, wenn solche Dateien versehentlich im öffentlichen Webroot liegen.&#xA;&#xA;Deshalb:&#xA;&#xA;Konfigurationsbackups möglichst unter /root oder einem anderen nicht öffentlichen Ort ablegen,&#xA;restriktive Dateirechte setzen,&#xA;nach erfolgreicher Reparatur nicht benötigte Kopien löschen.&#xA;&#xA;Ein Passwortwechsel hilft wenig, wenn anschließend zehn alte Konfigurationsbackups mit dem vorherigen Passwort herumliegen.&#xA;&#xA;10. Nach jeder Änderung testen&#xA;&#xA;Bei mehreren gleichzeitigen Passwortänderungen kann es sonst schwierig werden herauszufinden, welcher Schritt eine Anwendung beschädigt hat.&#xA;&#xA;Deshalb habe ich die Änderungen nacheinander durchgeführt und jeweils getestet.&#xA;&#xA;Typische Tests:&#xA;&#xA;curl -I https://example.org/&#xA;&#xA;Erwartet:&#xA;&#xA;HTTP 200&#xA;&#xA;Zusätzlich:&#xA;&#xA;Frontend aufrufen&#xA;Joomla-Backend aufrufen&#xA;Administrator-Login testen&#xA;Datenbankzugriff prüfen&#xA;&#xA;So lässt sich ein Fehler unmittelbar dem letzten Änderungsschritt zuordnen.&#xA;&#xA;11. Neue Zugangsdaten nicht in der Shell-History hinterlassen&#xA;&#xA;Gerade bei der Arbeit als root sollte man vermeiden, Passwörter direkt in Befehle einzutragen wie:&#xA;&#xA;mysql -u user -pSUPERGEHEIMESPASSWORT&#xA;&#xA;Denn je nach Shell und Konfiguration kann dieser Befehl in der History landen.&#xA;&#xA;Besser ist:&#xA;&#xA;mysql -u user -p&#xA;&#xA;Danach fragt der Client das Passwort interaktiv ab.&#xA;&#xA;Dasselbe Prinzip gilt für andere Programme, sofern sie eine sichere Passwortabfrage anbieten.&#xA;&#xA;12. Reihenfolge aus der Praxis&#xA;&#xA;Nach dem konkreten Vorfall hat sich für mich folgende Reihenfolge als sinnvoll erwiesen:&#xA;&#xA;Sicherheitslücke schließen.&#xA;bekannte Webshells und Backdoors entfernen bzw. quarantänisieren.&#xA;nach weiterer Persistenz suchen.&#xA;Joomla-Administratorpasswörter ändern.&#xA;Joomla-Sessions invalidieren.&#xA;Datenbankpasswort ändern und configuration.php synchron aktualisieren.&#xA;Datenbank und Webseite testen.&#xA;Passwort des Webspace-/Hosting-Benutzers ändern.&#xA;vorhandene private SSH-Schlüssel bewerten und gegebenenfalls ersetzen.&#xA;10. weitere für den Webserver lesbare Secrets prüfen.&#xA;11. Plesk-, Provider- und DNS-Zugänge nach Risikolage bewerten.&#xA;12. alte Backups mit inzwischen ungültigen oder sensiblen Zugangsdaten aufräumen.&#xA;13. anschließend erneut Logs, Prozesse und Dateisystem kontrollieren.&#xA;&#xA;Was ein Passwortwechsel nicht leistet&#xA;&#xA;Eine Credential-Rotation ist wichtig.&#xA;&#xA;Sie beseitigt aber keine Malware.&#xA;&#xA;Wenn weiterhin eine Webshell aktiv ist, können neue Passwörter möglicherweise sofort wieder abgegriffen werden.&#xA;&#xA;Umgekehrt bedeutet eine erfolgreiche Bereinigung des Webroots nicht, dass alte Zugangsdaten weiterhin bedenkenlos verwendet werden sollten.&#xA;&#xA;Beides gehört zusammen:&#xA;&#xA;Bereinigung + Zugangsdatenrotation&#xA;&#xA;Kurzfassung&#xA;&#xA;Nach einem bestätigten Joomla-Webshell-Befall würde ich mindestens folgende Dinge prüfen bzw. erneuern:&#xA;&#xA;Joomla-Administratorpasswörter&#xA;Joomla-Sitzungen&#xA;Datenbankpasswort&#xA;Webspace-/Hosting-Benutzer&#xA;private SSH-Schlüssel&#xA;SMTP-/API-/sonstige Secrets&#xA;&#xA;Weitere Administrationszugänge wie:&#xA;&#xA;root&#xA;Plesk&#xA;Hosting-Provider&#xA;DNS&#xA;Domain-Registrar&#xA;&#xA;sollten abhängig davon bewertet werden, ob es einen realistischen Zugriffspfad oder konkrete Hinweise auf eine Kompromittierung gibt.&#xA;&#xA;Die wichtigste Grundregel lautet:&#xA;&#xA;  Alles, was ein kompromittierter Webspace-Benutzer lesen konnte, sollte als potentiell offengelegt betrachtet werden.&#xA;&#xA;Und genauso wichtig:&#xA;&#xA;  Eine PHP-Webshell beweist nicht automatisch eine Root-Kompromittierung.&#xA;&#xA;Diese beiden Aussagen helfen, nach einem Vorfall weder zu wenig noch unnötig viel aus den vorhandenen Spuren abzuleiten.]]&gt;</description>
      <content:encoded><![CDATA[<p>Nach einem bestätigten Webshell-Befall reicht es nicht, die schädlichen PHP-Dateien zu löschen und die betroffene Joomla-Erweiterung zu aktualisieren.</p>

<p>Wenn fremder PHP-Code mit den Rechten des Webservers ausgeführt werden konnte, muss man davon ausgehen, dass dieser Code auch auf Dateien zugreifen konnte, die für den Webserver lesbar waren.</p>

<p>Bei Joomla gehört dazu insbesondere die <code>configuration.php</code>.</p>

<p>Damit können nach einem solchen Vorfall verschiedene Zugangsdaten als potentiell offengelegt gelten.</p>

<p>Dieser Beitrag beschreibt, welche Zugangsdaten ich nach einem tatsächlichen Joomla-Webshell-Befall geändert habe – und in welcher Reihenfolge.</p>

<h2 id="ausgangslage">Ausgangslage</h2>

<p>In meinem Fall waren über eine verwundbare Joomla-Erweiterung PHP-Webshells in den Webspace gelangt.</p>

<p>Damit war bestätigt:</p>

<blockquote><p>Fremder PHP-Code konnte mit den Rechten des Webspace-Benutzers ausgeführt werden.</p></blockquote>

<p>Das bedeutet noch nicht automatisch, dass der Server als <code>root</code> kompromittiert wurde.</p>

<p>Aber alles, was der Webspace-Benutzer lesen konnte, musste als potentiell bekannt betrachtet werden.</p>

<p>Dazu gehörte insbesondere:</p>

<pre><code class="language-text">configuration.php
</code></pre>

<p>In dieser Datei stehen unter anderem die Joomla-Datenbankverbindung und weitere Konfigurationswerte.</p>

<h2 id="erst-bereinigen-dann-passwörter-ändern">Erst bereinigen, dann Passwörter ändern</h2>

<p>Bevor Zugangsdaten rotiert werden, sollte der offensichtliche Angriffsweg geschlossen sein.</p>

<p>Sonst besteht die Gefahr, dass ein weiterhin vorhandener Webshell die neuen Zugangsdaten einfach erneut ausliest.</p>

<p>Die Reihenfolge war deshalb grundsätzlich:</p>
<ol><li>verwundbare Erweiterung aktualisieren bzw. Angriffsweg blockieren,</li>
<li>bekannte Webshells und Backdoors sichern und aus dem Webroot entfernen,</li>
<li>nach weiteren verdächtigen Dateien und Persistenz suchen,</li>
<li>erst danach Zugangsdaten rotieren.</li></ol>

<p>Das bedeutet nicht, dass damit garantiert jede mögliche Hintertür ausgeschlossen ist. Aber neue Zugangsdaten in ein offensichtlich noch kompromittiertes System einzutragen wäre wenig sinnvoll.</p>

<h2 id="1-joomla-administratorpasswörter-ändern">1. Joomla-Administratorpasswörter ändern</h2>

<p>Zunächst wurden die Passwörter aller aktiven Joomla-Administratoraccounts geändert.</p>

<p>Dabei sollte man nicht nur den Hauptadministrator berücksichtigen.</p>

<p>Im Joomla-Backend lohnt sich die Prüfung:</p>

<pre><code class="language-text">Benutzer → Verwalten
</code></pre>

<p>und dort insbesondere:</p>
<ul><li>Super Users</li>
<li>Administratoren</li>
<li>ältere oder kaum noch verwendete Accounts</li></ul>

<p>Nicht mehr benötigte privilegierte Konten sollten deaktiviert oder entfernt werden.</p>

<p>Wichtig ist außerdem:</p>

<blockquote><p>Das Passwort sollte neu sein und nicht lediglich eine Variante des bisherigen Passworts.</p></blockquote>

<p>Wenn Zugangsdaten möglicherweise abgeflossen sind, sollte das alte Passwort auch bei anderen Diensten nicht mehr verwendet werden.</p>

<h2 id="2-bestehende-joomla-sitzungen-beenden">2. Bestehende Joomla-Sitzungen beenden</h2>

<p>Ein neues Passwort beendet nicht zwangsläufig jede bereits bestehende Sitzung.</p>

<p>Ein Angreifer könnte theoretisch noch über eine gültige Session verfügen.</p>

<p>Deshalb habe ich zusätzlich die Joomla-Sitzungstabelle geleert.</p>

<p>Der Tabellenname hängt vom Präfix der jeweiligen Joomla-Installation ab und kann beispielsweise so aussehen:</p>

<pre><code class="language-text">abc_session
</code></pre>

<p>Vorher sollte man natürlich prüfen, welche Tabelle tatsächlich zur Installation gehört.</p>

<p>Das Leeren der Session-Tabelle meldet alle aktuell angemeldeten Benutzer ab.</p>

<p>Danach müssen sich auch legitime Administratoren erneut anmelden.</p>

<p>Je nach Joomla-Version und verwendeten Funktionen können außerdem persistente Login-Schlüssel existieren. Auch diese sollten nach einem relevanten Sicherheitsvorfall geprüft bzw. invalidiert werden.</p>

<h2 id="3-datenbankpasswort-rotieren">3. Datenbankpasswort rotieren</h2>

<p>Dieser Schritt ist besonders wichtig, weil Joomla seine Datenbankzugangsdaten normalerweise in:</p>

<pre><code class="language-text">configuration.php
</code></pre>

<p>speichert.</p>

<p>Wer diese Datei über eine Webshell lesen konnte, konnte damit unter Umständen auch Benutzername und Passwort der Joomla-Datenbank erfahren.</p>

<p>Das Datenbankpasswort sollte deshalb geändert werden.</p>

<p>Dabei gibt es allerdings eine kleine Falle:</p>

<blockquote><p>Datenbankserver und Joomla müssen anschließend dasselbe neue Passwort kennen.</p></blockquote>

<p>Ändert man nur das Passwort des MySQL-/MariaDB-Benutzers, kann Joomla keine Datenbankverbindung mehr herstellen.</p>

<p>Ändert man nur <code>configuration.php</code>, gilt dasselbe.</p>

<h2 id="sinnvolle-reihenfolge-beim-db-passwort">Sinnvolle Reihenfolge beim DB-Passwort</h2>

<p>Vorher unbedingt ein Backup der Joomla-Konfiguration anlegen.</p>

<p>Zum Beispiel:</p>

<pre><code class="language-bash">cp -a configuration.php /root/configuration.php-before-db-password-change
</code></pre>

<p>Dabei ist zu beachten:</p>

<p>Diese Sicherung enthält anschließend das <strong>alte Datenbankpasswort im Klartext</strong>.</p>

<p>Sie sollte deshalb:</p>
<ul><li>außerhalb des Webroots liegen,</li>
<li>nur für root lesbar sein,</li>
<li>und nach erfolgreichem Abschluss später sicher gelöscht werden.</li></ul>

<p>Dann:</p>
<ol><li>neues Datenbankpasswort erzeugen,</li>
<li>Passwort des Datenbankbenutzers ändern,</li>
<li>Joomla-<code>configuration.php</code> unmittelbar auf dasselbe Passwort setzen,</li>
<li>Datenbankverbindung testen,</li>
<li>Webseite testen,</li>
<li>Joomla-Backend testen.</li></ol>

<p>Während dieses kurzen Übergangs kann die Webseite vorübergehend einen HTTP-500-Fehler liefern, wenn Datenbank und Joomla-Konfiguration noch nicht synchron sind.</p>

<p>Das allein ist in diesem Moment noch kein Hinweis auf einen neuen Defekt.</p>

<h2 id="datenbankverbindung-direkt-testen">Datenbankverbindung direkt testen</h2>

<p>Nach der Änderung sollte nicht nur die Webseite getestet werden.</p>

<p>Sinnvoll ist auch ein direkter Verbindungstest mit PHP oder dem MySQL-/MariaDB-Client.</p>

<p>Bei PHP kann beispielsweise ein sehr kleiner Test verwendet werden, ohne das Passwort in der Shell-History zu hinterlassen.</p>

<p>Wichtig ist vor allem:</p>

<pre><code class="language-text">Verbindung erfolgreich
</code></pre>

<p>und anschließend ein normaler Aufruf der Joomla-Seite.</p>

<p>Bei meinem System wurden danach sowohl Startseite als auch Administratorbereich erneut kontrolliert.</p>

<p>Beide mussten wieder HTTP 200 liefern.</p>

<h2 id="4-passwort-des-webspace-hosting-benutzers-ändern">4. Passwort des Webspace-/Hosting-Benutzers ändern</h2>

<p>Wenn ein Angreifer Code mit den Rechten des Webspace-Benutzers ausführen konnte, ist auch dessen Zugang relevant.</p>

<p>Bei Plesk gibt es dafür typischerweise einen Systembenutzer für die jeweilige Subscription bzw. Domain.</p>

<p>Dieser Benutzer kann je nach Konfiguration beispielsweise für:</p>
<ul><li>SFTP,</li>
<li>SSH,</li>
<li>Dateizugriff,</li>
<li>Deployment</li></ul>

<p>verwendet werden.</p>

<p>Auch dessen Passwort wurde deshalb geändert.</p>

<p>Selbst wenn keine erfolgreiche SSH-Anmeldung dieses Benutzers nachgewiesen wurde, ist die Rotation sinnvoll, wenn dessen Zugangsdaten irgendwo für den kompromittierten Prozess erreichbar gewesen sein könnten.</p>

<h2 id="5-private-ssh-schlüssel-prüfen">5. Private SSH-Schlüssel prüfen</h2>

<p>Ein Punkt, den man leicht übersieht:</p>

<p>Im Home-Verzeichnis des Webspace-Benutzers können private SSH-Schlüssel liegen.</p>

<p>Beispielsweise:</p>

<pre><code class="language-text">~/.ssh/id_rsa
</code></pre>

<p>In meinem Fall existierte dort tatsächlich ein privater RSA-Schlüssel ohne Passphrase.</p>

<p>Da der Webshell mit den Rechten dieses Benutzers lief, musste angenommen werden, dass der Schlüssel ausgelesen worden sein könnte.</p>

<p>Der Schlüssel wurde deshalb nicht weiter verwendet.</p>

<p>Bei einem kompromittierten privaten SSH-Schlüssel gilt:</p>

<blockquote><p>Nicht nur die Datei löschen oder verschieben, sondern auch prüfen, wo der zugehörige öffentliche Schlüssel als <code>authorized_key</code> hinterlegt wurde.</p></blockquote>

<p>Dort sollte der alte Schlüssel entfernt und gegebenenfalls durch einen neuen ersetzt werden.</p>

<h2 id="6-muss-das-root-passwort-ebenfalls-geändert-werden">6. Muss das Root-Passwort ebenfalls geändert werden?</h2>

<p>Nicht zwangsläufig aufgrund der Webshell allein.</p>

<p>Hier ist die Unterscheidung wichtig.</p>

<p>Eine PHP-Webshell beweist zunächst die Kompromittierung des PHP-/Webspace-Kontexts.</p>

<p>Sie beweist nicht automatisch:</p>

<pre><code class="language-text">root wurde kompromittiert
</code></pre>

<p>Das Root-Passwort sollte natürlich geändert werden, wenn es Hinweise gibt auf:</p>
<ul><li>erfolgreiche unbekannte Root-Logins,</li>
<li>manipulierte <code>authorized_keys</code>,</li>
<li>verdächtige Systemdienste,</li>
<li>Root-Persistenz,</li>
<li>kompromittierte Administrationszugänge,</li>
<li>oder eine mögliche Privilege Escalation.</li></ul>

<p>In meinem Fall wurde das Root-Passwort im Zuge der Sicherheitsarbeiten ebenfalls erneuert.</p>

<p>Für die Bewertung des Vorfalls blieb die Formulierung trotzdem:</p>

<blockquote><p>Webspace-Kompromittierung bestätigt, Root-Kompromittierung nicht nachgewiesen.</p></blockquote>

<p>Diese Unterscheidung hilft, Beobachtungen und Vermutungen nicht miteinander zu vermischen.</p>

<h2 id="7-plesk-und-provider-zugang-nicht-vergessen">7. Plesk- und Provider-Zugang nicht vergessen</h2>

<p>Je nach Hostingumgebung können außerdem weitere Zugangsdaten relevant sein:</p>

<pre><code class="language-text">Plesk
Hosting-Provider
DNS-Provider
Domain-Registrar
Backup-System
Deployment-Dienste
Git-Zugänge
</code></pre>

<p>Ob sie tatsächlich geändert werden müssen, hängt davon ab, ob die entsprechenden Zugangsdaten für das kompromittierte System erreichbar waren.</p>

<p>Ein im Browser gespeichertes Provider-Passwort ist beispielsweise durch eine PHP-Webshell auf dem Server nicht automatisch kompromittiert.</p>

<p>Ein API-Key in einer für den Webserver lesbaren Konfigurationsdatei dagegen schon.</p>

<p>Die bessere Frage lautet also:</p>

<blockquote><p>Konnte der kompromittierte Benutzer dieses Geheimnis lesen?</p></blockquote>

<h2 id="8-auch-mail-zugangsdaten-und-api-keys-berücksichtigen">8. Auch Mail-Zugangsdaten und API-Keys berücksichtigen</h2>

<p>Viele Joomla-Installationen enthalten mehr Geheimnisse als nur die Datenbankverbindung.</p>

<p>Mögliche Beispiele:</p>

<pre><code class="language-text">SMTP-Passwörter
API-Keys
Cloud-Zugangsdaten
Backup-Credentials
OAuth-Secrets
externe Datenbankzugänge
FTP-/SFTP-Zugangsdaten
</code></pre>

<p>Sie können in:</p>

<pre><code class="language-text">configuration.php
.php-Dateien
.env-Dateien
Backup-Dateien
Erweiterungskonfigurationen
</code></pre>

<p>liegen.</p>

<p>Nach einer Webshell-Kompromittierung lohnt sich deshalb die Suche nach weiteren Secrets im Webroot.</p>

<p>Nicht jedes gefundene Passwort muss automatisch geändert werden.</p>

<p>Aber alles, was der kompromittierte Benutzer lesen konnte und noch gültig ist, sollte bewertet werden.</p>

<h2 id="9-vorsicht-mit-alten-backups">9. Vorsicht mit alten Backups</h2>

<p>Während einer Reparatur entstehen schnell Dateien wie:</p>

<pre><code class="language-text">configuration.php.bak
configuration.php.old
configuration.php-before-change
</code></pre>

<p>Diese Backups enthalten häufig dieselben Passwörter wie das Original.</p>

<p>Noch problematischer wird es, wenn solche Dateien versehentlich im öffentlichen Webroot liegen.</p>

<p>Deshalb:</p>
<ul><li>Konfigurationsbackups möglichst unter <code>/root</code> oder einem anderen nicht öffentlichen Ort ablegen,</li>
<li>restriktive Dateirechte setzen,</li>
<li>nach erfolgreicher Reparatur nicht benötigte Kopien löschen.</li></ul>

<p>Ein Passwortwechsel hilft wenig, wenn anschließend zehn alte Konfigurationsbackups mit dem vorherigen Passwort herumliegen.</p>

<h2 id="10-nach-jeder-änderung-testen">10. Nach jeder Änderung testen</h2>

<p>Bei mehreren gleichzeitigen Passwortänderungen kann es sonst schwierig werden herauszufinden, welcher Schritt eine Anwendung beschädigt hat.</p>

<p>Deshalb habe ich die Änderungen nacheinander durchgeführt und jeweils getestet.</p>

<p>Typische Tests:</p>

<pre><code class="language-bash">curl -I https://example.org/
</code></pre>

<p>Erwartet:</p>

<pre><code class="language-text">HTTP 200
</code></pre>

<p>Zusätzlich:</p>

<pre><code class="language-text">Frontend aufrufen
Joomla-Backend aufrufen
Administrator-Login testen
Datenbankzugriff prüfen
</code></pre>

<p>So lässt sich ein Fehler unmittelbar dem letzten Änderungsschritt zuordnen.</p>

<h2 id="11-neue-zugangsdaten-nicht-in-der-shell-history-hinterlassen">11. Neue Zugangsdaten nicht in der Shell-History hinterlassen</h2>

<p>Gerade bei der Arbeit als root sollte man vermeiden, Passwörter direkt in Befehle einzutragen wie:</p>

<pre><code class="language-bash">mysql -u user -pSUPERGEHEIMESPASSWORT
</code></pre>

<p>Denn je nach Shell und Konfiguration kann dieser Befehl in der History landen.</p>

<p>Besser ist:</p>

<pre><code class="language-bash">mysql -u user -p
</code></pre>

<p>Danach fragt der Client das Passwort interaktiv ab.</p>

<p>Dasselbe Prinzip gilt für andere Programme, sofern sie eine sichere Passwortabfrage anbieten.</p>

<h2 id="12-reihenfolge-aus-der-praxis">12. Reihenfolge aus der Praxis</h2>

<p>Nach dem konkreten Vorfall hat sich für mich folgende Reihenfolge als sinnvoll erwiesen:</p>
<ol><li>Sicherheitslücke schließen.</li>
<li>bekannte Webshells und Backdoors entfernen bzw. quarantänisieren.</li>
<li>nach weiterer Persistenz suchen.</li>
<li>Joomla-Administratorpasswörter ändern.</li>
<li>Joomla-Sessions invalidieren.</li>
<li>Datenbankpasswort ändern und <code>configuration.php</code> synchron aktualisieren.</li>
<li>Datenbank und Webseite testen.</li>
<li>Passwort des Webspace-/Hosting-Benutzers ändern.</li>
<li>vorhandene private SSH-Schlüssel bewerten und gegebenenfalls ersetzen.</li>
<li>weitere für den Webserver lesbare Secrets prüfen.</li>
<li>Plesk-, Provider- und DNS-Zugänge nach Risikolage bewerten.</li>
<li>alte Backups mit inzwischen ungültigen oder sensiblen Zugangsdaten aufräumen.</li>
<li>anschließend erneut Logs, Prozesse und Dateisystem kontrollieren.</li></ol>

<h2 id="was-ein-passwortwechsel-nicht-leistet">Was ein Passwortwechsel nicht leistet</h2>

<p>Eine Credential-Rotation ist wichtig.</p>

<p>Sie beseitigt aber keine Malware.</p>

<p>Wenn weiterhin eine Webshell aktiv ist, können neue Passwörter möglicherweise sofort wieder abgegriffen werden.</p>

<p>Umgekehrt bedeutet eine erfolgreiche Bereinigung des Webroots nicht, dass alte Zugangsdaten weiterhin bedenkenlos verwendet werden sollten.</p>

<p>Beides gehört zusammen:</p>

<pre><code class="language-text">Bereinigung + Zugangsdatenrotation
</code></pre>

<h2 id="kurzfassung">Kurzfassung</h2>

<p>Nach einem bestätigten Joomla-Webshell-Befall würde ich mindestens folgende Dinge prüfen bzw. erneuern:</p>

<pre><code class="language-text">Joomla-Administratorpasswörter
Joomla-Sitzungen
Datenbankpasswort
Webspace-/Hosting-Benutzer
private SSH-Schlüssel
SMTP-/API-/sonstige Secrets
</code></pre>

<p>Weitere Administrationszugänge wie:</p>

<pre><code class="language-text">root
Plesk
Hosting-Provider
DNS
Domain-Registrar
</code></pre>

<p>sollten abhängig davon bewertet werden, ob es einen realistischen Zugriffspfad oder konkrete Hinweise auf eine Kompromittierung gibt.</p>

<p>Die wichtigste Grundregel lautet:</p>

<blockquote><p>Alles, was ein kompromittierter Webspace-Benutzer lesen konnte, sollte als potentiell offengelegt betrachtet werden.</p></blockquote>

<p>Und genauso wichtig:</p>

<blockquote><p>Eine PHP-Webshell beweist nicht automatisch eine Root-Kompromittierung.</p></blockquote>

<p>Diese beiden Aussagen helfen, nach einem Vorfall weder zu wenig noch unnötig viel aus den vorhandenen Spuren abzuleiten.</p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-nach-einem-webshell-hack-welche-passwoerter-und-sitzungen-sollte-man</guid>
      <pubDate>Wed, 16 Sep 2026 13:07:14 +0000</pubDate>
    </item>
    <item>
      <title>Joomla gehackt: Warum Google plötzlich fremde Subdomains wie m., admin-v2. oder monitor. indexiert</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-gehackt-warum-google-ploetzlich-fremde-subdomains-wie-m-admin-v2</link>
      <description>&lt;![CDATA[Nach einem Joomla-Sicherheitsvorfall tauchten in der Google Search Console plötzlich zahlreiche URLs unter Subdomains auf, die ich selbst nie eingerichtet hatte.&#xA;&#xA;Beispiele sahen sinngemäß so aus:&#xA;&#xA;m.example.org/...&#xA;admin-v2.example.org/...&#xA;monitor.example.org/...&#xA;wildcard.example.org/...&#xA;&#xA;Das wirkte zunächst so, als hätte jemand diese Subdomains auf dem Server angelegt.&#xA;&#xA;Die eigentliche Ursache war aber eine Kombination aus zwei Dingen:&#xA;&#xA;Die Joomla-Seite war zuvor kompromittiert worden und Google hatte dabei ungewöhnliche bzw. manipulierte URLs entdeckt.&#xA;Für die Domain existierte ein Wildcard-DNS-Eintrag, durch den praktisch jede beliebige Subdomain auf denselben Webserver zeigte.&#xA;&#xA;Der Wildcard-DNS-Eintrag war also nicht die Ursache des Hacks. Er wirkte aber wie ein Verstärker für die unerwünschte Indexierung.&#xA;&#xA;Was ist ein Wildcard-DNS-Eintrag?&#xA;&#xA;Ein Eintrag wie&#xA;&#xA;.example.org   A   203.0.113.10&#xA;&#xA;bedeutet:&#xA;&#xA;  Jede nicht ausdrücklich definierte Subdomain von example.org soll auf diese IP-Adresse zeigen.&#xA;&#xA;Damit funktionieren nicht nur bewusst angelegte Hosts wie:&#xA;&#xA;www.example.org&#xA;mail.example.org&#xA;&#xA;sondern grundsätzlich auch:&#xA;&#xA;test.example.org&#xA;abc.example.org&#xA;m.example.org&#xA;admin-v2.example.org&#xA;monitor.example.org&#xA;irgendwas.example.org&#xA;&#xA;sofern dafür kein spezifischerer DNS-Eintrag existiert.&#xA;&#xA;Ein Test ist einfach:&#xA;&#xA;dig zufallsname-12345.example.org A +short&#xA;&#xA;Wenn dort dieselbe Server-IP zurückkommt, obwohl diese Subdomain nie eingerichtet wurde, ist sehr wahrscheinlich ein Wildcard-DNS-Eintrag aktiv.&#xA;&#xA;DNS allein reicht noch nicht&#xA;&#xA;Ein wichtiger Punkt:&#xA;&#xA;Der Wildcard-DNS-Eintrag allein sorgt noch nicht dafür, dass eine Webseite unter jeder Subdomain erscheint.&#xA;&#xA;DNS beantwortet zunächst nur die Frage:&#xA;&#xA;  Auf welche IP-Adresse soll dieser Hostname zeigen?&#xA;&#xA;Danach entscheidet der Webserver, was mit dem HTTP-Request passiert.&#xA;&#xA;Wenn Apache, nginx oder die Hosting-Konfiguration unbekannte Hostnamen einfach an dieselbe Joomla-Installation weiterleitet, kann die Webseite tatsächlich auch unter beliebigen Subdomains erreichbar sein.&#xA;&#xA;Dann wird aus:&#xA;&#xA;https://www.example.org/artikel&#xA;&#xA;möglicherweise ebenfalls:&#xA;&#xA;https://m.example.org/artikel&#xA;https://monitor.example.org/artikel&#xA;https://abc.example.org/artikel&#xA;&#xA;Wenn diese Varianten öffentlich erreichbar sind, können auch Suchmaschinen sie entdecken.&#xA;&#xA;Der Zusammenhang mit einem Joomla-Hack&#xA;&#xA;In meinem Fall war zuvor eine Joomla-Erweiterung kompromittiert worden.&#xA;&#xA;Dabei waren unter anderem ungewöhnliche URLs entstanden, die von Google gecrawlt wurden.&#xA;&#xA;Eine davon verwies beispielsweise auf eine hochgeladene PHP-Datei innerhalb einer Joomla-Komponente.&#xA;&#xA;Google kann solche URLs unter anderem über externe Links, Crawling, Sitemaps, interne Verweise oder bereits bekannte URL-Muster entdecken.&#xA;&#xA;Wenn gleichzeitig jede beliebige Subdomain auf dieselbe Joomla-Seite zeigt, vervielfacht sich die Zahl potentiell erreichbarer URL-Varianten.&#xA;&#xA;Aus einer URL kann dann theoretisch eine ganze Familie werden:&#xA;&#xA;example.org/pfad&#xA;m.example.org/pfad&#xA;admin-v2.example.org/pfad&#xA;monitor.example.org/pfad&#xA;foo.example.org/pfad&#xA;bar.example.org/pfad&#xA;&#xA;Das bedeutet nicht zwangsläufig, dass ein Angreifer jede dieser Subdomains bewusst eingerichtet hat.&#xA;&#xA;Sie können schlicht deshalb funktionieren, weil:&#xA;&#xA;.example.org&#xA;&#xA;auf denselben Server zeigt.&#xA;&#xA;So habe ich den Wildcard-Eintrag gefunden&#xA;&#xA;Zunächst wurde die öffentliche DNS-Konfiguration überprüft.&#xA;&#xA;Ein Test mit einer frei erfundenen Subdomain ist dafür besonders hilfreich:&#xA;&#xA;dig diesegibtesgarantiertnicht.example.org A&#xA;&#xA;Wenn die Antwort beispielsweise lautet:&#xA;&#xA;diesegibtesgarantiertnicht.example.org. 86400 IN A 203.0.113.10&#xA;&#xA;ist klar:&#xA;&#xA;Auch nicht eingerichtete Subdomains werden aufgelöst.&#xA;&#xA;Anschließend sollte man die DNS-Zone beim tatsächlichen DNS-Provider prüfen.&#xA;&#xA;Gesucht wird nach einem Eintrag wie:&#xA;&#xA;bzw.&#xA;&#xA;.example.org&#xA;&#xA;mit einem A- oder AAAA-Record.&#xA;&#xA;Zusätzlich prüfen, ob der Webserver die Subdomain annimmt&#xA;&#xA;Nach der DNS-Prüfung sollte getestet werden, was der Webserver mit einem erfundenen Hostnamen macht.&#xA;&#xA;Zum Beispiel:&#xA;&#xA;curl -I https://zufall-12345.example.org/&#xA;&#xA;Wenn die Seite dort normal mit:&#xA;&#xA;HTTP/2 200&#xA;&#xA;antwortet, wird der unbekannte Hostname tatsächlich von der Webseite bedient.&#xA;&#xA;Für einen gezielten Test gegen eine bestimmte Server-IP kann curl auch ohne Änderung des öffentlichen DNS verwendet werden:&#xA;&#xA;curl --resolve zufall-12345.example.org:443:203.0.113.10 \&#xA;  -I https://zufall-12345.example.org/&#xA;&#xA;Damit wird für diesen einzelnen Test so getan, als würde der Name auf die angegebene IP zeigen.&#xA;&#xA;Wildcard-DNS entfernen&#xA;&#xA;Wenn beliebige Subdomains nicht benötigt werden, kann der Wildcard-Eintrag entfernt werden.&#xA;&#xA;Danach sollte ein Test wie:&#xA;&#xA;dig zufall-12345.example.org A +short&#xA;&#xA;keine Server-IP mehr zurückgeben.&#xA;&#xA;Wichtig ist dabei die DNS-TTL.&#xA;&#xA;Bei einem Eintrag mit beispielsweise:&#xA;&#xA;86400&#xA;&#xA;beträgt die maximale Cache-Zeit:&#xA;&#xA;86400 Sekunden = 24 Stunden&#xA;&#xA;Ein bereits aufgelöster Wildcard-Eintrag kann deshalb noch eine Zeit lang in DNS-Caches vorhanden sein.&#xA;&#xA;Das bedeutet nicht, dass die Änderung nicht funktioniert hat.&#xA;&#xA;DNS entfernen reicht langfristig nicht immer aus&#xA;&#xA;Zusätzlich zur DNS-Bereinigung ist es sinnvoll, den Webserver so zu konfigurieren, dass unbekannte Hostnamen nicht einfach dieselbe Joomla-Seite ausliefern.&#xA;&#xA;Ein Server sollte idealerweise nur die ausdrücklich vorgesehenen Hosts akzeptieren, zum Beispiel:&#xA;&#xA;example.org&#xA;www.example.org&#xA;&#xA;Unbekannte Hosts können stattdessen:&#xA;&#xA;abgewiesen werden,&#xA;einen Fehlerstatus liefern,&#xA;oder kontrolliert auf die kanonische Domain umgeleitet werden.&#xA;&#xA;Welche Variante sinnvoll ist, hängt von der jeweiligen Server- und Hosting-Konfiguration ab.&#xA;&#xA;Der Vorteil:&#xA;&#xA;Selbst wenn später versehentlich wieder ein DNS-Eintrag auf den Server zeigt, liefert der Webserver nicht automatisch die eigentliche Joomla-Seite aus.&#xA;&#xA;Canonical-Tags helfen, lösen das Problem aber nicht allein&#xA;&#xA;Joomla bzw. SEO-Erweiterungen können sogenannte Canonical-URLs ausgeben.&#xA;&#xA;Beispielsweise:&#xA;&#xA;&lt;link rel=&#34;canonical&#34;&#xA;      href=&#34;https://www.example.org/artikel&#34;  Damit wird Suchmaschinen signalisiert:&#xA;&#xA;  Diese URL ist die bevorzugte Version des Inhalts.&#xA;&#xA;Das ist hilfreich, ersetzt aber keine saubere DNS- und Webserver-Konfiguration.&#xA;&#xA;Wenn hunderte oder tausende alternative Hosts öffentlich dieselben Inhalte ausliefern, sollte die Ursache behoben werden, statt sich nur auf Canonical-Tags zu verlassen.&#xA;&#xA;Warum Google die alten URLs trotzdem noch zeigt&#xA;&#xA;Nachdem der Wildcard-DNS-Eintrag entfernt wurde, verschwinden die betreffenden URLs nicht sofort aus der Google Search Console.&#xA;&#xA;Google kennt diese Adressen weiterhin aus seinem bisherigen Crawl.&#xA;&#xA;Erst wenn Google sie erneut besucht und feststellt, dass sie nicht mehr erreichbar sind oder korrekt umgeleitet werden, kann sich der Index entsprechend ändern.&#xA;&#xA;Das kann deutlich länger dauern als die DNS-Umstellung selbst.&#xA;&#xA;Deshalb sollte man unterscheiden:&#xA;&#xA;DNS&#xA;&#xA;Die Änderung kann – abhängig von der TTL – innerhalb von Stunden wirksam werden.&#xA;&#xA;Google-Index&#xA;&#xA;Bereits bekannte URLs können noch Tage, Wochen oder länger in Berichten auftauchen.&#xA;&#xA;Eine alte URL in der Search Console ist deshalb nicht automatisch ein Hinweis darauf, dass sie aktuell noch funktioniert.&#xA;&#xA;Stichproben nach der Änderung&#xA;&#xA;Nach Ablauf der DNS-TTL lohnt sich ein erneuter Test:&#xA;&#xA;dig zufall-67890.example.org A +short&#xA;&#xA;Erwartetes Ergebnis:&#xA;&#xA;keine Ausgabe&#xA;&#xA;Danach können einige der zuvor von Google gefundenen Hosts geprüft werden.&#xA;&#xA;Beispielsweise:&#xA;&#xA;dig m.example.org A +short&#xA;dig admin-v2.example.org A +short&#xA;dig monitor.example.org A +short&#xA;&#xA;Wenn diese Namen nie benötigt wurden, sollten sie nach der Bereinigung nicht mehr auf den Webserver zeigen.&#xA;&#xA;Wildcard-DNS ist nicht grundsätzlich schlecht&#xA;&#xA;Ein Wildcard-Eintrag ist kein Sicherheitsproblem per se.&#xA;&#xA;Es gibt legitime Anwendungen, beispielsweise:&#xA;&#xA;kunde1.example.org&#xA;kunde2.example.org&#xA;kunde3.example.org&#xA;&#xA;wenn eine Anwendung dynamisch beliebige Subdomains verwendet.&#xA;&#xA;Problematisch wird ein Wildcard-Eintrag vor allem dann, wenn:&#xA;&#xA;er gar nicht benötigt wird,&#xA;niemand mehr weiß, warum er existiert,&#xA;der Webserver jeden beliebigen Host akzeptiert,&#xA;oder ein kompromittiertes System dadurch unter vielen Hostnamen erreichbar wird.&#xA;&#xA;In einer normalen Joomla-Webseite mit wenigen fest definierten Domains gibt es häufig keinen Grund, jede denkbare Subdomain auf dieselbe Installation zeigen zu lassen.&#xA;&#xA;Was der Wildcard-Eintrag nicht beweist&#xA;&#xA;Bei der Untersuchung eines Hacks ist auch hier Vorsicht bei Schlussfolgerungen wichtig.&#xA;&#xA;Ein Eintrag wie:&#xA;&#xA;.example.org&#xA;&#xA;beweist nicht:&#xA;&#xA;  Ein Angreifer hat hunderte Subdomains angelegt.&#xA;&#xA;Tatsächlich wurde möglicherweise keine einzige davon individuell eingerichtet.&#xA;&#xA;Ebenso beweist eine bei Google auftauchende Adresse wie:&#xA;&#xA;admin-v2.example.org&#xA;&#xA;nicht automatisch, dass dort tatsächlich ein Administrationssystem existierte.&#xA;&#xA;Der Name kann vollkommen beliebig sein.&#xA;&#xA;Der entscheidende Test ist deshalb immer:&#xA;&#xA;  Warum löst dieser Hostname überhaupt auf und was liefert der Server bei einem Request dorthin aus?&#xA;&#xA;Kurzfassung&#xA;&#xA;Symptom&#xA;&#xA;Google kennt plötzlich viele unbekannte Subdomains wie:&#xA;&#xA;m.example.org&#xA;admin-v2.example.org&#xA;monitor.example.org&#xA;&#xA;Mögliche Ursache&#xA;&#xA;In der DNS-Zone existiert:&#xA;&#xA;*.example.org&#xA;&#xA;und zeigt auf denselben Webserver wie die eigentliche Webseite.&#xA;&#xA;Zusätzliche Voraussetzung&#xA;&#xA;Der Webserver akzeptiert die unbekannten Hostnamen und liefert dafür ebenfalls die Joomla-Seite aus.&#xA;&#xA;Folge&#xA;&#xA;Beliebig viele Hostnamen können auf dieselben oder auf kompromittierte Joomla-URLs zeigen und von Suchmaschinen entdeckt werden.&#xA;&#xA;Lösung&#xA;&#xA;Wenn Wildcard-Subdomains nicht benötigt werden:&#xA;&#xA;Wildcard-DNS-Eintrag entfernen.&#xA;DNS-TTL abwarten.&#xA;Zufällige Subdomains mit dig testen.&#xA;Webserver so konfigurieren, dass nur vorgesehene Hostnamen akzeptiert werden.&#xA;Canonical-URLs kontrollieren.&#xA;Google die alten URLs anschließend neu crawlen lassen.&#xA;&#xA;Fazit&#xA;&#xA;Der Wildcard-DNS-Eintrag war in meinem Fall nicht die Ursache der Joomla-Kompromittierung.&#xA;&#xA;Er erklärte aber, warum völlig frei erfundene Subdomains dieselbe Joomla-Webseite auslieferten und warum Google ungewöhnlich viele Hostvarianten kennen konnte.&#xA;&#xA;Die wichtigste Erkenntnis war deshalb:&#xA;&#xA;  Bei merkwürdigen Subdomains nach einem Website-Hack nicht automatisch davon ausgehen, dass diese einzeln angelegt wurden. Zuerst DNS-Wildcards und die Host-Konfiguration des Webservers prüfen.&#xA;&#xA;Gerade die Kombination aus kompromittierten URLs, Wildcard-DNS und einem Webserver, der unbekannte Hostnamen akzeptiert, kann einen einzelnen Vorfall im Suchmaschinenindex deutlich größer erscheinen lassen, als er technisch tatsächlich ist.]]&gt;</description>
      <content:encoded><![CDATA[<p>Nach einem Joomla-Sicherheitsvorfall tauchten in der Google Search Console plötzlich zahlreiche URLs unter Subdomains auf, die ich selbst nie eingerichtet hatte.</p>

<p>Beispiele sahen sinngemäß so aus:</p>

<pre><code class="language-text">m.example.org/...
admin-v2.example.org/...
monitor.example.org/...
wildcard.example.org/...
</code></pre>

<p>Das wirkte zunächst so, als hätte jemand diese Subdomains auf dem Server angelegt.</p>

<p>Die eigentliche Ursache war aber eine Kombination aus zwei Dingen:</p>
<ol><li>Die Joomla-Seite war zuvor kompromittiert worden und Google hatte dabei ungewöhnliche bzw. manipulierte URLs entdeckt.</li>
<li>Für die Domain existierte ein Wildcard-DNS-Eintrag, durch den praktisch jede beliebige Subdomain auf denselben Webserver zeigte.</li></ol>

<p>Der Wildcard-DNS-Eintrag war also nicht die Ursache des Hacks. Er wirkte aber wie ein Verstärker für die unerwünschte Indexierung.</p>

<h2 id="was-ist-ein-wildcard-dns-eintrag">Was ist ein Wildcard-DNS-Eintrag?</h2>

<p>Ein Eintrag wie</p>

<pre><code class="language-text">*.example.org   A   203.0.113.10
</code></pre>

<p>bedeutet:</p>

<blockquote><p>Jede nicht ausdrücklich definierte Subdomain von <code>example.org</code> soll auf diese IP-Adresse zeigen.</p></blockquote>

<p>Damit funktionieren nicht nur bewusst angelegte Hosts wie:</p>

<pre><code class="language-text">www.example.org
mail.example.org
</code></pre>

<p>sondern grundsätzlich auch:</p>

<pre><code class="language-text">test.example.org
abc.example.org
m.example.org
admin-v2.example.org
monitor.example.org
irgendwas.example.org
</code></pre>

<p>sofern dafür kein spezifischerer DNS-Eintrag existiert.</p>

<p>Ein Test ist einfach:</p>

<pre><code class="language-bash">dig zufallsname-12345.example.org A +short
</code></pre>

<p>Wenn dort dieselbe Server-IP zurückkommt, obwohl diese Subdomain nie eingerichtet wurde, ist sehr wahrscheinlich ein Wildcard-DNS-Eintrag aktiv.</p>

<h2 id="dns-allein-reicht-noch-nicht">DNS allein reicht noch nicht</h2>

<p>Ein wichtiger Punkt:</p>

<p>Der Wildcard-DNS-Eintrag allein sorgt noch nicht dafür, dass eine Webseite unter jeder Subdomain erscheint.</p>

<p>DNS beantwortet zunächst nur die Frage:</p>

<blockquote><p>Auf welche IP-Adresse soll dieser Hostname zeigen?</p></blockquote>

<p>Danach entscheidet der Webserver, was mit dem HTTP-Request passiert.</p>

<p>Wenn Apache, nginx oder die Hosting-Konfiguration unbekannte Hostnamen einfach an dieselbe Joomla-Installation weiterleitet, kann die Webseite tatsächlich auch unter beliebigen Subdomains erreichbar sein.</p>

<p>Dann wird aus:</p>

<pre><code class="language-text">https://www.example.org/artikel
</code></pre>

<p>möglicherweise ebenfalls:</p>

<pre><code class="language-text">https://m.example.org/artikel
https://monitor.example.org/artikel
https://abc.example.org/artikel
</code></pre>

<p>Wenn diese Varianten öffentlich erreichbar sind, können auch Suchmaschinen sie entdecken.</p>

<h2 id="der-zusammenhang-mit-einem-joomla-hack">Der Zusammenhang mit einem Joomla-Hack</h2>

<p>In meinem Fall war zuvor eine Joomla-Erweiterung kompromittiert worden.</p>

<p>Dabei waren unter anderem ungewöhnliche URLs entstanden, die von Google gecrawlt wurden.</p>

<p>Eine davon verwies beispielsweise auf eine hochgeladene PHP-Datei innerhalb einer Joomla-Komponente.</p>

<p>Google kann solche URLs unter anderem über externe Links, Crawling, Sitemaps, interne Verweise oder bereits bekannte URL-Muster entdecken.</p>

<p>Wenn gleichzeitig jede beliebige Subdomain auf dieselbe Joomla-Seite zeigt, vervielfacht sich die Zahl potentiell erreichbarer URL-Varianten.</p>

<p>Aus einer URL kann dann theoretisch eine ganze Familie werden:</p>

<pre><code class="language-text">example.org/pfad
m.example.org/pfad
admin-v2.example.org/pfad
monitor.example.org/pfad
foo.example.org/pfad
bar.example.org/pfad
</code></pre>

<p>Das bedeutet nicht zwangsläufig, dass ein Angreifer jede dieser Subdomains bewusst eingerichtet hat.</p>

<p>Sie können schlicht deshalb funktionieren, weil:</p>

<pre><code class="language-text">*.example.org
</code></pre>

<p>auf denselben Server zeigt.</p>

<h2 id="so-habe-ich-den-wildcard-eintrag-gefunden">So habe ich den Wildcard-Eintrag gefunden</h2>

<p>Zunächst wurde die öffentliche DNS-Konfiguration überprüft.</p>

<p>Ein Test mit einer frei erfundenen Subdomain ist dafür besonders hilfreich:</p>

<pre><code class="language-bash">dig diesegibtesgarantiertnicht.example.org A
</code></pre>

<p>Wenn die Antwort beispielsweise lautet:</p>

<pre><code class="language-text">diesegibtesgarantiertnicht.example.org. 86400 IN A 203.0.113.10
</code></pre>

<p>ist klar:</p>

<p>Auch nicht eingerichtete Subdomains werden aufgelöst.</p>

<p>Anschließend sollte man die DNS-Zone beim tatsächlichen DNS-Provider prüfen.</p>

<p>Gesucht wird nach einem Eintrag wie:</p>

<pre><code class="language-text">*
</code></pre>

<p>bzw.</p>

<pre><code class="language-text">*.example.org
</code></pre>

<p>mit einem A- oder AAAA-Record.</p>

<h2 id="zusätzlich-prüfen-ob-der-webserver-die-subdomain-annimmt">Zusätzlich prüfen, ob der Webserver die Subdomain annimmt</h2>

<p>Nach der DNS-Prüfung sollte getestet werden, was der Webserver mit einem erfundenen Hostnamen macht.</p>

<p>Zum Beispiel:</p>

<pre><code class="language-bash">curl -I https://zufall-12345.example.org/
</code></pre>

<p>Wenn die Seite dort normal mit:</p>

<pre><code class="language-text">HTTP/2 200
</code></pre>

<p>antwortet, wird der unbekannte Hostname tatsächlich von der Webseite bedient.</p>

<p>Für einen gezielten Test gegen eine bestimmte Server-IP kann <code>curl</code> auch ohne Änderung des öffentlichen DNS verwendet werden:</p>

<pre><code class="language-bash">curl --resolve zufall-12345.example.org:443:203.0.113.10 \
  -I https://zufall-12345.example.org/
</code></pre>

<p>Damit wird für diesen einzelnen Test so getan, als würde der Name auf die angegebene IP zeigen.</p>

<h2 id="wildcard-dns-entfernen">Wildcard-DNS entfernen</h2>

<p>Wenn beliebige Subdomains nicht benötigt werden, kann der Wildcard-Eintrag entfernt werden.</p>

<p>Danach sollte ein Test wie:</p>

<pre><code class="language-bash">dig zufall-12345.example.org A +short
</code></pre>

<p>keine Server-IP mehr zurückgeben.</p>

<p>Wichtig ist dabei die DNS-TTL.</p>

<p>Bei einem Eintrag mit beispielsweise:</p>

<pre><code class="language-text">86400
</code></pre>

<p>beträgt die maximale Cache-Zeit:</p>

<pre><code class="language-text">86400 Sekunden = 24 Stunden
</code></pre>

<p>Ein bereits aufgelöster Wildcard-Eintrag kann deshalb noch eine Zeit lang in DNS-Caches vorhanden sein.</p>

<p>Das bedeutet nicht, dass die Änderung nicht funktioniert hat.</p>

<h2 id="dns-entfernen-reicht-langfristig-nicht-immer-aus">DNS entfernen reicht langfristig nicht immer aus</h2>

<p>Zusätzlich zur DNS-Bereinigung ist es sinnvoll, den Webserver so zu konfigurieren, dass unbekannte Hostnamen nicht einfach dieselbe Joomla-Seite ausliefern.</p>

<p>Ein Server sollte idealerweise nur die ausdrücklich vorgesehenen Hosts akzeptieren, zum Beispiel:</p>

<pre><code class="language-text">example.org
www.example.org
</code></pre>

<p>Unbekannte Hosts können stattdessen:</p>
<ul><li>abgewiesen werden,</li>
<li>einen Fehlerstatus liefern,</li>
<li>oder kontrolliert auf die kanonische Domain umgeleitet werden.</li></ul>

<p>Welche Variante sinnvoll ist, hängt von der jeweiligen Server- und Hosting-Konfiguration ab.</p>

<p>Der Vorteil:</p>

<p>Selbst wenn später versehentlich wieder ein DNS-Eintrag auf den Server zeigt, liefert der Webserver nicht automatisch die eigentliche Joomla-Seite aus.</p>

<h2 id="canonical-tags-helfen-lösen-das-problem-aber-nicht-allein">Canonical-Tags helfen, lösen das Problem aber nicht allein</h2>

<p>Joomla bzw. SEO-Erweiterungen können sogenannte Canonical-URLs ausgeben.</p>

<p>Beispielsweise:</p>

<pre><code class="language-html">&lt;link rel=&#34;canonical&#34;
      href=&#34;https://www.example.org/artikel&#34;&gt;
</code></pre>

<p>Damit wird Suchmaschinen signalisiert:</p>

<blockquote><p>Diese URL ist die bevorzugte Version des Inhalts.</p></blockquote>

<p>Das ist hilfreich, ersetzt aber keine saubere DNS- und Webserver-Konfiguration.</p>

<p>Wenn hunderte oder tausende alternative Hosts öffentlich dieselben Inhalte ausliefern, sollte die Ursache behoben werden, statt sich nur auf Canonical-Tags zu verlassen.</p>

<h2 id="warum-google-die-alten-urls-trotzdem-noch-zeigt">Warum Google die alten URLs trotzdem noch zeigt</h2>

<p>Nachdem der Wildcard-DNS-Eintrag entfernt wurde, verschwinden die betreffenden URLs nicht sofort aus der Google Search Console.</p>

<p>Google kennt diese Adressen weiterhin aus seinem bisherigen Crawl.</p>

<p>Erst wenn Google sie erneut besucht und feststellt, dass sie nicht mehr erreichbar sind oder korrekt umgeleitet werden, kann sich der Index entsprechend ändern.</p>

<p>Das kann deutlich länger dauern als die DNS-Umstellung selbst.</p>

<p>Deshalb sollte man unterscheiden:</p>

<h3 id="dns">DNS</h3>

<p>Die Änderung kann – abhängig von der TTL – innerhalb von Stunden wirksam werden.</p>

<h3 id="google-index">Google-Index</h3>

<p>Bereits bekannte URLs können noch Tage, Wochen oder länger in Berichten auftauchen.</p>

<p>Eine alte URL in der Search Console ist deshalb nicht automatisch ein Hinweis darauf, dass sie aktuell noch funktioniert.</p>

<h2 id="stichproben-nach-der-änderung">Stichproben nach der Änderung</h2>

<p>Nach Ablauf der DNS-TTL lohnt sich ein erneuter Test:</p>

<pre><code class="language-bash">dig zufall-67890.example.org A +short
</code></pre>

<p>Erwartetes Ergebnis:</p>

<pre><code class="language-text">keine Ausgabe
</code></pre>

<p>Danach können einige der zuvor von Google gefundenen Hosts geprüft werden.</p>

<p>Beispielsweise:</p>

<pre><code class="language-bash">dig m.example.org A +short
dig admin-v2.example.org A +short
dig monitor.example.org A +short
</code></pre>

<p>Wenn diese Namen nie benötigt wurden, sollten sie nach der Bereinigung nicht mehr auf den Webserver zeigen.</p>

<h2 id="wildcard-dns-ist-nicht-grundsätzlich-schlecht">Wildcard-DNS ist nicht grundsätzlich schlecht</h2>

<p>Ein Wildcard-Eintrag ist kein Sicherheitsproblem per se.</p>

<p>Es gibt legitime Anwendungen, beispielsweise:</p>

<pre><code class="language-text">kunde1.example.org
kunde2.example.org
kunde3.example.org
</code></pre>

<p>wenn eine Anwendung dynamisch beliebige Subdomains verwendet.</p>

<p>Problematisch wird ein Wildcard-Eintrag vor allem dann, wenn:</p>
<ul><li>er gar nicht benötigt wird,</li>
<li>niemand mehr weiß, warum er existiert,</li>
<li>der Webserver jeden beliebigen Host akzeptiert,</li>
<li>oder ein kompromittiertes System dadurch unter vielen Hostnamen erreichbar wird.</li></ul>

<p>In einer normalen Joomla-Webseite mit wenigen fest definierten Domains gibt es häufig keinen Grund, jede denkbare Subdomain auf dieselbe Installation zeigen zu lassen.</p>

<h2 id="was-der-wildcard-eintrag-nicht-beweist">Was der Wildcard-Eintrag nicht beweist</h2>

<p>Bei der Untersuchung eines Hacks ist auch hier Vorsicht bei Schlussfolgerungen wichtig.</p>

<p>Ein Eintrag wie:</p>

<pre><code class="language-text">*.example.org
</code></pre>

<p>beweist nicht:</p>

<blockquote><p>Ein Angreifer hat hunderte Subdomains angelegt.</p></blockquote>

<p>Tatsächlich wurde möglicherweise keine einzige davon individuell eingerichtet.</p>

<p>Ebenso beweist eine bei Google auftauchende Adresse wie:</p>

<pre><code class="language-text">admin-v2.example.org
</code></pre>

<p>nicht automatisch, dass dort tatsächlich ein Administrationssystem existierte.</p>

<p>Der Name kann vollkommen beliebig sein.</p>

<p>Der entscheidende Test ist deshalb immer:</p>

<blockquote><p>Warum löst dieser Hostname überhaupt auf und was liefert der Server bei einem Request dorthin aus?</p></blockquote>

<h2 id="kurzfassung">Kurzfassung</h2>

<p><strong>Symptom</strong></p>

<p>Google kennt plötzlich viele unbekannte Subdomains wie:</p>

<pre><code class="language-text">m.example.org
admin-v2.example.org
monitor.example.org
</code></pre>

<p><strong>Mögliche Ursache</strong></p>

<p>In der DNS-Zone existiert:</p>

<pre><code class="language-text">*.example.org
</code></pre>

<p>und zeigt auf denselben Webserver wie die eigentliche Webseite.</p>

<p><strong>Zusätzliche Voraussetzung</strong></p>

<p>Der Webserver akzeptiert die unbekannten Hostnamen und liefert dafür ebenfalls die Joomla-Seite aus.</p>

<p><strong>Folge</strong></p>

<p>Beliebig viele Hostnamen können auf dieselben oder auf kompromittierte Joomla-URLs zeigen und von Suchmaschinen entdeckt werden.</p>

<p><strong>Lösung</strong></p>

<p>Wenn Wildcard-Subdomains nicht benötigt werden:</p>
<ol><li>Wildcard-DNS-Eintrag entfernen.</li>
<li>DNS-TTL abwarten.</li>
<li>Zufällige Subdomains mit <code>dig</code> testen.</li>
<li>Webserver so konfigurieren, dass nur vorgesehene Hostnamen akzeptiert werden.</li>
<li>Canonical-URLs kontrollieren.</li>
<li>Google die alten URLs anschließend neu crawlen lassen.</li></ol>

<h2 id="fazit">Fazit</h2>

<p>Der Wildcard-DNS-Eintrag war in meinem Fall nicht die Ursache der Joomla-Kompromittierung.</p>

<p>Er erklärte aber, warum völlig frei erfundene Subdomains dieselbe Joomla-Webseite auslieferten und warum Google ungewöhnlich viele Hostvarianten kennen konnte.</p>

<p>Die wichtigste Erkenntnis war deshalb:</p>

<blockquote><p>Bei merkwürdigen Subdomains nach einem Website-Hack nicht automatisch davon ausgehen, dass diese einzeln angelegt wurden. Zuerst DNS-Wildcards und die Host-Konfiguration des Webservers prüfen.</p></blockquote>

<p>Gerade die Kombination aus kompromittierten URLs, Wildcard-DNS und einem Webserver, der unbekannte Hostnamen akzeptiert, kann einen einzelnen Vorfall im Suchmaschinenindex deutlich größer erscheinen lassen, als er technisch tatsächlich ist.</p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-gehackt-warum-google-ploetzlich-fremde-subdomains-wie-m-admin-v2</guid>
      <pubDate>Wed, 16 Sep 2026 13:04:46 +0000</pubDate>
    </item>
    <item>
      <title>RSFiles!: Fehlende Datei erzeugt HTTP 500 statt 404 – kleiner Joomla-Patch</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/rsfiles-fehlende-datei-erzeugt-http-500-statt-404-kleiner-joomla-patch</link>
      <description>&lt;![CDATA[Bei einer Joomla-Installation mit RSFiles! fiel nach einer Bereinigung auf, dass Links auf nicht mehr vorhandene Dateien keinen normalen HTTP-404-Fehler zurückgaben.&#xA;&#xA;Stattdessen antwortete die Seite mit:&#xA;&#xA;HTTP 500 Internal Server Error&#xA;&#xA;Das ist ungünstig, weil eine nicht vorhandene Ressource semantisch kein Serverfehler ist. Für Browser, Suchmaschinen und Monitoring sollte eine nicht mehr existierende Datei mit HTTP 404 Not Found beantwortet werden.&#xA;&#xA;In meinem Fall ließ sich das mit einem sehr kleinen Patch beheben.&#xA;&#xA;Betroffene Datei&#xA;&#xA;Der Fehler trat in folgender RSFiles-Datei auf:&#xA;&#xA;components/comrsfiles/views/rsfiles/view.html.php&#xA;&#xA;Dort wird zunächst versucht, die angeforderte Datei zu laden:&#xA;&#xA;$this-  file = $this-  get(&#39;File&#39;);&#xA;&#xA;Wenn weder ein passender Datenbankeintrag noch eine physische Datei vorhanden war, lieferte das Model false zurück.&#xA;&#xA;Die Verarbeitung lief anschließend jedoch weiter.&#xA;&#xA;Später landete der Fehler in der allgemeinen RSFiles-Fehlerbehandlung, die in dieser Konstellation eine Exception mit HTTP-Status 500 erzeugte.&#xA;&#xA;Damit wurde aus einer simplen „Datei existiert nicht“-Situation ein interner Serverfehler.&#xA;&#xA;Der Patch&#xA;&#xA;Direkt nach:&#xA;&#xA;$this-  file = $this-  get(&#39;File&#39;);&#xA;&#xA;habe ich folgende Prüfung eingefügt:&#xA;&#xA;// Return a proper 404 for missing files&#xA;if (empty($this-  file)) {&#xA;    throw new Exception(Text::(&#39;COMRSFILESCANNOTFINDFILE&#39;), 404);&#xA;}&#xA;&#xA;Damit wird eine nicht vorhandene RSFiles-Ressource unmittelbar als HTTP 404 behandelt.&#xA;&#xA;Vorher sichern&#xA;&#xA;Vor Änderungen an Erweiterungsdateien sollte die Originaldatei gesichert werden.&#xA;&#xA;Zum Beispiel:&#xA;&#xA;cp -a \&#xA;components/comrsfiles/views/rsfiles/view.html.php \&#xA;/root/rsfiles-view.html.php-before-404&#xA;&#xA;Anschließend kann der Patch in die Live-Datei eingefügt werden.&#xA;&#xA;PHP-Syntax prüfen&#xA;&#xA;Nach der Änderung sollte die Datei unbedingt auf Syntaxfehler geprüft werden.&#xA;&#xA;In meinem Setup mit Plesk und PHP 8.2:&#xA;&#xA;/opt/plesk/php/8.2/bin/php -l \&#xA;components/comrsfiles/views/rsfiles/view.html.php&#xA;&#xA;Erwartetes Ergebnis:&#xA;&#xA;No syntax errors detected&#xA;&#xA;Test&#xA;&#xA;Anschließend habe ich bewusst nicht vorhandene RSFiles-Pfade aufgerufen.&#xA;&#xA;Vor dem Patch:&#xA;&#xA;HTTP 500&#xA;&#xA;Nach dem Patch:&#xA;&#xA;HTTP 404&#xA;&#xA;Unter anderem wurden ein frei erfundener Dateiname sowie eine zuvor entfernte verdächtige Datei getestet.&#xA;&#xA;Die Joomla-Startseite lieferte gleichzeitig weiterhin:&#xA;&#xA;HTTP 200&#xA;&#xA;Damit war sichergestellt, dass nicht versehentlich eine allgemeine Fehlerbehandlung beschädigt worden war.&#xA;&#xA;Warum das relevant ist&#xA;&#xA;Der Unterschied zwischen 404 und 500 ist nicht nur kosmetisch.&#xA;&#xA;HTTP 404&#xA;&#xA;bedeutet:&#xA;&#xA;  Die angeforderte Ressource existiert nicht.&#xA;&#xA;Das ist bei einer gelöschten oder unbekannten Datei korrekt.&#xA;&#xA;HTTP 500&#xA;&#xA;bedeutet:&#xA;&#xA;  Bei der Verarbeitung auf dem Server ist ein interner Fehler aufgetreten.&#xA;&#xA;Das suggeriert einen Defekt der Anwendung oder des Servers.&#xA;&#xA;Gerade nach einem Sicherheitsvorfall kann das relevant sein, wenn Suchmaschinen noch URLs zu inzwischen entfernten Dateien kennen.&#xA;&#xA;Ein sauberer HTTP-404-Status signalisiert dann:&#xA;&#xA;  Diese Ressource ist nicht mehr vorhanden.&#xA;&#xA;Wichtig: Updatefest ist der Patch nicht&#xA;&#xA;Die Änderung liegt direkt in einer RSFiles-Komponentendatei:&#xA;&#xA;components/comrsfiles/views/rsfiles/view.html.php&#xA;&#xA;Ein späteres RSFiles-Update kann diese Datei überschreiben.&#xA;&#xA;Deshalb sollte der Patch dokumentiert und nach Updates kontrolliert werden.&#xA;&#xA;Idealerweise sollte ein solcher Fall natürlich direkt in der Erweiterung selbst korrekt behandelt werden. Ein lokaler Patch ist vor allem dann sinnvoll, wenn kurzfristig ein sauberes HTTP-Verhalten benötigt wird.&#xA;&#xA;Kurzfassung&#xA;&#xA;Problem&#xA;&#xA;Nicht vorhandene RSFiles-Dateien erzeugten:&#xA;&#xA;HTTP 500&#xA;&#xA;Ursache&#xA;&#xA;Das RSFiles-Model lieferte bei einer fehlenden Datei false, die View behandelte diesen Zustand aber nicht unmittelbar als „nicht gefunden“.&#xA;&#xA;Patch&#xA;&#xA;Direkt nach:&#xA;&#xA;$this-  file = $this-  get(&#39;File&#39;);&#xA;&#xA;einfügen:&#xA;&#xA;if (empty($this-  file)) {&#xA;    throw new Exception(Text::(&#39;COMRSFILESCANNOTFINDFILE&#39;), 404);&#xA;}&#xA;&#xA;Ergebnis&#xA;&#xA;Fehlende Dateien liefern anschließend korrekt:&#xA;&#xA;HTTP 404&#xA;&#xA;statt:&#xA;&#xA;HTTP 500&#xA;&#xA;---&#xA;&#xA;Hinweis: Der Patch wurde an einer konkreten RSFiles-Installation getestet. Vor Änderungen sollte die betroffene Datei gesichert und nach jedem RSFiles-Update geprüft werden, ob die Anpassung noch notwendig ist.]]&gt;</description>
      <content:encoded><![CDATA[<p>Bei einer Joomla-Installation mit RSFiles! fiel nach einer Bereinigung auf, dass Links auf nicht mehr vorhandene Dateien keinen normalen HTTP-404-Fehler zurückgaben.</p>

<p>Stattdessen antwortete die Seite mit:</p>

<pre><code class="language-text">HTTP 500 Internal Server Error
</code></pre>

<p>Das ist ungünstig, weil eine nicht vorhandene Ressource semantisch kein Serverfehler ist. Für Browser, Suchmaschinen und Monitoring sollte eine nicht mehr existierende Datei mit <strong>HTTP 404 Not Found</strong> beantwortet werden.</p>

<p>In meinem Fall ließ sich das mit einem sehr kleinen Patch beheben.</p>

<h2 id="betroffene-datei">Betroffene Datei</h2>

<p>Der Fehler trat in folgender RSFiles-Datei auf:</p>

<pre><code class="language-text">components/com_rsfiles/views/rsfiles/view.html.php
</code></pre>

<p>Dort wird zunächst versucht, die angeforderte Datei zu laden:</p>

<pre><code class="language-php">$this-&gt;file = $this-&gt;get(&#39;File&#39;);
</code></pre>

<p>Wenn weder ein passender Datenbankeintrag noch eine physische Datei vorhanden war, lieferte das Model <code>false</code> zurück.</p>

<p>Die Verarbeitung lief anschließend jedoch weiter.</p>

<p>Später landete der Fehler in der allgemeinen RSFiles-Fehlerbehandlung, die in dieser Konstellation eine Exception mit HTTP-Status 500 erzeugte.</p>

<p>Damit wurde aus einer simplen „Datei existiert nicht“-Situation ein interner Serverfehler.</p>

<h2 id="der-patch">Der Patch</h2>

<p>Direkt nach:</p>

<pre><code class="language-php">$this-&gt;file = $this-&gt;get(&#39;File&#39;);
</code></pre>

<p>habe ich folgende Prüfung eingefügt:</p>

<pre><code class="language-php">// Return a proper 404 for missing files
if (empty($this-&gt;file)) {
    throw new Exception(Text::_(&#39;COM_RSFILES_CANNOT_FIND_FILE&#39;), 404);
}
</code></pre>

<p>Damit wird eine nicht vorhandene RSFiles-Ressource unmittelbar als HTTP 404 behandelt.</p>

<h2 id="vorher-sichern">Vorher sichern</h2>

<p>Vor Änderungen an Erweiterungsdateien sollte die Originaldatei gesichert werden.</p>

<p>Zum Beispiel:</p>

<pre><code class="language-bash">cp -a \
components/com_rsfiles/views/rsfiles/view.html.php \
/root/rsfiles-view.html.php-before-404
</code></pre>

<p>Anschließend kann der Patch in die Live-Datei eingefügt werden.</p>

<h2 id="php-syntax-prüfen">PHP-Syntax prüfen</h2>

<p>Nach der Änderung sollte die Datei unbedingt auf Syntaxfehler geprüft werden.</p>

<p>In meinem Setup mit Plesk und PHP 8.2:</p>

<pre><code class="language-bash">/opt/plesk/php/8.2/bin/php -l \
components/com_rsfiles/views/rsfiles/view.html.php
</code></pre>

<p>Erwartetes Ergebnis:</p>

<pre><code class="language-text">No syntax errors detected
</code></pre>

<h2 id="test">Test</h2>

<p>Anschließend habe ich bewusst nicht vorhandene RSFiles-Pfade aufgerufen.</p>

<p>Vor dem Patch:</p>

<pre><code class="language-text">HTTP 500
</code></pre>

<p>Nach dem Patch:</p>

<pre><code class="language-text">HTTP 404
</code></pre>

<p>Unter anderem wurden ein frei erfundener Dateiname sowie eine zuvor entfernte verdächtige Datei getestet.</p>

<p>Die Joomla-Startseite lieferte gleichzeitig weiterhin:</p>

<pre><code class="language-text">HTTP 200
</code></pre>

<p>Damit war sichergestellt, dass nicht versehentlich eine allgemeine Fehlerbehandlung beschädigt worden war.</p>

<h2 id="warum-das-relevant-ist">Warum das relevant ist</h2>

<p>Der Unterschied zwischen 404 und 500 ist nicht nur kosmetisch.</p>

<h3 id="http-404">HTTP 404</h3>

<p>bedeutet:</p>

<blockquote><p>Die angeforderte Ressource existiert nicht.</p></blockquote>

<p>Das ist bei einer gelöschten oder unbekannten Datei korrekt.</p>

<h3 id="http-500">HTTP 500</h3>

<p>bedeutet:</p>

<blockquote><p>Bei der Verarbeitung auf dem Server ist ein interner Fehler aufgetreten.</p></blockquote>

<p>Das suggeriert einen Defekt der Anwendung oder des Servers.</p>

<p>Gerade nach einem Sicherheitsvorfall kann das relevant sein, wenn Suchmaschinen noch URLs zu inzwischen entfernten Dateien kennen.</p>

<p>Ein sauberer HTTP-404-Status signalisiert dann:</p>

<blockquote><p>Diese Ressource ist nicht mehr vorhanden.</p></blockquote>

<h2 id="wichtig-updatefest-ist-der-patch-nicht">Wichtig: Updatefest ist der Patch nicht</h2>

<p>Die Änderung liegt direkt in einer RSFiles-Komponentendatei:</p>

<pre><code class="language-text">components/com_rsfiles/views/rsfiles/view.html.php
</code></pre>

<p>Ein späteres RSFiles-Update kann diese Datei überschreiben.</p>

<p>Deshalb sollte der Patch dokumentiert und nach Updates kontrolliert werden.</p>

<p>Idealerweise sollte ein solcher Fall natürlich direkt in der Erweiterung selbst korrekt behandelt werden. Ein lokaler Patch ist vor allem dann sinnvoll, wenn kurzfristig ein sauberes HTTP-Verhalten benötigt wird.</p>

<h2 id="kurzfassung">Kurzfassung</h2>

<p><strong>Problem</strong></p>

<p>Nicht vorhandene RSFiles-Dateien erzeugten:</p>

<pre><code class="language-text">HTTP 500
</code></pre>

<p><strong>Ursache</strong></p>

<p>Das RSFiles-Model lieferte bei einer fehlenden Datei <code>false</code>, die View behandelte diesen Zustand aber nicht unmittelbar als „nicht gefunden“.</p>

<p><strong>Patch</strong></p>

<p>Direkt nach:</p>

<pre><code class="language-php">$this-&gt;file = $this-&gt;get(&#39;File&#39;);
</code></pre>

<p>einfügen:</p>

<pre><code class="language-php">if (empty($this-&gt;file)) {
    throw new Exception(Text::_(&#39;COM_RSFILES_CANNOT_FIND_FILE&#39;), 404);
}
</code></pre>

<p><strong>Ergebnis</strong></p>

<p>Fehlende Dateien liefern anschließend korrekt:</p>

<pre><code class="language-text">HTTP 404
</code></pre>

<p>statt:</p>

<pre><code class="language-text">HTTP 500
</code></pre>

<hr>

<p><em>Hinweis: Der Patch wurde an einer konkreten RSFiles-Installation getestet. Vor Änderungen sollte die betroffene Datei gesichert und nach jedem RSFiles-Update geprüft werden, ob die Anpassung noch notwendig ist.</em></p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/rsfiles-fehlende-datei-erzeugt-http-500-statt-404-kleiner-joomla-patch</guid>
      <pubDate>Wed, 16 Sep 2026 13:00:26 +0000</pubDate>
    </item>
    <item>
      <title>Joomla / RSFiles!: Unauthenticated File Upload – Webshell-Befall erkennen und bereinigen</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-rsfiles-unauthenticated-file-upload-webshell-befall-erkennen-und</link>
      <description>&lt;![CDATA[Im Sommer 2026 wurde für die Joomla-Erweiterung RSFiles! eine kritische Sicherheitslücke bekannt, über die nicht angemeldete Besucher Dateien hochladen konnten. Besonders problematisch: Dadurch konnten auch PHP-Dateien in den Downloadbereich gelangen und anschließend über den Webserver ausgeführt werden.&#xA;&#xA;In einer von mir administrierten Joomla-Installation war diese Schwachstelle tatsächlich ausgenutzt worden.&#xA;&#xA;Dieser Beitrag dokumentiert die wichtigsten Spuren, die Bereinigung und einige Erkenntnisse aus dem Vorfall.&#xA;&#xA;Die Sicherheitslücke&#xA;&#xA;RSJoomla veröffentlichte am 10. Juli 2026 RSFiles! 1.17.12 und schloss damit eine Schwachstelle im Frontend-Upload.&#xA;&#xA;Nach Angaben des Herstellers konnte die fehlerhafte Upload-Funktion von nicht angemeldeten Besuchern verwendet werden und erlaubte dabei auch Dateitypen, die dort niemals hätten landen dürfen – einschließlich PHP-Dateien.&#xA;&#xA;Die Schwachstelle wurde mit RSFiles! 1.17.12 behoben.&#xA;&#xA;Wer eine ältere Version eingesetzt hat, sollte deshalb nicht nur aktualisieren, sondern auch prüfen, ob die Lücke bereits ausgenutzt wurde.&#xA;&#xA;Das erste sichtbare Symptom&#xA;&#xA;Die Webseite selbst wirkte zunächst weitgehend normal.&#xA;&#xA;Auffällig wurde der Vorfall unter anderem durch ungewöhnliche, von Google indexierte URLs. Eine davon sah beispielsweise so aus:&#xA;&#xA;/component/rsfiles/details?path=Nx164.php&amp;Itemid=...&#xA;&#xA;Eine PHP-Datei mit einem Namen wie Nx164.php hat in einem normalen Downloadbestand nichts verloren.&#xA;&#xA;Bei der anschließenden Untersuchung fanden sich tatsächlich zahlreiche verdächtige PHP-Dateien im RSFiles-Downloadbereich.&#xA;&#xA;Gefundene Dateien&#xA;&#xA;Unter den Funden waren unter anderem Namen wie:&#xA;&#xA;Nx.php&#xA;shell.php&#xA;shell.PHP&#xA;sh.phtml&#xA;se.phar&#xA;se.inc&#xA;rsfilescache.php&#xA;bobipob4.php&#xA;bobipob4.php5&#xA;bobipob4.php7&#xA;&#xA;Daneben existierten weitere PHP-, PHTML- und PHAR-Dateien mit zufällig wirkenden Namen.&#xA;&#xA;Wichtig: Ein verdächtiger Dateiname allein beweist noch nicht, was eine Datei tut.&#xA;&#xA;In diesem Fall zeigte die Inhaltsanalyse jedoch eindeutig Funktionen, die in einem normalen Downloadverzeichnis nichts zu suchen haben, darunter Datei-Uploads, Base64-Verarbeitung, PHP-Informationen und Möglichkeiten zur Ausführung von PHP-Funktionen.&#xA;&#xA;Damit war klar: Es handelte sich nicht nur um erfolglose Angriffsversuche. Auf dem Webspace lagen tatsächlich Webshells.&#xA;&#xA;Was ein solcher Fund bedeutet – und was nicht&#xA;&#xA;Eine ausführbare Webshell unter dem Benutzer des Webservers ist ein ernstzunehmender Sicherheitsvorfall.&#xA;&#xA;Sie bedeutet in diesem Fall:&#xA;&#xA;  Fremder PHP-Code konnte mit den Rechten des Joomla-/Webspace-Benutzers ausgeführt werden.&#xA;&#xA;Das ist allerdings nicht automatisch gleichbedeutend mit:&#xA;&#xA;  Der gesamte Server wurde als root kompromittiert.&#xA;&#xA;Diese Unterscheidung ist wichtig.&#xA;&#xA;Ein PHP-Prozess kann beispielsweise:&#xA;&#xA;Joomla-Dateien lesen und verändern,&#xA;configuration.php und damit Datenbankzugangsdaten lesen,&#xA;Dateien im Webspace anlegen,&#xA;weitere Schadsoftware nachladen,&#xA;erreichbare Dienste ansprechen.&#xA;&#xA;Ob darüber hinaus Root-Rechte erlangt wurden, muss separat untersucht werden.&#xA;&#xA;In meinem Fall gab es Belege für eine Kompromittierung des Webspace-Benutzers, aber keinen belastbaren Nachweis einer erfolgreichen Root-Kompromittierung.&#xA;&#xA;Die Access-Logs waren besonders hilfreich&#xA;&#xA;In den Webserver-Logs ließen sich Upload-Versuche nachvollziehen.&#xA;&#xA;Auffällig waren unter anderem Requests auf:&#xA;&#xA;index.php?option=comrsfiles&amp;task=upload&#xA;&#xA;bzw.&#xA;&#xA;index.php?option=comrsfiles&amp;task=rsfiles.upload&#xA;&#xA;Der Hersteller selbst empfiehlt nach Bekanntwerden der Schwachstelle, insbesondere nach entsprechenden POST-Requests zu suchen.&#xA;&#xA;Eine einfache Suche kann beispielsweise so aussehen:&#xA;&#xA;grep -Rni \&#xA;&#39;option=comrsfiles.task=.upload&#39; \&#xA;/var/www/vhosts/system/DOMAIN/logs/&#xA;&#xA;Dabei sollte man beachten:&#xA;&#xA;Eine IP-Adresse im Log bezeichnet zunächst nur die Quelle eines Requests. Daraus lässt sich nicht ohne Weiteres ableiten, welche reale Person hinter einem Angriff stand.&#xA;&#xA;Nicht einfach alles löschen&#xA;&#xA;Bei einem solchen Vorfall ist die Versuchung groß, verdächtige Dateien sofort zu löschen.&#xA;&#xA;Für eine spätere Analyse ist es sinnvoller, sie zunächst außerhalb des Webroots zu sichern.&#xA;&#xA;Beispielsweise:&#xA;&#xA;mkdir -p /root/incident-quarantine&#xA;chmod 700 /root/incident-quarantine&#xA;&#xA;Verdächtige Dateien können anschließend dorthin verschoben und nur für root lesbar gemacht werden.&#xA;&#xA;Vor dem Verschieben sollte man – sofern möglich – mindestens festhalten:&#xA;&#xA;stat DATEI&#xA;sha256sum DATEI&#xA;file DATEI&#xA;&#xA;So bleiben Dateigröße, Zeitstempel, Dateityp und kryptographischer Hash dokumentiert.&#xA;&#xA;Ein SHA256-Hash ist beispielsweise hilfreich, um später festzustellen, ob mehrere unterschiedlich benannte Dateien tatsächlich identisch waren.&#xA;&#xA;Weitere Kopien außerhalb des Downloadordners&#xA;&#xA;Die Untersuchung sollte nicht beim offensichtlichen Fundort enden.&#xA;&#xA;In meinem Fall fanden sich weitere verdächtige PHP-Dateien außerhalb des ursprünglichen Upload-Verzeichnisses, unter anderem in Bereichen wie:&#xA;&#xA;administrator/cache/&#xA;modules/&#xA;templates/&#xA;&#xA;Das ist ein typisches Problem bei Webshell-Befall: Eine zunächst hochgeladene Datei kann dazu verwendet werden, weitere Kopien oder Backdoors anzulegen.&#xA;&#xA;Deshalb wurde anschließend der gesamte Joomla-Dateibaum nach bekannten Mustern und den bereits gefundenen Dateinamen durchsucht.&#xA;&#xA;Beispielsweise:&#xA;&#xA;grep -RIl \&#xA;&#39;DataBaseCache\|Nx-&#39; \&#xA;/var/www/vhosts/DOMAIN/httpdocs \&#xA;2  /dev/null&#xA;&#xA;Solche Suchmuster sind immer nur Indikatoren. Ein Treffer muss geprüft werden, bevor eine Datei entfernt wird.&#xA;&#xA;Auch temporäre Verzeichnisse prüfen&#xA;&#xA;Besonders interessant wurden außerdem:&#xA;&#xA;/tmp&#xA;/var/tmp&#xA;/dev/shm&#xA;&#xA;Dort fanden sich in diesem Fall mehrere versteckte ausführbare Dateien unter harmlos klingenden Namen.&#xA;&#xA;Beispiele für solche Tarnnamen waren sinngemäß:&#xA;&#xA;.dbus-session-temp-runtime&#xA;.firefox-browser-profile-cache&#xA;.libreoffice-auto-save-tmp&#xA;.nautilus-metadata-cache&#xA;&#xA;Mehrere dieser Dateien waren binär identisch.&#xA;&#xA;Sie lagen in einem privaten temporären Verzeichnis des Apache-Dienstes und gehörten dem Webspace-Benutzer.&#xA;&#xA;Vor der Quarantäne wurde geprüft, ob sie aktuell ausgeführt oder von einem Prozess geladen wurden, beispielsweise mit:&#xA;&#xA;lsof&#xA;&#xA;sowie über /proc.&#xA;&#xA;Zum Zeitpunkt der Untersuchung gab es dafür keine Hinweise.&#xA;&#xA;Auch das ist eine wichtige sprachliche Unterscheidung:&#xA;&#xA;  Keine laufende Instanz gefunden&#xA;&#xA;ist nicht dasselbe wie:&#xA;&#xA;  Diese Datei wurde niemals ausgeführt.&#xA;&#xA;SSH-Schlüssel des Webspace-Benutzers&#xA;&#xA;Im Home-Verzeichnis des Webspace-Benutzers fand sich außerdem ein privater SSH-Schlüssel.&#xA;&#xA;Er war nicht mit einer Passphrase geschützt.&#xA;&#xA;Da die Webshell mit den Rechten dieses Benutzers lief, musste davon ausgegangen werden, dass dieser private Schlüssel ausgelesen worden sein könnte.&#xA;&#xA;Der Schlüssel wurde deshalb aus dem Webspace entfernt und in die Root-Quarantäne verschoben.&#xA;&#xA;Das ist ein Punkt, der bei Webshell-Vorfällen leicht übersehen wird:&#xA;&#xA;Nicht nur Joomla-Passwörter können betroffen sein. Alles, was der kompromittierte Benutzer lesen konnte, muss als potentiell bekannt betrachtet werden.&#xA;&#xA;Zugangsdaten nach der Bereinigung ändern&#xA;&#xA;Nach der Dateibereinigung wurden deshalb unter anderem folgende Zugangsdaten rotiert:&#xA;&#xA;Joomla-Administratorpasswörter&#xA;Joomla-Datenbankpasswort&#xA;Passwort des Hosting-/Webspace-Benutzers&#xA;&#xA;Zusätzlich wurden bestehende Joomla-Sitzungen invalidiert.&#xA;&#xA;Beim Datenbankpasswort ist wichtig, dass Passwortänderung und Joomla-Konfiguration zusammenpassen. Wird nur das Datenbankkonto geändert, kann Joomla anschließend keine Verbindung mehr herstellen.&#xA;&#xA;Nach der Änderung sollte deshalb sowohl eine direkte Datenbankverbindung als auch die Webseite getestet werden.&#xA;&#xA;RSFiles! aktualisieren&#xA;&#xA;Der entscheidende erste Schutz ist selbstverständlich die Aktualisierung von RSFiles!.&#xA;&#xA;Die konkrete Upload-Schwachstelle wurde mit:&#xA;&#xA;RSFiles! 1.17.12&#xA;&#xA;behoben.&#xA;&#xA;Bei einer späteren Bereinigung sollte jedoch nicht gezielt auf 1.17.12 aktualisiert werden, sondern auf die jeweils aktuelle vom Hersteller bereitgestellte Version.&#xA;&#xA;Nach einem Update sollte außerdem geprüft werden, ob wirklich der neue Code ausgeführt wird und ob nicht alte Kopien der Erweiterung im Webroot liegen.&#xA;&#xA;Frontend-Upload zusätzlich blockieren&#xA;&#xA;In meinem Fall wurde der öffentliche RSFiles-Upload überhaupt nicht benötigt.&#xA;&#xA;Deshalb habe ich zusätzlich auf Webserver-Ebene den öffentlichen Upload-Endpunkt gesperrt.&#xA;&#xA;Die Regel wurde vor den normalen Joomla-Rewrite-Regeln in .htaccess eingefügt:&#xA;&#xA;Block public RSFiles frontend upload&#xA;RewriteCond %{REQUESTURI} !^/administrator/ [NC]&#xA;RewriteCond %{QUERYSTRING} (^|&amp;)option=comrsfiles(&amp;|$) [NC]&#xA;RewriteCond %{QUERYSTRING} (^|&amp;)task=(rsfiles\.)?upload(&amp;|$) [NC]&#xA;RewriteRule ^ - [F,L]&#xA;&#xA;Danach wurden unter anderem folgende Varianten getestet:&#xA;&#xA;task=rsfiles.upload&#xA;task=upload&#xA;&#xA;und mussten mit HTTP 403 beantwortet werden.&#xA;&#xA;Die normale Startseite musste gleichzeitig weiterhin HTTP 200 liefern.&#xA;&#xA;Wichtig&#xA;&#xA;Diese Regel ist nur sinnvoll, wenn Frontend-Uploads tatsächlich nicht benötigt werden.&#xA;&#xA;Wer RSFiles! bewusst dafür einsetzt, darf den Endpunkt natürlich nicht einfach sperren.&#xA;&#xA;Die Aktualisierung der Erweiterung ersetzt diese zusätzliche Schutzmaßnahme ebenfalls nicht.&#xA;&#xA;Downloadverzeichnis gegen direkte Ausführung schützen&#xA;&#xA;RSJoomla empfiehlt außerdem, Download- und Briefcase-Verzeichnisse zu schützen.&#xA;&#xA;Je nach Serverkonfiguration wird dafür beispielsweise eine .htaccess verwendet, die direkten Zugriff verhindert.&#xA;&#xA;Auf dem untersuchten System existierte im Downloadverzeichnis bereits eine entsprechende Schutzdatei.&#xA;&#xA;Trotzdem war die Schwachstelle zuvor ausnutzbar gewesen.&#xA;&#xA;Daraus folgt:&#xA;&#xA;  Ein geschützter Downloadordner ersetzt kein Sicherheitsupdate.&#xA;&#xA;Mehrere Schutzschichten sind sinnvoll, aber die verwundbare Anwendung selbst muss trotzdem aktualisiert werden.&#xA;&#xA;Joomla- und RSFiles-Datenbank ebenfalls prüfen&#xA;&#xA;Die Untersuchung beschränkte sich nicht auf das Dateisystem.&#xA;&#xA;Auch die RSFiles-Datenbank enthielt Einträge zu den hochgeladenen Dateien.&#xA;&#xA;Dabei konnten verdächtige Einträge identifiziert und nach vorheriger Sicherung entfernt werden.&#xA;&#xA;Wichtig ist auch hier die Trennung:&#xA;&#xA;Verdächtig aussehende Zeichenketten in einer Datenbank beweisen nicht automatisch eine erfolgreiche SQL-Injection.&#xA;&#xA;Sie können beispielsweise einfach Eingaben eines Angreifers sein, die eine Anwendung gespeichert hat.&#xA;&#xA;Prüfen, ob noch PHP-Dateien in schreibbaren Bereichen liegen&#xA;&#xA;Nach der Bereinigung wurde gezielt nach PHP-Dateien in typischerweise schreibbaren Verzeichnissen gesucht.&#xA;&#xA;Besonders relevant sind beispielsweise:&#xA;&#xA;images/&#xA;cache/&#xA;tmp/&#xA;downloads/&#xA;&#xA;Ein möglicher Check:&#xA;&#xA;find \&#xA;images cache tmp downloads \&#xA;-type f \&#xA;\( -iname &#39;.php&#39; -o -iname &#39;.phtml&#39; -o -iname &#39;.phar&#39; \) \&#xA;-print&#xA;&#xA;Ein leerer Treffer ist beruhigend.&#xA;&#xA;Er beweist allerdings nicht mathematisch, dass ein System vollständig sauber ist.&#xA;&#xA;Prozesse kontrollieren&#xA;&#xA;Auch nach der Dateibereinigung sollte geprüft werden, ob noch Prozesse unter dem Webspace-Benutzer laufen, die dort nicht hingehören.&#xA;&#xA;Beispielsweise:&#xA;&#xA;ps aux&#xA;&#xA;und gezielter:&#xA;&#xA;ps -u WEBUSER -f&#xA;&#xA;Außerdem lohnt sich ein Blick auf Netzwerkverbindungen und offene Ports.&#xA;&#xA;Bei einer beobachteten SSH-Verbindung ist allerdings Vorsicht bei der Interpretation nötig:&#xA;&#xA;Eine bestehende TCP-Verbindung auf Port 22 beweist noch keinen erfolgreichen SSH-Login.&#xA;&#xA;Im konkreten Fall zeigte die anschließende Prüfung der Authentifizierungslogs lediglich fehlgeschlagene Loginversuche.&#xA;&#xA;Cronjobs und Persistenz&#xA;&#xA;Ein Angreifer könnte versuchen, Schadcode über Cronjobs oder andere Autostartmechanismen dauerhaft zu machen.&#xA;&#xA;Deshalb wurden unter anderem geprüft:&#xA;&#xA;root-Crontab&#xA;Crontab des Webspace-Benutzers&#xA;systemweite Cron-Verzeichnisse&#xA;&#xA;Dort fanden sich keine offensichtlichen Hinweise auf eine zusätzliche Persistenz.&#xA;&#xA;Weitere sinnvolle Prüfstellen sind:&#xA;&#xA;systemd-Units und Timer&#xA;/etc/rc.local&#xA;Shell-Startdateien&#xA;&#xA;Was man aus einem leeren Malware-Scan nicht ableiten darf&#xA;&#xA;Nach der Bereinigung ergaben verschiedene Suchläufe keine Treffer mehr auf die bekannten Indikatoren.&#xA;&#xA;Das ist wichtig – aber die Formulierung sollte korrekt bleiben.&#xA;&#xA;Richtig ist:&#xA;&#xA;  In den untersuchten Verzeichnissen wurden keine weiteren Dateien gefunden, die den bekannten Suchmustern entsprachen.&#xA;&#xA;Nicht richtig wäre:&#xA;&#xA;  Der Server ist damit garantiert sauber.&#xA;&#xA;Insbesondere nach einer bestätigten Webshell-Kompromittierung bleibt eine Neuinstallation bzw. Migration auf ein frisches System die robusteste langfristige Lösung.&#xA;&#xA;Root-Kompromittierung nicht ohne Beleg behaupten&#xA;&#xA;Ein weiterer wichtiger Punkt aus diesem Vorfall:&#xA;&#xA;Die Existenz einer Webshell beweist eine erfolgreiche Codeausführung im Kontext des Webservers.&#xA;&#xA;Sie beweist nicht automatisch, dass ein Angreifer Root-Rechte hatte.&#xA;&#xA;In meinem Fall gab es:&#xA;&#xA;bestätigte fremde PHP-Dateien,&#xA;Webshell-Funktionalität,&#xA;verdächtige Binärdateien unter dem Webspace-Benutzer,&#xA;Zugriffsmöglichkeiten auf Joomla-Konfiguration und andere für diesen Benutzer lesbare Dateien.&#xA;&#xA;Es gab jedoch keinen belastbaren Beleg dafür, dass Root-Rechte erlangt wurden.&#xA;&#xA;Für die Entscheidung über eine spätere Neuinstallation ist der Vorfall trotzdem ernst genug.&#xA;&#xA;Meine Kurzfassung des Vorgehens&#xA;&#xA;Bei einem bestätigten RSFiles-Webshell-Befall würde ich heute in ungefähr dieser Reihenfolge vorgehen:&#xA;&#xA;RSFiles! auf die aktuelle sichere Version bringen bzw. den verwundbaren Upload sofort unterbinden.&#xA;Verdächtige Dateien nicht blind löschen, sondern zunächst dokumentieren und außerhalb des Webroots quarantänisieren.&#xA;Access-Logs auf RSFiles-Upload-Requests untersuchen.&#xA;Joomla-Webroot vollständig nach weiteren Backdoors durchsuchen.&#xA;/tmp, /var/tmp und /dev/shm prüfen.&#xA;Prozesse und Netzwerkverbindungen kontrollieren.&#xA;Cronjobs und weitere Persistenzmechanismen untersuchen.&#xA;Alle für den kompromittierten Webuser lesbaren Zugangsdaten als potentiell offengelegt behandeln.&#xA;Joomla-Admin-, Datenbank- und Hosting-Zugangsdaten rotieren.&#xA;10. Sitzungen invalidieren.&#xA;11. Downloadbereiche absichern.&#xA;12. Wenn Frontend-Upload nicht gebraucht wird, ihn zusätzlich auf Webserver-Ebene blockieren.&#xA;13. Nach der Bereinigung erneut nach den bekannten Indikatoren suchen.&#xA;14. Langfristig eine Migration auf ein frisches, unterstütztes System erwägen.&#xA;&#xA;Fazit&#xA;&#xA;Der wichtigste Punkt dieses Vorfalls war für mich, einen zunächst relativ unauffälligen Joomla-Hack nicht nur als „ein paar PHP-Dateien im Downloadordner“ zu behandeln.&#xA;&#xA;Sobald eine ausführbare Webshell vorhanden war, musste davon ausgegangen werden, dass der Angreifer alle Möglichkeiten des Webspace-Benutzers hatte.&#xA;&#xA;Gleichzeitig ist es wichtig, bei der Analyse nicht weiterzugehen, als die Belege tragen: Eine kompromittierte Joomla-/PHP-Umgebung ist noch kein Beweis für einen kompromittierten Root-Account.&#xA;&#xA;Die Kombination aus Log-Auswertung, Dateisystemanalyse, Quarantäne, Credential-Rotation und zusätzlichem Hardening hat den konkreten Vorfall bereinigt.&#xA;&#xA;Langfristig ersetzt das jedoch nicht die Überlegung, ein kompromittiertes Altsystem auf einer frischen und unterstützten Plattform neu aufzubauen.&#xA;&#xA;---&#xA;&#xA;Quellen&#xA;&#xA;RSJoomla!, „Unauthenticated File Upload fixed in RSFiles! version 1.17.12 – update NOW!“, 10.07.2026&#xA;RSJoomla!, RSFiles! Changelog, Version 1.17.12 und folgende Versionen&#xA;&#xA;Die Herstellerinformation bestätigt, dass RSFiles! 1.17.12 die unauthenticated-file-upload-Schwachstelle behebt und empfiehlt ausdrücklich, Download- und Briefcase-Verzeichnisse sowie die Webserver-Logs auf verdächtige Uploads zu kontrollieren.]]&gt;</description>
      <content:encoded><![CDATA[<p>Im Sommer 2026 wurde für die Joomla-Erweiterung RSFiles! eine kritische Sicherheitslücke bekannt, über die nicht angemeldete Besucher Dateien hochladen konnten. Besonders problematisch: Dadurch konnten auch PHP-Dateien in den Downloadbereich gelangen und anschließend über den Webserver ausgeführt werden.</p>

<p>In einer von mir administrierten Joomla-Installation war diese Schwachstelle tatsächlich ausgenutzt worden.</p>

<p>Dieser Beitrag dokumentiert die wichtigsten Spuren, die Bereinigung und einige Erkenntnisse aus dem Vorfall.</p>

<h2 id="die-sicherheitslücke">Die Sicherheitslücke</h2>

<p>RSJoomla veröffentlichte am 10. Juli 2026 RSFiles! 1.17.12 und schloss damit eine Schwachstelle im Frontend-Upload.</p>

<p>Nach Angaben des Herstellers konnte die fehlerhafte Upload-Funktion von nicht angemeldeten Besuchern verwendet werden und erlaubte dabei auch Dateitypen, die dort niemals hätten landen dürfen – einschließlich PHP-Dateien.</p>

<p>Die Schwachstelle wurde mit RSFiles! 1.17.12 behoben.</p>

<p>Wer eine ältere Version eingesetzt hat, sollte deshalb nicht nur aktualisieren, sondern auch prüfen, ob die Lücke bereits ausgenutzt wurde.</p>

<h2 id="das-erste-sichtbare-symptom">Das erste sichtbare Symptom</h2>

<p>Die Webseite selbst wirkte zunächst weitgehend normal.</p>

<p>Auffällig wurde der Vorfall unter anderem durch ungewöhnliche, von Google indexierte URLs. Eine davon sah beispielsweise so aus:</p>

<pre><code class="language-text">/component/rsfiles/details?path=Nx_164.php&amp;Itemid=...
</code></pre>

<p>Eine PHP-Datei mit einem Namen wie <code>Nx_164.php</code> hat in einem normalen Downloadbestand nichts verloren.</p>

<p>Bei der anschließenden Untersuchung fanden sich tatsächlich zahlreiche verdächtige PHP-Dateien im RSFiles-Downloadbereich.</p>

<h2 id="gefundene-dateien">Gefundene Dateien</h2>

<p>Unter den Funden waren unter anderem Namen wie:</p>

<pre><code class="language-text">Nx_*.php
shell.php
shell.PHP
sh.phtml
se.phar
se.inc
rsfiles_cache.php
bob_ipob4.php
bob_ipob4.php5
bob_ipob4.php7
</code></pre>

<p>Daneben existierten weitere PHP-, PHTML- und PHAR-Dateien mit zufällig wirkenden Namen.</p>

<p>Wichtig: Ein verdächtiger Dateiname allein beweist noch nicht, was eine Datei tut.</p>

<p>In diesem Fall zeigte die Inhaltsanalyse jedoch eindeutig Funktionen, die in einem normalen Downloadverzeichnis nichts zu suchen haben, darunter Datei-Uploads, Base64-Verarbeitung, PHP-Informationen und Möglichkeiten zur Ausführung von PHP-Funktionen.</p>

<p>Damit war klar: Es handelte sich nicht nur um erfolglose Angriffsversuche. Auf dem Webspace lagen tatsächlich Webshells.</p>

<h2 id="was-ein-solcher-fund-bedeutet-und-was-nicht">Was ein solcher Fund bedeutet – und was nicht</h2>

<p>Eine ausführbare Webshell unter dem Benutzer des Webservers ist ein ernstzunehmender Sicherheitsvorfall.</p>

<p>Sie bedeutet in diesem Fall:</p>

<blockquote><p>Fremder PHP-Code konnte mit den Rechten des Joomla-/Webspace-Benutzers ausgeführt werden.</p></blockquote>

<p>Das ist allerdings nicht automatisch gleichbedeutend mit:</p>

<blockquote><p>Der gesamte Server wurde als root kompromittiert.</p></blockquote>

<p>Diese Unterscheidung ist wichtig.</p>

<p>Ein PHP-Prozess kann beispielsweise:</p>
<ul><li>Joomla-Dateien lesen und verändern,</li>
<li><code>configuration.php</code> und damit Datenbankzugangsdaten lesen,</li>
<li>Dateien im Webspace anlegen,</li>
<li>weitere Schadsoftware nachladen,</li>
<li>erreichbare Dienste ansprechen.</li></ul>

<p>Ob darüber hinaus Root-Rechte erlangt wurden, muss separat untersucht werden.</p>

<p>In meinem Fall gab es Belege für eine Kompromittierung des Webspace-Benutzers, aber keinen belastbaren Nachweis einer erfolgreichen Root-Kompromittierung.</p>

<h2 id="die-access-logs-waren-besonders-hilfreich">Die Access-Logs waren besonders hilfreich</h2>

<p>In den Webserver-Logs ließen sich Upload-Versuche nachvollziehen.</p>

<p>Auffällig waren unter anderem Requests auf:</p>

<pre><code class="language-text">index.php?option=com_rsfiles&amp;task=upload
</code></pre>

<p>bzw.</p>

<pre><code class="language-text">index.php?option=com_rsfiles&amp;task=rsfiles.upload
</code></pre>

<p>Der Hersteller selbst empfiehlt nach Bekanntwerden der Schwachstelle, insbesondere nach entsprechenden POST-Requests zu suchen.</p>

<p>Eine einfache Suche kann beispielsweise so aussehen:</p>

<pre><code class="language-bash">grep -Rni \
&#39;option=com_rsfiles.*task=.*upload&#39; \
/var/www/vhosts/system/DOMAIN/logs/
</code></pre>

<p>Dabei sollte man beachten:</p>

<p>Eine IP-Adresse im Log bezeichnet zunächst nur die Quelle eines Requests. Daraus lässt sich nicht ohne Weiteres ableiten, welche reale Person hinter einem Angriff stand.</p>

<h2 id="nicht-einfach-alles-löschen">Nicht einfach alles löschen</h2>

<p>Bei einem solchen Vorfall ist die Versuchung groß, verdächtige Dateien sofort zu löschen.</p>

<p>Für eine spätere Analyse ist es sinnvoller, sie zunächst außerhalb des Webroots zu sichern.</p>

<p>Beispielsweise:</p>

<pre><code class="language-bash">mkdir -p /root/incident-quarantine
chmod 700 /root/incident-quarantine
</code></pre>

<p>Verdächtige Dateien können anschließend dorthin verschoben und nur für root lesbar gemacht werden.</p>

<p>Vor dem Verschieben sollte man – sofern möglich – mindestens festhalten:</p>

<pre><code class="language-bash">stat DATEI
sha256sum DATEI
file DATEI
</code></pre>

<p>So bleiben Dateigröße, Zeitstempel, Dateityp und kryptographischer Hash dokumentiert.</p>

<p>Ein SHA256-Hash ist beispielsweise hilfreich, um später festzustellen, ob mehrere unterschiedlich benannte Dateien tatsächlich identisch waren.</p>

<h2 id="weitere-kopien-außerhalb-des-downloadordners">Weitere Kopien außerhalb des Downloadordners</h2>

<p>Die Untersuchung sollte nicht beim offensichtlichen Fundort enden.</p>

<p>In meinem Fall fanden sich weitere verdächtige PHP-Dateien außerhalb des ursprünglichen Upload-Verzeichnisses, unter anderem in Bereichen wie:</p>

<pre><code class="language-text">administrator/cache/
modules/
templates/
</code></pre>

<p>Das ist ein typisches Problem bei Webshell-Befall: Eine zunächst hochgeladene Datei kann dazu verwendet werden, weitere Kopien oder Backdoors anzulegen.</p>

<p>Deshalb wurde anschließend der gesamte Joomla-Dateibaum nach bekannten Mustern und den bereits gefundenen Dateinamen durchsucht.</p>

<p>Beispielsweise:</p>

<pre><code class="language-bash">grep -RIl \
&#39;DataBase_Cache_\|Nx-&#39; \
/var/www/vhosts/DOMAIN/httpdocs \
2&gt;/dev/null
</code></pre>

<p>Solche Suchmuster sind immer nur Indikatoren. Ein Treffer muss geprüft werden, bevor eine Datei entfernt wird.</p>

<h2 id="auch-temporäre-verzeichnisse-prüfen">Auch temporäre Verzeichnisse prüfen</h2>

<p>Besonders interessant wurden außerdem:</p>

<pre><code class="language-text">/tmp
/var/tmp
/dev/shm
</code></pre>

<p>Dort fanden sich in diesem Fall mehrere versteckte ausführbare Dateien unter harmlos klingenden Namen.</p>

<p>Beispiele für solche Tarnnamen waren sinngemäß:</p>

<pre><code class="language-text">.dbus-session-temp-runtime
.firefox-browser-profile-cache
.libreoffice-auto-save-tmp
.nautilus-metadata-cache
</code></pre>

<p>Mehrere dieser Dateien waren binär identisch.</p>

<p>Sie lagen in einem privaten temporären Verzeichnis des Apache-Dienstes und gehörten dem Webspace-Benutzer.</p>

<p>Vor der Quarantäne wurde geprüft, ob sie aktuell ausgeführt oder von einem Prozess geladen wurden, beispielsweise mit:</p>

<pre><code class="language-bash">lsof
</code></pre>

<p>sowie über <code>/proc</code>.</p>

<p>Zum Zeitpunkt der Untersuchung gab es dafür keine Hinweise.</p>

<p>Auch das ist eine wichtige sprachliche Unterscheidung:</p>

<blockquote><p>Keine laufende Instanz gefunden</p></blockquote>

<p>ist nicht dasselbe wie:</p>

<blockquote><p>Diese Datei wurde niemals ausgeführt.</p></blockquote>

<h2 id="ssh-schlüssel-des-webspace-benutzers">SSH-Schlüssel des Webspace-Benutzers</h2>

<p>Im Home-Verzeichnis des Webspace-Benutzers fand sich außerdem ein privater SSH-Schlüssel.</p>

<p>Er war nicht mit einer Passphrase geschützt.</p>

<p>Da die Webshell mit den Rechten dieses Benutzers lief, musste davon ausgegangen werden, dass dieser private Schlüssel ausgelesen worden sein könnte.</p>

<p>Der Schlüssel wurde deshalb aus dem Webspace entfernt und in die Root-Quarantäne verschoben.</p>

<p>Das ist ein Punkt, der bei Webshell-Vorfällen leicht übersehen wird:</p>

<p>Nicht nur Joomla-Passwörter können betroffen sein. Alles, was der kompromittierte Benutzer lesen konnte, muss als potentiell bekannt betrachtet werden.</p>

<h2 id="zugangsdaten-nach-der-bereinigung-ändern">Zugangsdaten nach der Bereinigung ändern</h2>

<p>Nach der Dateibereinigung wurden deshalb unter anderem folgende Zugangsdaten rotiert:</p>
<ul><li>Joomla-Administratorpasswörter</li>
<li>Joomla-Datenbankpasswort</li>
<li>Passwort des Hosting-/Webspace-Benutzers</li></ul>

<p>Zusätzlich wurden bestehende Joomla-Sitzungen invalidiert.</p>

<p>Beim Datenbankpasswort ist wichtig, dass Passwortänderung und Joomla-Konfiguration zusammenpassen. Wird nur das Datenbankkonto geändert, kann Joomla anschließend keine Verbindung mehr herstellen.</p>

<p>Nach der Änderung sollte deshalb sowohl eine direkte Datenbankverbindung als auch die Webseite getestet werden.</p>

<h2 id="rsfiles-aktualisieren">RSFiles! aktualisieren</h2>

<p>Der entscheidende erste Schutz ist selbstverständlich die Aktualisierung von RSFiles!.</p>

<p>Die konkrete Upload-Schwachstelle wurde mit:</p>

<pre><code class="language-text">RSFiles! 1.17.12
</code></pre>

<p>behoben.</p>

<p>Bei einer späteren Bereinigung sollte jedoch nicht gezielt auf 1.17.12 aktualisiert werden, sondern auf die jeweils aktuelle vom Hersteller bereitgestellte Version.</p>

<p>Nach einem Update sollte außerdem geprüft werden, ob wirklich der neue Code ausgeführt wird und ob nicht alte Kopien der Erweiterung im Webroot liegen.</p>

<h2 id="frontend-upload-zusätzlich-blockieren">Frontend-Upload zusätzlich blockieren</h2>

<p>In meinem Fall wurde der öffentliche RSFiles-Upload überhaupt nicht benötigt.</p>

<p>Deshalb habe ich zusätzlich auf Webserver-Ebene den öffentlichen Upload-Endpunkt gesperrt.</p>

<p>Die Regel wurde vor den normalen Joomla-Rewrite-Regeln in <code>.htaccess</code> eingefügt:</p>

<pre><code class="language-apache"># Block public RSFiles frontend upload
RewriteCond %{REQUEST_URI} !^/administrator/ [NC]
RewriteCond %{QUERY_STRING} (^|&amp;)option=com_rsfiles(&amp;|$) [NC]
RewriteCond %{QUERY_STRING} (^|&amp;)task=(rsfiles\.)?upload(&amp;|$) [NC]
RewriteRule ^ - [F,L]
</code></pre>

<p>Danach wurden unter anderem folgende Varianten getestet:</p>

<pre><code class="language-text">task=rsfiles.upload
task=upload
</code></pre>

<p>und mussten mit HTTP 403 beantwortet werden.</p>

<p>Die normale Startseite musste gleichzeitig weiterhin HTTP 200 liefern.</p>

<h3 id="wichtig">Wichtig</h3>

<p>Diese Regel ist <strong>nur sinnvoll, wenn Frontend-Uploads tatsächlich nicht benötigt werden</strong>.</p>

<p>Wer RSFiles! bewusst dafür einsetzt, darf den Endpunkt natürlich nicht einfach sperren.</p>

<p>Die Aktualisierung der Erweiterung ersetzt diese zusätzliche Schutzmaßnahme ebenfalls nicht.</p>

<h2 id="downloadverzeichnis-gegen-direkte-ausführung-schützen">Downloadverzeichnis gegen direkte Ausführung schützen</h2>

<p>RSJoomla empfiehlt außerdem, Download- und Briefcase-Verzeichnisse zu schützen.</p>

<p>Je nach Serverkonfiguration wird dafür beispielsweise eine <code>.htaccess</code> verwendet, die direkten Zugriff verhindert.</p>

<p>Auf dem untersuchten System existierte im Downloadverzeichnis bereits eine entsprechende Schutzdatei.</p>

<p>Trotzdem war die Schwachstelle zuvor ausnutzbar gewesen.</p>

<p>Daraus folgt:</p>

<blockquote><p>Ein geschützter Downloadordner ersetzt kein Sicherheitsupdate.</p></blockquote>

<p>Mehrere Schutzschichten sind sinnvoll, aber die verwundbare Anwendung selbst muss trotzdem aktualisiert werden.</p>

<h2 id="joomla-und-rsfiles-datenbank-ebenfalls-prüfen">Joomla- und RSFiles-Datenbank ebenfalls prüfen</h2>

<p>Die Untersuchung beschränkte sich nicht auf das Dateisystem.</p>

<p>Auch die RSFiles-Datenbank enthielt Einträge zu den hochgeladenen Dateien.</p>

<p>Dabei konnten verdächtige Einträge identifiziert und nach vorheriger Sicherung entfernt werden.</p>

<p>Wichtig ist auch hier die Trennung:</p>

<p>Verdächtig aussehende Zeichenketten in einer Datenbank beweisen nicht automatisch eine erfolgreiche SQL-Injection.</p>

<p>Sie können beispielsweise einfach Eingaben eines Angreifers sein, die eine Anwendung gespeichert hat.</p>

<h2 id="prüfen-ob-noch-php-dateien-in-schreibbaren-bereichen-liegen">Prüfen, ob noch PHP-Dateien in schreibbaren Bereichen liegen</h2>

<p>Nach der Bereinigung wurde gezielt nach PHP-Dateien in typischerweise schreibbaren Verzeichnissen gesucht.</p>

<p>Besonders relevant sind beispielsweise:</p>

<pre><code class="language-text">images/
cache/
tmp/
downloads/
</code></pre>

<p>Ein möglicher Check:</p>

<pre><code class="language-bash">find \
images cache tmp downloads \
-type f \
\( -iname &#39;*.php&#39; -o -iname &#39;*.phtml&#39; -o -iname &#39;*.phar&#39; \) \
-print
</code></pre>

<p>Ein leerer Treffer ist beruhigend.</p>

<p>Er beweist allerdings nicht mathematisch, dass ein System vollständig sauber ist.</p>

<h2 id="prozesse-kontrollieren">Prozesse kontrollieren</h2>

<p>Auch nach der Dateibereinigung sollte geprüft werden, ob noch Prozesse unter dem Webspace-Benutzer laufen, die dort nicht hingehören.</p>

<p>Beispielsweise:</p>

<pre><code class="language-bash">ps aux
</code></pre>

<p>und gezielter:</p>

<pre><code class="language-bash">ps -u WEBUSER -f
</code></pre>

<p>Außerdem lohnt sich ein Blick auf Netzwerkverbindungen und offene Ports.</p>

<p>Bei einer beobachteten SSH-Verbindung ist allerdings Vorsicht bei der Interpretation nötig:</p>

<p>Eine bestehende TCP-Verbindung auf Port 22 beweist noch keinen erfolgreichen SSH-Login.</p>

<p>Im konkreten Fall zeigte die anschließende Prüfung der Authentifizierungslogs lediglich fehlgeschlagene Loginversuche.</p>

<h2 id="cronjobs-und-persistenz">Cronjobs und Persistenz</h2>

<p>Ein Angreifer könnte versuchen, Schadcode über Cronjobs oder andere Autostartmechanismen dauerhaft zu machen.</p>

<p>Deshalb wurden unter anderem geprüft:</p>

<pre><code class="language-text">root-Crontab
Crontab des Webspace-Benutzers
systemweite Cron-Verzeichnisse
</code></pre>

<p>Dort fanden sich keine offensichtlichen Hinweise auf eine zusätzliche Persistenz.</p>

<p>Weitere sinnvolle Prüfstellen sind:</p>

<pre><code class="language-text">systemd-Units und Timer
/etc/rc.local
Shell-Startdateien
</code></pre>

<h2 id="was-man-aus-einem-leeren-malware-scan-nicht-ableiten-darf">Was man aus einem leeren Malware-Scan nicht ableiten darf</h2>

<p>Nach der Bereinigung ergaben verschiedene Suchläufe keine Treffer mehr auf die bekannten Indikatoren.</p>

<p>Das ist wichtig – aber die Formulierung sollte korrekt bleiben.</p>

<p>Richtig ist:</p>

<blockquote><p>In den untersuchten Verzeichnissen wurden keine weiteren Dateien gefunden, die den bekannten Suchmustern entsprachen.</p></blockquote>

<p>Nicht richtig wäre:</p>

<blockquote><p>Der Server ist damit garantiert sauber.</p></blockquote>

<p>Insbesondere nach einer bestätigten Webshell-Kompromittierung bleibt eine Neuinstallation bzw. Migration auf ein frisches System die robusteste langfristige Lösung.</p>

<h2 id="root-kompromittierung-nicht-ohne-beleg-behaupten">Root-Kompromittierung nicht ohne Beleg behaupten</h2>

<p>Ein weiterer wichtiger Punkt aus diesem Vorfall:</p>

<p>Die Existenz einer Webshell beweist eine erfolgreiche Codeausführung im Kontext des Webservers.</p>

<p>Sie beweist <strong>nicht automatisch</strong>, dass ein Angreifer Root-Rechte hatte.</p>

<p>In meinem Fall gab es:</p>
<ul><li>bestätigte fremde PHP-Dateien,</li>
<li>Webshell-Funktionalität,</li>
<li>verdächtige Binärdateien unter dem Webspace-Benutzer,</li>
<li>Zugriffsmöglichkeiten auf Joomla-Konfiguration und andere für diesen Benutzer lesbare Dateien.</li></ul>

<p>Es gab jedoch keinen belastbaren Beleg dafür, dass Root-Rechte erlangt wurden.</p>

<p>Für die Entscheidung über eine spätere Neuinstallation ist der Vorfall trotzdem ernst genug.</p>

<h2 id="meine-kurzfassung-des-vorgehens">Meine Kurzfassung des Vorgehens</h2>

<p>Bei einem bestätigten RSFiles-Webshell-Befall würde ich heute in ungefähr dieser Reihenfolge vorgehen:</p>
<ol><li>RSFiles! auf die aktuelle sichere Version bringen bzw. den verwundbaren Upload sofort unterbinden.</li>
<li>Verdächtige Dateien nicht blind löschen, sondern zunächst dokumentieren und außerhalb des Webroots quarantänisieren.</li>
<li>Access-Logs auf RSFiles-Upload-Requests untersuchen.</li>
<li>Joomla-Webroot vollständig nach weiteren Backdoors durchsuchen.</li>
<li><code>/tmp</code>, <code>/var/tmp</code> und <code>/dev/shm</code> prüfen.</li>
<li>Prozesse und Netzwerkverbindungen kontrollieren.</li>
<li>Cronjobs und weitere Persistenzmechanismen untersuchen.</li>
<li>Alle für den kompromittierten Webuser lesbaren Zugangsdaten als potentiell offengelegt behandeln.</li>
<li>Joomla-Admin-, Datenbank- und Hosting-Zugangsdaten rotieren.</li>
<li>Sitzungen invalidieren.</li>
<li>Downloadbereiche absichern.</li>
<li>Wenn Frontend-Upload nicht gebraucht wird, ihn zusätzlich auf Webserver-Ebene blockieren.</li>
<li>Nach der Bereinigung erneut nach den bekannten Indikatoren suchen.</li>
<li>Langfristig eine Migration auf ein frisches, unterstütztes System erwägen.</li></ol>

<h2 id="fazit">Fazit</h2>

<p>Der wichtigste Punkt dieses Vorfalls war für mich, einen zunächst relativ unauffälligen Joomla-Hack nicht nur als „ein paar PHP-Dateien im Downloadordner“ zu behandeln.</p>

<p>Sobald eine ausführbare Webshell vorhanden war, musste davon ausgegangen werden, dass der Angreifer alle Möglichkeiten des Webspace-Benutzers hatte.</p>

<p>Gleichzeitig ist es wichtig, bei der Analyse nicht weiterzugehen, als die Belege tragen: Eine kompromittierte Joomla-/PHP-Umgebung ist noch kein Beweis für einen kompromittierten Root-Account.</p>

<p>Die Kombination aus Log-Auswertung, Dateisystemanalyse, Quarantäne, Credential-Rotation und zusätzlichem Hardening hat den konkreten Vorfall bereinigt.</p>

<p>Langfristig ersetzt das jedoch nicht die Überlegung, ein kompromittiertes Altsystem auf einer frischen und unterstützten Plattform neu aufzubauen.</p>

<hr>

<h2 id="quellen">Quellen</h2>
<ul><li>RSJoomla!, „Unauthenticated File Upload fixed in RSFiles! version 1.17.12 – update NOW!“, 10.07.2026</li>
<li>RSJoomla!, RSFiles! Changelog, Version 1.17.12 und folgende Versionen</li></ul>

<p>Die Herstellerinformation bestätigt, dass RSFiles! 1.17.12 die unauthenticated-file-upload-Schwachstelle behebt und empfiehlt ausdrücklich, Download- und Briefcase-Verzeichnisse sowie die Webserver-Logs auf verdächtige Uploads zu kontrollieren.</p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-rsfiles-unauthenticated-file-upload-webshell-befall-erkennen-und</guid>
      <pubDate>Wed, 16 Sep 2026 12:56:27 +0000</pubDate>
    </item>
    <item>
      <title>Joomla 5.4 / EasyBlog 6.0.18: „Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()“</title>
      <link>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-5-4-easyblog-6-0-18-call-to-undefined-method</link>
      <description>&lt;![CDATA[Nach einem Joomla-Update ließen sich auf einer meiner Webseiten einzelne EasyBlog-Beiträge nicht mehr öffnen. Statt des Artikels erschien:&#xA;&#xA;Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()&#xA;&#xA;Die EasyBlog-Übersichten und die auf der Startseite eingebundenen „Latest Blogs“-Module funktionierten weiterhin. Erst beim Aufruf eines einzelnen Blogbeitrags trat der Fehler auf.&#xA;&#xA;Hier dokumentiere ich Ursache und Lösung, weil sich der eigentliche Fehler erst nach einem Vergleich der installierten EasyBlog-Dateien mit dem offiziellen Installationspaket zeigte.&#xA;&#xA;System&#xA;&#xA;Zum Zeitpunkt des Fehlers:&#xA;&#xA;Joomla 5.4.8&#xA;EasyBlog 6.0.18&#xA;PHP 8.2&#xA;Plesk&#xA;&#xA;EasyBlog selbst zeigte im Backend korrekt die Version 6.0.18 an.&#xA;&#xA;Die Fehlermeldung&#xA;&#xA;Die Meldung&#xA;&#xA;Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()&#xA;&#xA;deutet zunächst auf alten Datenbankcode hin.&#xA;&#xA;In verschiedenen EasyBlog-Dateien fanden sich noch Aufrufe wie:&#xA;&#xA;$db-  nameQuote(...)&#xA;&#xA;Beim normalen Joomla-Datenbanktreiber steht diese alte Methode unter Joomla 5.4 nicht mehr zur Verfügung. Die aktuelle Methode heißt:&#xA;&#xA;$db-  quoteName(...)&#xA;&#xA;Ein globales Suchen und Ersetzen von nameQuote() durch quoteName() ist trotzdem keine gute Lösung, weil EasyBlog teilweise eine eigene Datenbank-Kompatibilitätsschicht verwendet.&#xA;&#xA;Entscheidend ist also auch, welches Datenbankobjekt an der jeweiligen Stelle verwendet wird.&#xA;&#xA;Auffälliger Code&#xA;&#xA;Bei der Fehlersuche fanden sich beispielsweise Konstruktionen wie:&#xA;&#xA;$db = $this-  getDBO();&#xA;&#xA;$query = &#39;SELECT ... &#39; . $db-  nameQuote(...);&#xA;&#xA;$this-  getDBO() liefert hier den normalen Joomla-Datenbanktreiber.&#xA;&#xA;Andere EasyBlog-Bereiche verwenden dagegen:&#xA;&#xA;$db = EB::db();&#xA;&#xA;EasyBlog besitzt dafür eine eigene Kompatibilitätsschicht.&#xA;&#xA;Damit stellte sich die Frage, warum in einer Installation, die bereits als EasyBlog 6.0.18 ausgewiesen wurde, unterschiedliche Code-Stände vorhanden waren.&#xA;&#xA;Der entscheidende Test: Vergleich mit dem offiziellen EasyBlog-Paket&#xA;&#xA;Ich hatte das originale Installationspaket von EasyBlog 6.0.18 noch vorliegen.&#xA;&#xA;Daraufhin wurden die installierten Dateien per SHA256 mit den entsprechenden Dateien aus diesem offiziellen Paket verglichen.&#xA;&#xA;Dabei zeigten sich sechs Abweichungen im Verzeichnis:&#xA;&#xA;administrator/components/comeasyblog/tables/&#xA;&#xA;Betroffen waren:&#xA;&#xA;oauth.php&#xA;meta.php&#xA;feedburner.php&#xA;tag.php&#xA;adsense.php&#xA;teamblog.php&#xA;&#xA;Andere Dateien aus demselben Verzeichnis entsprachen dagegen exakt dem offiziellen 6.0.18-Paket.&#xA;&#xA;Damit war die eigentliche Ursache gefunden:&#xA;&#xA;  Die Installation meldete zwar EasyBlog 6.0.18, der Dateibestand entsprach aber nicht vollständig dem offiziellen EasyBlog-6.0.18-Paket.&#xA;&#xA;Es handelte sich also nicht einfach um eine generelle Inkompatibilität von EasyBlog 6.0.18 mit Joomla 5.4, sondern um einen gemischten bzw. unvollständig aktualisierten Dateibestand.&#xA;&#xA;Die Lösung&#xA;&#xA;Zunächst wurden die vorhandenen sechs Dateien gesichert.&#xA;&#xA;Beispielsweise:&#xA;&#xA;mkdir -p /root/easyblog-backup&#xA;&#xA;cp -a \&#xA;administrator/components/comeasyblog/tables/oauth.php \&#xA;administrator/components/comeasyblog/tables/meta.php \&#xA;administrator/components/comeasyblog/tables/feedburner.php \&#xA;administrator/components/comeasyblog/tables/tag.php \&#xA;administrator/components/comeasyblog/tables/adsense.php \&#xA;administrator/components/com_easyblog/tables/teamblog.php \&#xA;/root/easyblog-backup/&#xA;&#xA;Anschließend wurden genau diese sechs Dateien durch die entsprechenden Originaldateien aus dem offiziellen EasyBlog-6.0.18-Paket ersetzt.&#xA;&#xA;Danach wurden die neuen Live-Dateien erneut per SHA256 gegen das Originalpaket geprüft.&#xA;&#xA;Alle sechs Dateien ergaben:&#xA;&#xA;OK&#xA;&#xA;Zusätzlich wurde jede Datei mit PHP auf Syntaxfehler geprüft:&#xA;&#xA;/opt/plesk/php/8.2/bin/php -l DATEI.php&#xA;&#xA;Bei allen sechs Dateien lautete das Ergebnis:&#xA;&#xA;No syntax errors detected&#xA;&#xA;Ergebnis&#xA;&#xA;Unmittelbar nach dem Austausch ließen sich die EasyBlog-Beiträge wieder normal öffnen.&#xA;&#xA;Der Fehler&#xA;&#xA;Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()&#xA;&#xA;war verschwunden.&#xA;&#xA;Auch sämtliche auf der Startseite über das EasyBlog-LatestBlogs-Modul verlinkten Beiträge funktionierten wieder.&#xA;&#xA;An der Datenbank musste für die eigentliche Reparatur nichts verändert werden.&#xA;&#xA;Was während der Diagnose nicht geholfen hat&#xA;&#xA;Vor der eigentlichen Ursache wurden unter anderem folgende Möglichkeiten untersucht:&#xA;&#xA;EasyBlog-Smart-Search-Plugin deaktiviert&#xA;alte EasySocial-Plugins kontrolliert&#xA;EasyBlog-Menüeinträge und Routing untersucht&#xA;verschiedene EasyBlog-URL-Präfixe getestet&#xA;einzelne nameQuote()-Aufrufe testweise auf quoteName() geändert&#xA;OAuth-Code als möglicher Fehlerpfad untersucht&#xA;&#xA;Diese Schritte halfen bei der Eingrenzung, behoben das Problem aber nicht dauerhaft.&#xA;&#xA;Nebenbefund: EasyBlog-Routing&#xA;&#xA;Während der Fehlersuche fiel auf, dass EasyBlog für dieselbe Kategorie mehrere technische Menüeinträge zur Verfügung hatte.&#xA;&#xA;Dadurch wurden je nach aktiviertem Menüpunkt URLs wie&#xA;&#xA;/easyblog-aktuelles-template/artikelname&#xA;&#xA;oder&#xA;&#xA;/mzffm-aktuelles/artikelname&#xA;&#xA;erzeugt.&#xA;&#xA;Das war jedoch nicht die Ursache des nameQuote()-Fehlers. Das Ändern des Menüeintrags änderte lediglich die URL, während der PHP-Fehler bestehen blieb.&#xA;&#xA;Warum ich nicht einfach nameQuote() global ersetzt habe&#xA;&#xA;Ein pauschales&#xA;&#xA;nameQuote() → quoteName()&#xA;&#xA;über die gesamte EasyBlog-Installation hätte zwar einzelne Fehlerstellen beseitigen können, aber nicht die eigentliche Ursache gelöst.&#xA;&#xA;Da EasyBlog teilweise eigene Datenbank-Wrapper verwendet, kann derselbe Methodenaufruf je nach Kontext unterschiedlich problematisch sein.&#xA;&#xA;Der zuverlässigere Ansatz war:&#xA;&#xA;installierte EasyBlog-Version feststellen,&#xA;originales Installationspaket derselben Version heranziehen,&#xA;Dateien vergleichen,&#xA;abweichende Dateien sichern,&#xA;diese durch die offiziellen Originaldateien ersetzen,&#xA;Prüfsummen und PHP-Syntax kontrollieren.&#xA;&#xA;Empfehlung bei ähnlichen Fehlern&#xA;&#xA;Wenn unter Joomla 5.4 mit EasyBlog die Meldung&#xA;&#xA;Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()&#xA;&#xA;erscheint, würde ich nicht zuerst massenhaft PHP-Code ändern.&#xA;&#xA;Stattdessen sollte geprüft werden, ob der tatsächliche Dateibestand wirklich vollständig der angezeigten EasyBlog-Version entspricht.&#xA;&#xA;Eine Versionsnummer in der Joomla-Erweiterungsverwaltung garantiert offenbar nicht zwangsläufig, dass jede einzelne Datei tatsächlich aus diesem Versionsstand stammt.&#xA;&#xA;Kurzfassung&#xA;&#xA;Fehler&#xA;&#xA;Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()&#xA;&#xA;Umgebung&#xA;&#xA;Joomla 5.4.8&#xA;EasyBlog 6.0.18&#xA;PHP 8.2&#xA;&#xA;Ursache&#xA;&#xA;EasyBlog meldete Version 6.0.18, mehrere installierte PHP-Dateien wichen jedoch vom offiziellen EasyBlog-6.0.18-Paket ab.&#xA;&#xA;Lösung&#xA;&#xA;Die abweichenden Dateien wurden identifiziert, gesichert und durch die entsprechenden Originaldateien aus dem offiziellen EasyBlog-6.0.18-Paket ersetzt.&#xA;&#xA;Ergebnis&#xA;&#xA;Die EasyBlog-Beiträge funktionieren wieder und der nameQuote()-Fehler ist verschwunden.&#xA;&#xA;---&#xA;&#xA;Hinweis: Die EasyBlog-Originaldateien stelle ich bewusst nicht zum Download bereit. EasyBlog ist kommerzielle Software. Für diese Reparatur wird ein legitimes Installationspaket der jeweils verwendeten Version benötigt.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>Nach einem Joomla-Update ließen sich auf einer meiner Webseiten einzelne EasyBlog-Beiträge nicht mehr öffnen. Statt des Artikels erschien:</p>

<pre><code class="language-text">Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()
</code></pre>

<p>Die EasyBlog-Übersichten und die auf der Startseite eingebundenen „Latest Blogs“-Module funktionierten weiterhin. Erst beim Aufruf eines einzelnen Blogbeitrags trat der Fehler auf.</p>

<p>Hier dokumentiere ich Ursache und Lösung, weil sich der eigentliche Fehler erst nach einem Vergleich der installierten EasyBlog-Dateien mit dem offiziellen Installationspaket zeigte.</p>

<h2 id="system">System</h2>

<p>Zum Zeitpunkt des Fehlers:</p>
<ul><li>Joomla 5.4.8</li>
<li>EasyBlog 6.0.18</li>
<li>PHP 8.2</li>
<li>Plesk</li></ul>

<p>EasyBlog selbst zeigte im Backend korrekt die Version <strong>6.0.18</strong> an.</p>

<h2 id="die-fehlermeldung">Die Fehlermeldung</h2>

<p>Die Meldung</p>

<pre><code class="language-text">Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()
</code></pre>

<p>deutet zunächst auf alten Datenbankcode hin.</p>

<p>In verschiedenen EasyBlog-Dateien fanden sich noch Aufrufe wie:</p>

<pre><code class="language-php">$db-&gt;nameQuote(...)
</code></pre>

<p>Beim normalen Joomla-Datenbanktreiber steht diese alte Methode unter Joomla 5.4 nicht mehr zur Verfügung. Die aktuelle Methode heißt:</p>

<pre><code class="language-php">$db-&gt;quoteName(...)
</code></pre>

<p>Ein globales Suchen und Ersetzen von <code>nameQuote()</code> durch <code>quoteName()</code> ist trotzdem keine gute Lösung, weil EasyBlog teilweise eine eigene Datenbank-Kompatibilitätsschicht verwendet.</p>

<p>Entscheidend ist also auch, <strong>welches Datenbankobjekt an der jeweiligen Stelle verwendet wird</strong>.</p>

<h2 id="auffälliger-code">Auffälliger Code</h2>

<p>Bei der Fehlersuche fanden sich beispielsweise Konstruktionen wie:</p>

<pre><code class="language-php">$db = $this-&gt;getDBO();

$query = &#39;SELECT ... &#39; . $db-&gt;nameQuote(...);
</code></pre>

<p><code>$this-&gt;getDBO()</code> liefert hier den normalen Joomla-Datenbanktreiber.</p>

<p>Andere EasyBlog-Bereiche verwenden dagegen:</p>

<pre><code class="language-php">$db = EB::db();
</code></pre>

<p>EasyBlog besitzt dafür eine eigene Kompatibilitätsschicht.</p>

<p>Damit stellte sich die Frage, warum in einer Installation, die bereits als EasyBlog 6.0.18 ausgewiesen wurde, unterschiedliche Code-Stände vorhanden waren.</p>

<h2 id="der-entscheidende-test-vergleich-mit-dem-offiziellen-easyblog-paket">Der entscheidende Test: Vergleich mit dem offiziellen EasyBlog-Paket</h2>

<p>Ich hatte das originale Installationspaket von EasyBlog 6.0.18 noch vorliegen.</p>

<p>Daraufhin wurden die installierten Dateien per SHA256 mit den entsprechenden Dateien aus diesem offiziellen Paket verglichen.</p>

<p>Dabei zeigten sich sechs Abweichungen im Verzeichnis:</p>

<pre><code class="language-text">administrator/components/com_easyblog/tables/
</code></pre>

<p>Betroffen waren:</p>

<pre><code class="language-text">oauth.php
meta.php
feedburner.php
tag.php
adsense.php
teamblog.php
</code></pre>

<p>Andere Dateien aus demselben Verzeichnis entsprachen dagegen exakt dem offiziellen 6.0.18-Paket.</p>

<p>Damit war die eigentliche Ursache gefunden:</p>

<blockquote><p>Die Installation meldete zwar EasyBlog 6.0.18, der Dateibestand entsprach aber nicht vollständig dem offiziellen EasyBlog-6.0.18-Paket.</p></blockquote>

<p>Es handelte sich also nicht einfach um eine generelle Inkompatibilität von EasyBlog 6.0.18 mit Joomla 5.4, sondern um einen gemischten bzw. unvollständig aktualisierten Dateibestand.</p>

<h2 id="die-lösung">Die Lösung</h2>

<p>Zunächst wurden die vorhandenen sechs Dateien gesichert.</p>

<p>Beispielsweise:</p>

<pre><code class="language-bash">mkdir -p /root/easyblog-backup

cp -a \
administrator/components/com_easyblog/tables/oauth.php \
administrator/components/com_easyblog/tables/meta.php \
administrator/components/com_easyblog/tables/feedburner.php \
administrator/components/com_easyblog/tables/tag.php \
administrator/components/com_easyblog/tables/adsense.php \
administrator/components/com_easyblog/tables/teamblog.php \
/root/easyblog-backup/
</code></pre>

<p>Anschließend wurden genau diese sechs Dateien durch die entsprechenden Originaldateien aus dem offiziellen EasyBlog-6.0.18-Paket ersetzt.</p>

<p>Danach wurden die neuen Live-Dateien erneut per SHA256 gegen das Originalpaket geprüft.</p>

<p>Alle sechs Dateien ergaben:</p>

<pre><code class="language-text">OK
</code></pre>

<p>Zusätzlich wurde jede Datei mit PHP auf Syntaxfehler geprüft:</p>

<pre><code class="language-bash">/opt/plesk/php/8.2/bin/php -l DATEI.php
</code></pre>

<p>Bei allen sechs Dateien lautete das Ergebnis:</p>

<pre><code class="language-text">No syntax errors detected
</code></pre>

<h2 id="ergebnis">Ergebnis</h2>

<p>Unmittelbar nach dem Austausch ließen sich die EasyBlog-Beiträge wieder normal öffnen.</p>

<p>Der Fehler</p>

<pre><code class="language-text">Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()
</code></pre>

<p>war verschwunden.</p>

<p>Auch sämtliche auf der Startseite über das EasyBlog-LatestBlogs-Modul verlinkten Beiträge funktionierten wieder.</p>

<p>An der Datenbank musste für die eigentliche Reparatur nichts verändert werden.</p>

<h2 id="was-während-der-diagnose-nicht-geholfen-hat">Was während der Diagnose nicht geholfen hat</h2>

<p>Vor der eigentlichen Ursache wurden unter anderem folgende Möglichkeiten untersucht:</p>
<ul><li>EasyBlog-Smart-Search-Plugin deaktiviert</li>
<li>alte EasySocial-Plugins kontrolliert</li>
<li>EasyBlog-Menüeinträge und Routing untersucht</li>
<li>verschiedene EasyBlog-URL-Präfixe getestet</li>
<li>einzelne <code>nameQuote()</code>-Aufrufe testweise auf <code>quoteName()</code> geändert</li>
<li>OAuth-Code als möglicher Fehlerpfad untersucht</li></ul>

<p>Diese Schritte halfen bei der Eingrenzung, behoben das Problem aber nicht dauerhaft.</p>

<h2 id="nebenbefund-easyblog-routing">Nebenbefund: EasyBlog-Routing</h2>

<p>Während der Fehlersuche fiel auf, dass EasyBlog für dieselbe Kategorie mehrere technische Menüeinträge zur Verfügung hatte.</p>

<p>Dadurch wurden je nach aktiviertem Menüpunkt URLs wie</p>

<pre><code class="language-text">/easyblog-aktuelles-template/artikelname
</code></pre>

<p>oder</p>

<pre><code class="language-text">/mzffm-aktuelles/artikelname
</code></pre>

<p>erzeugt.</p>

<p>Das war jedoch <strong>nicht die Ursache des <code>nameQuote()</code>-Fehlers</strong>. Das Ändern des Menüeintrags änderte lediglich die URL, während der PHP-Fehler bestehen blieb.</p>

<h2 id="warum-ich-nicht-einfach-namequote-global-ersetzt-habe">Warum ich nicht einfach <code>nameQuote()</code> global ersetzt habe</h2>

<p>Ein pauschales</p>

<pre><code class="language-text">nameQuote() → quoteName()
</code></pre>

<p>über die gesamte EasyBlog-Installation hätte zwar einzelne Fehlerstellen beseitigen können, aber nicht die eigentliche Ursache gelöst.</p>

<p>Da EasyBlog teilweise eigene Datenbank-Wrapper verwendet, kann derselbe Methodenaufruf je nach Kontext unterschiedlich problematisch sein.</p>

<p>Der zuverlässigere Ansatz war:</p>
<ol><li>installierte EasyBlog-Version feststellen,</li>
<li>originales Installationspaket derselben Version heranziehen,</li>
<li>Dateien vergleichen,</li>
<li>abweichende Dateien sichern,</li>
<li>diese durch die offiziellen Originaldateien ersetzen,</li>
<li>Prüfsummen und PHP-Syntax kontrollieren.</li></ol>

<h2 id="empfehlung-bei-ähnlichen-fehlern">Empfehlung bei ähnlichen Fehlern</h2>

<p>Wenn unter Joomla 5.4 mit EasyBlog die Meldung</p>

<pre><code class="language-text">Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()
</code></pre>

<p>erscheint, würde ich nicht zuerst massenhaft PHP-Code ändern.</p>

<p>Stattdessen sollte geprüft werden, ob der <strong>tatsächliche Dateibestand wirklich vollständig der angezeigten EasyBlog-Version entspricht</strong>.</p>

<p>Eine Versionsnummer in der Joomla-Erweiterungsverwaltung garantiert offenbar nicht zwangsläufig, dass jede einzelne Datei tatsächlich aus diesem Versionsstand stammt.</p>

<h2 id="kurzfassung">Kurzfassung</h2>

<p><strong>Fehler</strong></p>

<pre><code class="language-text">Call to undefined method Joomla\Database\Mysqli\MysqliDriver::nameQuote()
</code></pre>

<p><strong>Umgebung</strong></p>

<pre><code class="language-text">Joomla 5.4.8
EasyBlog 6.0.18
PHP 8.2
</code></pre>

<p><strong>Ursache</strong></p>

<p>EasyBlog meldete Version 6.0.18, mehrere installierte PHP-Dateien wichen jedoch vom offiziellen EasyBlog-6.0.18-Paket ab.</p>

<p><strong>Lösung</strong></p>

<p>Die abweichenden Dateien wurden identifiziert, gesichert und durch die entsprechenden Originaldateien aus dem offiziellen EasyBlog-6.0.18-Paket ersetzt.</p>

<p><strong>Ergebnis</strong></p>

<p>Die EasyBlog-Beiträge funktionieren wieder und der <code>nameQuote()</code>-Fehler ist verschwunden.</p>

<hr>

<p><em>Hinweis: Die EasyBlog-Originaldateien stelle ich bewusst nicht zum Download bereit. EasyBlog ist kommerzielle Software. Für diese Reparatur wird ein legitimes Installationspaket der jeweils verwendeten Version benötigt.</em></p>
]]></content:encoded>
      <guid>https://write.medienzentrum.rocks/patches-and-notes-atmertengiesen/joomla-5-4-easyblog-6-0-18-call-to-undefined-method</guid>
      <pubDate>Wed, 16 Sep 2026 12:50:57 +0000</pubDate>
    </item>
  </channel>
</rss>