WordPress 2.6 erlaubt das Auslagern von wp-content

Die Zeiten ändern sich.

Dieser Beitrag scheint älter als 15 Jahre zu sein – eine lange Zeit im Internet. Der Inhalt ist vielleicht veraltet.

Mit der kommenden Version 2.6 von WordPress kommen weitere Möglichkeiten für mehr Sicherheit hinzu. In einem vorhergehenden Beitrag habe ich schon erklärt, dass man die wp-config.php auslagern kann.

Nun führt WordPress drei neue Konstanten ein und das Auslagern des Ordners wp-content inklusiver aller Inhalte ist möglich. Damit wird wesentlich mehr Sicherheit von WordPress gewährleistet, denn die Struktur der am leichtesten anzugreifenden Bausteine, wie Themes und Plugins, kann verdeckt gehalten werden. Damit ist WordPress auch nicht mehr so einfach erkennbar und potentiellen Eindringlingen wird es erschwert.

Dabei sollte aber beachtet werden, dass man mit wenig Kenntnis die Adresse im head des Blog auslesen kann, es sein denn man steckt viel mehr Arbeit in die Änderungen, was dann aber dazu führt, dass man alle Plugins, Themes ändern muss; bei jedem Update ist man ebenso außen vor und bastelt wieder.

Bisher war die Struktur von WordPress wie folgt:


htdocs\
      wp-admin\
      wp-content\
      wp-includes\
      ...

Ab Version 2.6 kann WordPress frei gestaltet werden im Bereich wp-content.


htdocs\
      wp-admin\
      wp-includes\
      wp-mein_content\
      ...

Folgende drei Konstanten können in der wp-config.php definiert werden und erlauben die Auslagerung des jeweiligen Verzeichnisses.


define('WP_CONTENT_DIR', ABSPATH . 'wp-content');    // wp-content Directory

/**
 * Allows for the plugins directory to be moved from the default location.
 *
 * @since 2.6
 */
define('WP_CONTENT_URL', get_option( 'siteurl' ) . '/wp-content');    // wp-content URL
define('WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins');    // Plugin Directory

Ordner umbenennen

Die folgende Beispieldefinition in der wp-config.php geht davon aus, dass der klassische Ordner wp-content nun test heißt und sich im Root des Webspace http://localhost/wpbeta/ befindet.


define( 'WP_CONTENT_DIR', ABSPATH . 'test' );    // wp-content Directory
define( 'WP_CONTENT_URL', 'http://localhost/wpbeta/test' );    // wp-content URL

Wobei die Definition von WP_CONTENT_URL dabei nicht unbedingt erforderlich ist, denn der Ordner bleibt ja am bestehenden Platz.

Wichtig: WordPress lässt es nicht zu, dass man wp-content außerhalb des Root ablegt, dazu muss im Core geändert werden. Ebenso sollte man das nur dann tun, wenn man ausreichend Kenntnisse hat um diverse Plugins oder Themes zu ändern, da viele Entwickler noch immer mit statischen Adressangaben arbeiten und damit wp-content erwarten.

Für Entwickler

Wer beim Entwickeln von Themes und Plugins auf diesen Ordner zugreifen will, der muss also spätestens jetzt auf Konstanten zugreifen.
Schon beim Einbinden eines Sprachfiles kommt es sonst zu Problemen.
War folgender Code bisher noch möglich, …


if ( function_exists( 'load_plugin_textdomain' ) )
	load_plugin_textdomain( 'gettext_name', 'wp-content/plugins/plugin_name/languages' );

so muss es ab 2.6 nun zum Beispiel wie folgt aussehen.


if ( function_exists('load_plugin_textdomain' ) )
	load_plugin_textdomain( 'gettext_name', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );

Prinzipiell sollte der obige Syntax in allen Versionen von WordPress laufen, falls es Probleme gibt, kann man ja die Konstante abfragen.


if ( function_exists( 'load_plugin_textdomain' ) ) {
	if ( ! defined( 'WP_PLUGIN_DIR' ) ) {
		load_plugin_textdomain( 'gettext_name', str_replace( ABSPATH, '', dirname(__FILE__) ) . '/languages' );
	} else {
		load_plugin_textdomain( 'gettext_name', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
	}
}

Hinweise zum Thema finden sich im Changeset 7999 und 7075 des WP-Trac.
Weitere Hinweise zur kommenden Version 2.6 von WordPress gibt es hier im Blog zum Tag WP2.6.

Von Frank Bültge

bueltge.de [by:ltge.de] wird von Frank Bültge geführt, administriert und gestaltet. Alle Inhalte sind persönlich von mir ausgewählt und erstellt, nach bestem Gewissen und Können, was die Möglichkeit von Fehlern nicht ausschließt.

70 Kommentare

  1. Und gleich einen Bug gefunden:
    define( ‚WP_CONTENT_URL‘, get_option(‚home‘) . ‚/wp-content‘);
    Ich musste „home“ durch „siteurl“ ersetzen, weil ich die WordPress-Software in einem Unterverzeichnis und nicht im Domain-Hauptverzeichnis habe. Mit „home“ stimmt der Pfad zur Stylesheetdatei des Themas nicht mehr. Mit dem Patch geht es dann wieder.

  2. Mir ist noch nicht ganz klar, warum sich durch ein Verlagern oder Umbenennen des Verzeichnisses die Sicherheit von WP erhöhen sollte. Im Source der ausgelieferten Seite ist doch nach wie vor der Name des Verzeichnisses im Klartext enthalten, z. Bsp. im Link zum Stylesheet.

    Dafür werden aber jede Menge Themes und Plugins nicht mehr laufen, die das wp-content-Verzeichnis fest verdrahtet haben. Nah, das wird ein schönes Geschrei geben, wenn die ersten Noobs nach dem Erscheinen von WP 2.6 mit dem Verzeichnisnamen herumexperimentieren.

  3. @Lothar Baier: Daher ja der Hinweis, auch in älteren Versionen von WP war die Richtlinie: Nutze Konstanten und Funktionen und verweise nicht auf Verzeichnisse.

    Damit kann man aber das Verzeichnis eine Ebene tiefer legen und so vor dem Zugriff von Außen schützen. Das Umbenennen erschwert nur den Zugriff, weil Scripte erst mit dem Namen gefüttert werden müssen. Allerdings kann man nun einfacher den Zugriff auf Theme, Plugin etc. verstecken.

  4. @André Wegner: Nein, weil dann der Zugriff auf Plugins und Themes trotzdem noch bleibt und so ist, wie es 99% aller WP-User haben. Plugins und Themes sind meist die Sicherheitslücke und wenn man den Ordner wp-content eine Ebene tiefer ablegt, dann ist dieser von Außen nicht erreichbar und so viel sicherer.
    Das Umbenennen ist nicht so sicher, trägt aber zu mehr Sicherheit bei.

  5. Also ich fände es hochgradigst sinnvoll wenn dies mit dem wp-admin passieren könnte. Den Sinn wieso man wp-content verschieben und umbenennen kann versteh ich nicht. Was liegt im wp-content das WordPress angreifbar macht?

  6. Hmm, mit dem Verlagern raus aus der öffentlich erreichbaren Domain wird es aber nicht ohne Patches in den rewrite Rules gehen, CSS oder JS Files aus Plugins oder Themes ausliefern zu lassen. Was mir bei deiner Ausführung fehlt ist die Aussage, ob WP 2.6 das „automatisch“ macht oder ob ich die .htaccess selbst dazu bringen muß.
    Beispiel:

    \www-domain-root\wp-admin
    \ausgelagert-wp-content\plugins

    ausliefern von z.B.: http://www.xyz.de/../ausgelagert-wp-content/plugins/meins/style.css

    Das verhindert ja normal der Server (sinn der Sache). Also wie bringt man dann ein Plugin dazu, ein Stylesheet so auszuliefern ?

  7. @Heiko: alles was in wp-content ist, wird gezogen, automatisch. Themes und Plugins müssen noch immer in diesem Ordner bleiben, wo immer er auch liegt. Im aktuellen Release habe ich noch Fehler gefunden, allerdings ist es ja auch nicht frei gegeben und diese Aktion zeiht viel nach sich in den klassischen Funktionen.
    Mit Plugins konnte man schon immer auf das Verzeichnis Plugins zugreifen mir Konstante PLUGINDIR, alternativ bietet sich __FILE__ an, weil damit das File an sich den Pfad vorgibt. Wenn also die PHP-Datei auf eine style.css dieses Plugins verweist, dann kann man entweder mit PLUGINDIR . '/meins/style.css' zugreifen oder mit str_replace( ABSPATH, '', dirname(__FILE__) . '/style.css' ), siehe auch Text im Beitrag.

  8. @Frank: Okay. Dennoch würde ich meinen das man sehr gut beraten wären wenn
    wp-admin genauso wie wp-content verschoben würde.

  9. @gr4y: mit Sicherheit; es wäre wünschenswert, wenn man alle drei Ordner frei definieren könnte. Das gibt ungemein Freiheit und macht die Administration einfacher und unzugänglicher bei Auftragsarbeiten.

  10. @Frank:

    
    

    Wie soll das gehen, wenn wp-content außerhalb des Domain Root liegt ?
    Dann kommt ja der Browser nicht ran !

  11. @Heiko: Der Zugriff wird über die index.php und load.php im root erlaubt und gelöst. Man kann doch auch die config unterhalb des root halten und die Zugriffe sind möglich. IM Admin-Bereich kümmert sich die Klasse WP_Filesystem_Base um die Zugriffe.

  12. Hi, ich hab im trunk/wp-settings.php das als neu gefunden:

    276 if ( !defined('WP_CONTENT_URL') )
    277 define( 'WP_CONTENT_URL', get_option('home') . '/wp-content'); // full url - WP_CONTENT_DIR is defined further up

    Alles was ich das sehen kann erlaubt nicht, aus dem Domainroot raus zu gehen. Nur innerhalb der Domain kann man das umbenennen und in den 30ten Subpfad des Domainroots packen. Aber keine Chance nach diesen Code Änderungen, das ausserhalb der Domain zu haben!

  13. Frank, zum Thema Sprachdatei schreibst du, dass jetzt der Code str_replace( ABSPATH, "", dirname( __FILE__ ) ) nötig ist. So ähnlich sah mein Code bisher immer aus, um das Verzeichnis des Plugins zu ermitteln. Aber jetzt bei WP 2.6 funktioniert es nicht mehr. Wenn ich nur, wie früher, bei „wp-content/plugins/mein-plugin-ordner“ anfange, wird mein Plugin nicht mehr übersetzt. Die Funktion get_template_directory liefert mir übrigens auch den kompletten Pfad „/home/mathias/…“ usw. zurück.

  14. Mal von den Sicherheitsaspekten abgesehen wäre es generell eine gute Idee, alle WP-Basisverzeichnisse über Konstanten definierbar zu gestelten, auch wp-include. Somit könnte man sehr einfach mehrere WP-Installationen nur durch ein Update der gemeinsam benutzten Basisverzeichnisse auf dem aktuellen Stand halten.

  15. sehr feine funktion.
    habe so einen ähnlichen wunsch schon im entwicklungsforum von wordpress geäußert. finde gut dass jetzt mal mehr in richtung sicherheit hingearbeitet wird.

  16. Sehr komisch, wenn ich deinen Weg nutze, bekomme ich als Ergebnis nur die Ordner ab „wp-content“. Ich habe die Revision 8030 im Test, und ich muss tatsächlich ganz oben anfangen, bei „/home/…/blog/wp-content/…“. Ich musste die Funktion in meinen Plugins umgestalten:
    private static function getplugindirectory( $absolute = false ) {
    $me = str_replace( „\\“, „/“, dirname( __FILE__ ) );
    if ( $absolute === true ) {
    $me = get_settings( „siteurl“ ) . „/“ .
    str_replace( str_replace( „\\“, „/“, ABSPATH ), „“, $me );
    }
    return $me;
    }
    sonst wird die Sprachdatei ignoriert. Ich nehme die Funktion auch zum Laden der Stylesheets (darum der Parameter), und ich habe noch einen Testblog auf einem Windowsserver (darum das Hantieren mit \ und /).

  17. @Mathias: Das Problem ist der Windows-Server. gettext hat damit Probleme, nicht WordPress.

    Bei der Pfadangabe in bindtextdomain() ist zwingend ein abschließender "/" erforderlich (im Bsp. unten also bindtextdomain("test", "./locale/"), getestet mit Apache/2.2.6 (Win32) PHP/5.2.5 unter WinXPSP2). 
    

    siehe phpbar.de

    Habe es nie getestet, aber mal irgendwo aufgeschnappt, eventuell hilft es.

  18. Frank, mein eigener Blog läuft auf einem Linuxserver. Und da funktioniert es nicht, wenn ich den Pfad bei „wp-content“ beginne.

  19. @Mathias: Ich kann nur sagen, dass es unter WP 2.6bleeding bie mir wunderbar klappt, wie oben angegeben. Mehr habe ich nicht drin. Eventuell kannst du mal eines meiner Plugins nutze und testen, eventuell ©Feed, das hatte ich extra eben noch mal in 2.6 aktiv.

  20. Ich glaube dir das, Frank, aber bei mir gehts nicht. Ich habe gerade dein Plugin getestet. Und bei mir läuft auch WP 2.6. Wer weiß, was die Ursache dafür ist. Denk nur mal an meinen zweiten Kommentar bzgl des Bugs in WP. Da scheine ich auch einer der Wenigen zu sein, die das Problem hatten. Der Entwickler hat sich im TRAC dazu gemeldet und gesagt, bei ihm funktioniert der Originalcode (noch mit „home“, nicht mit „siteurl“), obwohl auch seine WP-Dateien in einem Unterverzeichnis liegen.

  21. @Mathias: wenn mein Plugin bei dir auch nicht geht, dann muss es so sein. Konnte im Trac jemand helfen.
    Eventuell sollte man es mal vormerken, wenn das Problem besteht.
    Hast du mal mit dem / gespielt, wie beim Problem mit Win.

  22. Im Trac hatte ich das Problem bisher noch nicht gemeldet. Sollte ich vielleicht mal tun.

    Thema Backslash, Ja. Ich bin in Sachen Programmierung nicht ganz unbedarft (wie du dir vermutlich denken kannst ;)), und auch wenn man es nicht machen sollte, ich habe die Pfade mal fest vorgegeben. Lokal spielt es ohnehin keine Rolle. Mit oder ohne Backslash, es funktioniert nicht, wenn der Pfad bei „wp-content“ beginnt.

    Ich habe eben auch noch mal eine unveränderte Version von WP 2.6 getestet. Bei mir läuft die Aktualisierung über ein Skript, das nebenbei auch ein paar Dateien ändert. Manchmal sind es nur Kleinigkeiten (das Umbenennen der Standardnamen für die Cookies etwa), manchmal sind es bisher noch nicht behobene Fehler (dass das „aktuelle Beiträge“-Widget etwa den $post-Zähler verändert und dadurch andere Widgets, die evtl. die ID des aktuellen Beitrags benötigen nicht mehr richtig funktionieren). Es hätte ja sein können, dass eine von meinen Änderungen für das veränderte Verhalten meines Blogs verantwortlich ist. Aber auch mit den Originaldateien funktioniert es bei mir nicht.

    Ich hatte sogar K2 in Verdacht. Vielleicht kennst du das Thema, und vielleicht weißt du, dass da doch ziemlich viel im Hintergrund passiert. Mit einem der Originalthemen änderte sich jedoch auch nichts.

  23. @Mathias:

    Ja. Ich bin in Sachen Programmierung nicht ganz unbedarft (wie du dir vermutlich denken kannst ;)),

    … darauf kannst du Gift nehmen. Da ich nur im Hobbybereich code, gehe ich immer davon aus, dass Der/Die gegenüber besser ist. Aber ich gebe auch in der Regel nur wieder, was ich getestet habe. Aber mir sind auch schon die unterschiedlichsten Probleme unter gekommen und es war nicht klar warum. Oft schiebe ich es auf den Server, weil ich den nicht beeinflussen kann.
    Eventuell könnte man ja deine lokale Install mal als zip bekommen und dann teste ich sie bei mir im XAMPP, mal sehen, was dann ist.

  24. Ich bin nicht sicher, ob sich das Schicken wirklich lohnt, Frank. Ich habe zwar kein Problem damit, aber wenn ich mir so anschaue, was ich gemacht habe: nichts davon erklärt das merkwürdige Verhalten in Bezug auf die Verzeichnisse.

    Ich habe das Dashboard auf die Kommentare und eingehenden Links reduziert. Oben der Menüheader „Dashboard“ verlinkt bei mir fix und nicht mehr mit „admin.php?page=XY“. Ich habe das Mailkonto umbenannt, weil ich kein wordpress@-Konto habe und anlegen wollte. Wie gesagt, ich habe die Standardcookies in der „wp-settings.php“ umbenannt. Ein paar Smilies sind neu, und mein WP erkennt diese jetzt auch wieder ohne Leer- oder Textendezeichen. Der more-Anker ist weg, so dass man von oben anfängt. Ich habe Autosave und Word-Count deaktiviert. Beim Prüfen auf Pluginupdates wird nicht mehr meine Blogadresse übertragen (du erinnerst dich an die Diskussion?), die Prüfung auf eine neue WP-Version ist abgeschaltet. Ich habe deine Funktion eingefügt, um Login-Fehler zu verschleiern, dafür sind rsd_link, wlwmanifest_link, wp_generator und wp_save_post_revision deaktiviert. Und ich habe den Referer beim Speichern von Beiträgen und Seiten entfernt (post.php, page.php = _wp_original_http_referer). Ich habe nämlich eine „.htaccess“-Regel, die auf „http“ in Parametern recht allergisch reagiert und die Anfragen umleitet. Ein Schutz vor den Bots, die ständig versuchen, mit irgendwelchen externen Dateien meinen Server anzugreifen.

    Puh! Langer Text, sorry. Sieht man von den Änderungen ab, ist es eine ganz normale WP-Installation.

  25. @Mathias: sehe ich auch, so die Eingriffe sollten das Problem nicht tangieren.
    Die meisten Punkte habe ich auch so, allerdings alles per Funktion in Plugins oder Theme-Function.

  26. Kleiner Nachtrag, Frank. In der aktuellen Revision wurde die Konstante WP_PLUGIN_DIR in die Funktion „load_plugin_textdomain“ aufgenommen. Der von dir beschriebene Weg dürfte damit auch nicht mehr funktionieren.

    Meine Funktion sieht jetzt so aus

    private static function getplugindirectory( $absolute = false ) {
    	$me = str_replace( "\\", "/", dirname( __FILE__ ) );
    	if ( defined( "WP_PLUGIN_DIR") and defined( "WP_PLUGIN_URL" ) ) {
    		$me = ltrim( str_replace( str_replace( "\\", "/", WP_PLUGIN_DIR ), "", $me ) );
    		if ( $absolute === true ) {
    			$me = sprintf( "%s/%s", rtrim( WP_PLUGIN_URL, "/" ), $me );
    		}
    	}
    	else {
    		if ( $absolute === true ) {
    			$me = sprintf( "%s/%s", get_option( "siteurl" ), str_replace( str_replace( "\\", "/", ABSPATH ), "", $me ) );
    		}
    	}
    	return $me;
    }
    

    Damit dürfte ich wohl auf der sicheren Seite sein, egal wo die Pfade nun liegen. Stell dir hier trotzdem mein Augenrollen vor.

  27. @Mathias: Habe ich schon bedacht, auch oben erwähnt. Die Lösung klappt aber in meinen Tests überall bestens. Mit der Konstante ist es aber noch schöner wie ich finde.
    Eventuell könnte man ja die Konstante definieren, wenn sie nicht da ist, direkt im Plugin und so sicher sein, dass man auch Versionen kleiner 2.6 unterstützt.

    if ( !defined('WP_PLUGIN_DIR') )
    	define( 'WP_PLUGIN_DIR', '/wp-content/plugins' );
    
  28. Ich widerspreche ungern, Frank, aber das kann nicht funktionieren. Oder ich habe gerade einen Blackout der üblen Sorte. 🙂 Beispiel: Sagen wir ABSPATH ist „/home/mathias/wordpress/“. Standardmäßig setzt sich WP_PLUGIN_DIR zusammen aus ABSPATH „wp-content“ (= WP_CONTENT_DIR) „/plugins“. Mit

    str_replace( ABSPATH, "", dirname( __FILE__ ) )

    entfernst du aber nur ABSPATH. Es bleiben also „wp-content/plugins“ und ggf. der Ordner deines Plugins übrig. Und jetzt gucke in die Datei „l10n.php“; dort steht nämlich:

    $mofile = WP_PLUGIN_DIR . $path . '/'. $domain . '-' . $locale . '.mo';

    WP_PLUGIN_DIR enthält aber den kompletten Pfad bis einschließlich „wp-content/plugins“. Daran wird jetzt dein Pfad gehangen, der ja auch noch mal „wp-content/plugins“ enthält. Rein logisch stimmt da der Pfad schon nicht mehr, und die Sprachdatei wird nicht gefunden werden.

    Die Idee mit der Konstante klingt aber ganz gut.

  29. @Mathias: habe mich der Sache nochmal angenommen und den obigen Syntax ergänzt, diese Lösung läuft bei mir in allen getesteten Versionen. Zusätzlich sieh Link zum Trac. Allerdings sollte der load mittels

    load_plugin_textdomain('copyrightfeed', dirname(plugin_basename(__FILE__)) . '/languages');
    

    in allen Version von WP laufen, wenn ich den Trac richtig verstehe, macht er bei mir nicht. Daher frage ich die Konstante ab und lade dementsprechend.

  30. Das Ticket #7075, oder besser gesagt: die letzte Antwort darin, bezieht sich auf den 2.6er-Trunk und funktioniert auch nur dort. Aus den von mir erwähnten Gründen beim „Zusammenbau“ des Pfades. An sich relevanter dürfte das Changeset 8041, da siehst du nämlich auch die von mir erwähnte Änderung in der „l10n.php“.

    Man muss also wirklich künftig prüfen, ob die Konstanten existieren, sonst laufen die Plugins nicht mehr richtig.

Kommentare sind geschlossen.