WordPress: Plugin ohne Dashboard deaktivieren

Muss man in WordPress ein Plugin ohne Dashboard, also über die WordPress-Oberfläche deaktivieren? Was man schnell verneinen möchte, hat einen ernsten Hintergrund: Wenn ein Security-Plugin dem Admin das Login verweigert, kann das Entfernen des Plugins über die Datenbank die letzte Hoffnung sein.

Normalerweise kommt man nicht in die Versuchung ein Plugin ohne das WordPress-eigene Dashboard zu installieren oder deinstallieren. Zu einfach ins dies – nur wenige Klicks genügen. Dennoch war dies die einzige Hoffnung, eine Kundeninstallation vor WordPress schnell wieder zugänglich zu machen.

Plugin-Limit-Attempts
Das Plugin „Limit Attempts“ von BestWebSoft sperrte den Admin vom Backend aus. Das WordPress-Plugin musste also über die Datenbank deaktiviert werden. Als bisher stabil zeigt sich das Plugin „Limit Login Attempts“ von Johann Eanfeldt.

Folgendes war passiert:
Ich persönlich bei kein Freund von Plugins wie „Limit Login Attempts“. Diese Plugin verhindert, dass jemand durch mehrmaliges probieren sich Zugang über die WordPress-Login-Box verschafft. Nach drei Fehlversuchen wird die IP für einen gewissen Zeitraum gesperrt. Wenn jemand wirklich ein Brute-Force Attacke auf einen WP-Blog starten möchte, wird er sicherlich wechselnde Proxy-Server nutzen. Der bessere Ansatz ist, dass die Login-Box erst gar nicht einem Hacker zur Verfügung steht. Ansatzpunkte zum besseren Schutz der WordPress Installation habe ich in meinem Artikel „WordPress absichern: 10 Tipps für ein sicheres WP“ beschrieben.

Doch zum vorliegenden Fall zurück. Das „Limit Login Attempts“-Plugin von Johan Eanfeldt wird meist auch über die WordPress-CPanel-Installationen mit angeboten. Das Plugin ist meiner Meinung nach stabil und schadet nicht. Über seinen Nutzen kann man auf jeden Fall diskutieren. Im vorliegenden Fall war jedoch nicht das Plugin von Johan Eanfeldt, sondern das Plugin „Limit Attempts“ von „BestWebSoft“ installiert gewesen. Und nach der Installation machte das Plugin den Zugang absolut sicher: Auch der Admin hatte keinen Zugriff mehr auf das Dashboard. Ohne Fehlermeldung (im entsprechenden HTML-Container war kein Hinweis ersichtlich) blockierte das Plugin den WP-Zugang. Das Passwort war richtig. Auch eine Passwort-Änderung brachte kein Erfolg. Ebenso wenig das Hinzufügen eines neuen Admins über die Datenbank: Das Plugin „Limit Attempts“ sperrte jeden Benutzer aus.

Es war zu diesem Zeitpunkt nicht sicher, dass es an diesem Plugin lag. Doch der Verdacht lag nahe und eine Lösung musste her, das Plugin zu deaktivieren und wieder einen Zugriff auf das Backend zu gewähren. Die Idee, das Plugin per FTP einfach zu löschen, habe ich verworfen. Denkbar, dass dies auch zum Erfolg geführt hätte: Man müsste dies einmal mit einer Testinstallation ausprobieren. Sauberer Weg ist sicherlich das Deaktivieren des Plugins. Und da der Zugriff auf die Datenbank gegeben war (hier zeigt sich, dass es sehr wichtig ist, auch die Sicherheitsaspekte rund um die Datenbank im Auge zu behalten!), konnte das Plugin deaktiviert werden.

Vorgehen zum Entfernen eines WordPress Plugins per Datenbank

  1. Mit phpmyadmin einen Login auf die Datenbank herstellen und einen Sicherheits-Dump der gesamten Datenbank erstellen
  2. Die Tabelle „wp_options“ öffnen und die Zeile mit dem `option_name` „active_plugins“ suchen (wenn die Datenbank einen anderen Suffix hat, die Tabelle mit der Endung „_options“ suchen):
    SELECT * FROM `wp_options` WHERE `option_name` = ‚active_plugins‘
  3. Den Inhalt von `option_value` in ein Textpad oder Notepad kopieren (2x; einmal zur Sicherung, einmal zur Manipulation)
  4. Den String wie untenstehend manipulieren (wenn einzelne Plugins deaktiviert werden sollen). Um alle Plugins zu deaktivieren, kann der Inhalt in ‚option_value‘ einfach geleert werden.
  5. Den manipulierten String wieder zurückschreiben

Bei dem Textwert im Feld `option_value` handelt es sich um Serialize-Array in PHP http://php.net/manual/de/function.serialize.php

Etwas leserlicher dargestellt sieht der Wert so aus:

a:5:
{
i:0;s:41:"better-wp-security/better-wp-security.php";
i:1;s:33:"limit-attempts/limit-attempts.php";
i:2;s:23:"ssclassic/functions.php";
i:3;s:34:"wp-clone-by-wp-academy/wpclone.php";
i:4;s:31:"wp-permalauts/wp-permalauts.php";
}

Da ich im Versuch auch das Plugin “Better-WP-Security“ erst einmal deaktivieren wollte, habe ich beide Plugins erst einmal deaktiviert. Folgende Veränderungen wurden vorgenommen:

  1. „a:5:“ wurde zu „a:3“ -> es sind nur noch drei „Zeilen“ = Plugins im Array
  2. Die Zeilen „i:0“ und „i:1“ habe ich gelöscht (es verbleiben drei Zeilen)
  3. Die Werte der nachfolgenden Zeilen „i:X“ wurden angepasst
  4. Dann wurde der String wieder in eine Zeile gekürzt (Returns gelöscht)
a:3:
{
i:0;s:23:"ssclassic/functions.php";
i:1;s:34:"wp-clone-by-wp-academy/wpclone.php";
i:2;s:31:"wp-permalauts/wp-permalauts.php";
}

Mit dem Neuaufruf der WP-Login-Maske konnte ich mich wieder als Admin anmelden. Beide herausgenommenen Plugins waren nun im Dashboard als „deaktiviert“ ersichtlich. Das Plugin „WP-Better-Sercurity“ habe ich wieder aktiviert. Das Plugin „Limit Attempts“ habe ich gelöscht und durch das Plugin „Limit Login Attempts“ ersetzt.

Mit Captcha gegen WordPress Kommentar Spam

Die Kommentarfunktion gehört als elementarer Bestandteil zu WordPress und vielen weiteren Content Management Systemen (CMS). Doch leider ist der Webmaster gezwungen, diese schon bei der Installation zu deaktivieren, da er sonst tagtäglich hunderte, wenn nicht gar tausende Spam-Kommentare erhält. Leider keine Übertreibung. Harter WordPress-Alltag eines WordPress Admins.

klassisches-wordpress-captcha
Kommentar-Spam: Mittels Captcha-Breaker können auch solche Captchas automatisiert überwunden werden.

Auch auf diesem Blog habe ich die Kommentarfunktion zuerst deaktiviert. Doch von Anfang an fand ich es schade, dass Leser keinen Kommentar hinterlassen können. Auf anderen von mir betreuten Blogs habe ich die Kommentarfunktion aktiviert und das Absenden der Kommentare per WordPress Plugin „SI CAPTCHA Anti-Spam“ ermöglicht. Der Erfolg ist mäßig. Es erreichen je nach Blog einem sinnvolle Kommentare, doch das Spam-Aufkommen sinkt nicht in einen akzeptablen Bereich. Ich selbst ging davon aus, dass Captchas durchaus das Aufkommen minimieren (das glaube ich nach wie vor) und war mir sicher, dass ein (ScrapeBox)-Spammer die Kosten für den Captcha-Brecher scheuen. Musste jedoch eines besseren belehrt werden. Vor ein paar Tagen lief wohl einem Spammer sein Skript Amok und hinterließ auf einer von mir betreuten Seite mit diesem WordPress Plugin mal über 1.000 Spam-Kommentare in ein paar Stunden.

Auf sirmark.de habe ich dann die Kommentarfunktion von mood.it getestet. Positiv: Kein einziger Spam-Kommentar im Testzeitraum. Negativ: Kein einziger Kommentar überhaupt. Es zeigt sich also, dass mood.it in Deutschland noch zu wenig bekannt ist und sich jemand für einen sinnvollen Kommentar nicht unbedingt erst noch irgendwo extern registriert. Zudem hat diese Lösung einen ganz entscheidenden Nachteil: Bei den Kommentaren geht es auch darum, dass der Content einer Seite durch neuen Text „aufgefrischt“ und vielleicht sogar noch durch wichtige Keywords ergänzt wird. Diesen Effekt verbaut man sich, wenn man eine externe Lösung einsetzt.

Nach diesen Erfahrungen werde ich seit heute eine neue Lösung hier testen. Das WordPress Plugin heißt einfach „Captcha“, liegt aktuell in der Version 3.7.1 vor und erzeugt kein Text, der abzutippen ist, sondern eine simple Rechenaufgabe. Ich erhoffe mir, auch nach dem Lesen einiger Erfahrungsberichte , dass diverse Übersee-Pillenverkäufer über ein deutsches Text-Captcha stolpern. Ob dies der Fall sein wird – wir werden sehen.

Akzeptanz der Captchas

Oftmals liest man, dass es negativ sei Captchas einzusetzen, da so die Akzeptanz der Kommentarschreiber leidet. Ich persönlich behaupte, dass gerade das Gegenteil eintritt (zumindest bei mir persönlich). Wenn ich auf einer Seite einen Kommentar hinterlasse möchte, bin ich bei einer offenen Kommentarfunktion immer sehr skeptisch. Da ich abschätzen kann, wie viel Spam-Kommentar der Webmaster erhalten dürfte, gehe ich davon aus, dass mein Kommentar in der Masse verschwindet und nie veröffentlicht wird. Ist ein Captcha eingesetzt, deutet dies zumindest darauf hin, dass der Webmaster sich mit der Problematik auseinandergesetzt hat. Die Chance, dass mein Kommentar veröffentlich wird, steigt meines Erachtens.

WordPress: Mit PHP eigenes Widget (Plugin) erstellen

Auch wenn man WordPress seit vielen Jahren nutzt, kommt man nur selten in die Verlegenheit, ein Plugin selbst zu programmieren. Auch wenn es einem oftmals in den Fingern juckt und man kurz davor ist, ein eigenes Widget zu erstellen, zeigt einem eine kurze Suche, dass es bereits eine fertige und kostenlose Lösung gibt. Warum dann also selbst programmieren?
Nach vielen Jahren der WordPress-Nutzung kam ich vor wenigen Tagen zum ersten Mal an eine Stelle, an der ich ein eigenes Widget programmiert habe. Vielleicht hätte nach einer längeren Suche sich eine fertige Lösung ergeben, doch da es – wie immer – mal wieder schnell, schnell gehen musste, griff ich in die Tasten. Im vorliegenden Fall musste ein fertiges Theme genutzt werden, in dessen Footer-Bereich mehrspaltig eine Datenbankabfrage angezeigt werden sollte. Jede Spalte wurde vom Theme mit einem eigenen Widget bedient. Im Kundenfall jedoch sollten in allen drei Spalten nicht unterschiedliche Abfragen, sondern eine Abfrage verteilt auf drei Spalten angezeigt werden.
Wordpress-Plugin-selbst-programmiertDie einfachste wie auch schnellste Lösung wäre sicherlich der Eingriff in das (fremde) Theme gewesen, was aber die Wartbarkeit bzw. das Update des Themes erschwert hätte. So lautete der Auftrag, dies per Widget zu lösen.
Untenstehend zeige ich Euch eine Basisklasse „MyTestWidget“, die als Layout für die meisten Plugins dienen kann. Geht folgendermaßen beim Erstellen vor:

  1. Kopiert den Code in ein Textpad, speichert den Code als [mein-widget-Name].php
  2. Passt im Code die Stellen „MyTest Widget“ und „My_Test_Widget“ entsprechend an
  3. Kopiert den Code in den WordPress-Ordner „/wp-content/plugins“. Im WordPress Backend sollte nun unter „Plugins“ Euer Plugin auftauchen. Wenn Ihr es aktiviert, erscheint auf der Layout/Widgets Seite das Widget, welches Ihr in den entsprechenden Widget-Area ziehen könnt.
  4. widget-einstellungNach diesem kurzen Test müsst Ihr natürlich das Plugin entsprechend anpassen. In der Funktion „public function form($instance)“ werden die Parameter abgehandelt, die im Widget-Bereich vom Admin gesetzt werden sollen. Im Code findet Ihr die Variablen ‚footer-books-title‘ , ‚footer-books-start‘  und ‚footer-books-ende‘ . Passt hier Eure Angaben an. Ein Beispielcode, wie mit einer HTML-Texteingabe umzugehen ist, findet Ihr auskommentiert im Code.
  5. Die Eingaben werden im Plugin auf Richtigkeit geprüft und in der WordPress-Datenbank gespeichert. Diese Stellen sind ebenfalls anzupassen.
  6. Nun wird es endlich spannend. In der Funktion „public function widget($args, $instance)“ erfolgt endlich die Ausgabe des Widgets. Im Code könnt Ihr sehen, wie man auf die Widget-Parameter und die Datenbank zugreift. Der Beispielcode hier macht relativ wenig; er dient nur als Demonstration.
  7. Und das war´s auch schon. Die restlichen Funktionen dienen der Registrierung etc. Weitere Infos findet Ihr wie immer unter wordpress.org
<?php
/**
 * Plugin Name: MyTest Widget
 * Plugin URI: http://sirmark.de
 * Description: Basis-Code für ein WordPress - Widget
 * Version: 1.0
 * Author: sirmark
 * Author URI: http://sirmark.de
 */
if(!class_exists('My_Test_Widget')) {
    /**
     * Class My_Test_Widget
     * Aufbau eines Test Widgets
     */
    class My_Test_Widget extends WP_Widget {
        private $var_sTextdomain; // Variable Textdomain: Übersetzungen des Widgets

        /**
         * Konstruktorfunktion (PHP4).
         *
         * Diese Funktion muss den gleichen Namen haben, wie das Klasse des Widgets.
         * Somit wird diese Funktion direkt nach dem Initialisieren der Klasse
         * aufgerufen und ausgeführt. Da dies der Konstruktor für PHP4 ist wird durch diesen
         * direkt die Konstruktor-Funktion für PHP5 aufgerufen.
         *
         * @see My_Test_Widget::__construct()
         */
        public function My_Test_Widget() {
            My_Test_Widget::__construct();
        } // END function My_Test_Widget()

        /**
         * Konstruktorfunktion (PHP5).
         *
         * @param array $widget_options Optional Passed to wp_register_sidebar_widget()
         *   - description: shown on the configuration page
         *   - classname
         * @param array $control_options Optional Passed to wp_register_widget_control()
         *   - width: required if more than 250px
         *   - height: currently not used but may be needed in the future
         */
        public function __construct() {
            $this->var_sTextdomain = 'my-Test-widget';

            /**
             * Übersetzungsfunktion für das Widget aktivieren.
             * Die Sprachdateien liegen im Ordner "l10n" innerhalb des Widgets.
             */
            if(function_exists('load_plugin_textdomain')) {
                load_plugin_textdomain($this->var_sTextdomain, PLUGINDIR . '/' . dirname(plugin_basename(__FILE__)) . '/l10n', dirname(plugin_basename(__FILE__)) . '/l10n');
            }

            $widget_options = array(
                'classname' => 'My_Test_Widget',
                'description' => __('Kurze Beschreibung des Widgets.', $this->var_sTextdomain)
            );

            $control_options = array();

            $this->WP_Widget('My_Test_Widget', __('My TestWidget', $this->var_sTextdomain), $widget_options, $control_options);
        } // END function __construct()

        /**
         * Widgetformular – dient zur Einstellung des Widgets
         *
         * @see WP_Widget::form()
         */
        public function form($instance) {
            /**
             * Standardwerte
             *
             * @var array
             */
            $instance = wp_parse_args((array) $instance, array(
                'footer-books-title' => '',
                'footer-books-start' => '0',
                'footer-books-ende' => '5'
            ));

            // Titel
            echo '<p style="border-bottom: 1px solid #DFDFDF;"><strong>' . __('Title', $this->var_sTextdomain) . '</strong></p>';
            echo '<p><input id="' . $this->get_field_id('footer-books-title') . '" name="' . $this->get_field_name('footer-books-title') . '" type="text" value="' . $instance['footer-books-title'] . '" /></p>';
            echo '<p style="clear:both;"></p>';

            ?>
            <p>
                <label for="<?php echo $this->get_field_id('footer-books-start'); ?>"><?php echo 'Start:'; ?></label>
                <input id="<?php echo $this->get_field_id('footer-books-start'); ?>" name="<?php echo $this->get_field_name('footer-books-start'); ?>" type="text" value="<?php echo $instance['footer-books-start']; ?>" />
            </p>
            <p>
                <label for="<?php echo $this->get_field_id('footer-books-ende'); ?>"><?php echo 'Anzahl:'; ?></label>
                <input id="<?php echo $this->get_field_id('footer-books-ende'); ?>" name="<?php echo $this->get_field_name('footer-books-ende'); ?>" type="text" value="<?php echo $instance['footer-books-ende']; ?>" />
            </p>            
            <?php

            // Beispielcode für ein Textfeld
           // echo '<p style="border-bottom: 1px solid #DFDFDF;"><strong>' . __('Textarea:', $this->var_sTextdomain) . '</strong></p>';
           // echo '<p><span style="display:inline-block;">' . __('Write some text here ...', $this->var_sTextdomain) . '</span><textarea style="width:100%;" id="' . $this->get_field_id('my-widget-text') . '" rows="10" name="' . $this->get_field_name('my-widget-text') . '">' . $instance['my-widget-text'] . '</textarea></p>';
           // echo '<p style="clear:both;"></p>';
        } // END function form($instance)

        /**
         * Widgeteinstellungen in die Datenbank schreiben.
         *
         * @see WP_Widget::update()
         */
        public function update($new_instance, $old_instance) {
            $instance = $old_instance;

            /**
             * Standardwerte setzen
             *
             * @var array
             */
            $new_instance = wp_parse_args((array) $new_instance, array(
                'footer-books-title' => '',
                'footer-books-start' => '0',
                'footer-books-ende' => '5'
            ));

            /**
             * Die über das Formular getätigten Eingaben auf Richtigkeit und Schadcode prüfen
             *
             * @var array
             */
            $instance['footer-books-title'] = (string) strip_tags($new_instance['footer-books-title']);
            $instance['footer-books-start'] = (string) strip_tags($new_instance['footer-books-start']);
            $instance['footer-books-ende'] = (string) strip_tags($new_instance['footer-books-ende']);

            /**
             * Nun kann ein Array mit den Einstellungen an die verarbeitende Funktion
             * zurückgeliefert werden (Klasse WP_Widget). Die Einstellungen werden in der WP-Datenbank
             * gespeichert.
             */
            return $instance;
        } // END function update($new_instance, $old_instance)

        /**
         * Ausgabe des Widgets im Frontend.
         *
         * @see WP_Widget::widget()
         */
        public function widget($args, $instance) {
            extract($args);

            echo $before_widget;

            $title = (empty($instance['footer-books-title'])) ? '' : apply_filters('footer_books_title', $instance['footer-books-title']);

            if(!empty($title)) {
                echo $before_title . $title . $after_title;
            } // END if(!empty($title))

            // HIER nur sinnloser Beispielcode
            // Bereich für die eigentliche Tätigkeit des Widgets
            global $wpdb;

            //Beispiel für ein SQL-Statement
            $results = $wpdb->get_results("
                SELECT a.post_name, post_title
                FROM `wp_posts` AS a
                WHERE a.post_status = 'publish'
                ORDER BY post_date DESC
                LIMIT ".$instance['footer-books-start'].",".$instance['footer-books-ende']
            );

            $output = '<ul>';

            foreach($results as $row)
            {
                //Ausgabe von $row->post_name
            }                

            echo $output . '</ul>';

            echo $after_widget;
        } // END function widget($args, $instance)

        /**
         * HTML des Widgets
         *
         * @param array $args
         */
        private function my_widget_html_output($args = array()) {
            /**
             * Widgetausgabe
             * Hier wird nun das HTML für das Widget erstellt
             */
            $var_sWidetHTML = wpautop($args['my-widget-text'], true);

            return $var_sWidetHTML;
        } // private function my_widget_html_output($args = array())
    } // END class My_Test_Widget extends WP_Widget

    /**
     * Widget initialisieren.
     */
    add_action('widgets_init', create_function('', 'return register_widget("My_Test_Widget");'));
} // END if(!class_exists('My_Test_Widget'))

WordPress absichern: 10 Tipps für ein sicheres WP – Tipp 6-10

<< Zum ersten Teil des Artikels „WordPress absichern: 10 Tipps für ein sicheres WP“ (Tipp 1-5)

6. “wp-config.php” verschieben oder Zugriff zur “wp-config.php” verhindern

ordnerverzeichnis wordpress
Wenn es der Hoster erlaubt, sollte die wp-config.php ausserhalb des www-Ordners liegen

Die obenstehenden Punkte sind alle schön und gut, was aber, wenn ein Hacker es schafft, die Datei „wp-config.php“ als Textfile zu öffnen? Hier stehen beispielsweise die Datenbank-Passwörter im Klartext, was mehr als unschön ist. Zumal jedem Hacker bekannt ist, dass bei einer Standard-Installation diese Datei im Root liegt.

Eine nette Möglichkeit ist, diese Config-Datei aus dem Root in ein Verzeichnis unterhalb des Root-Verzeichnis zu verschieben. WordPress kann auch auf solche Verzeichnisse ohne Anpassung zugreifen. Wenn das Root-Verzeichnis beispielsweise „meineDomain_de/httpdocs“ heißt und der Hoster eine Dateiablage unterhalb des „www“-Verzeichnisses erlaubt, sollte man die wp-config.php aus „meineDomain_de/httpdocs“ in das Verzeichnis „meineDomain_de“ verschieben. Die meisten Hoster erlauben jedoch einen Zugriff unterhalb des eigentlichen Web-Verzeichnises nicht. Für diese Installationen bleibt die Möglichkeit, den Zugriff auf die Datei noch weiter per .htaccess-Datei zu verhindern. In der zentralen .htaccess-Datei (oder vhosts – siehe Punkt 10) einfach das nachfolgende Code-Fragment hinzufügen.

<files wp-config.php>
Order deny,allow
deny from all
</files>

7. Admin-Zugriff nur per SSL erlauben

Sofern der Hoster einen SSL-Zugang ermöglicht, sollte dies auf jeden Fall auch genutzt werden. Denn wenn der Admin Zugriff SSL gesichert erfolgt, werden die Benutzerdaten nur noch verschlüsselt übertragen. Um dies WordPress-seitig zu aktivieren, ist in der wp-config.php lediglich die nachfolgende Zeile einzutragen:

define('FORCE_SSL_ADMIN', true);

8. Admin-Zugang doppelt schützen: wp-admin mittels htaccess schützen

Klingt dies paranoid? Ein wenig vielleicht, doch es macht Sinn ein zweites Schloss an den Administrations-Zugang zu hängen: Wir schützen das WordPress Backend noch zusätzlich über eine vorgeschaltete Passwortabfrage per htaccess. Von Tipps, WordPress per Plugin noch weiter anzusichern, halte ich persönlich wenig. Vergleichen wir es mal mit einem Einbruch in einem Haus. Durch ein sicheres Passwort und einem Administratoren-Konto, welches nicht “admin“ lautet, haben wir schon einmal eine sehr gutes Schloss an der Tür. Wenn wir nun per Plugin fehlerhafte Login-Versuche protokollieren ist das in etwa so, als ob wir im Haus selbst einen Wächter stellen. Das ist sicherlich gut, doch sinnvoller wäre es, der Wächter kontrolliert außerhalb des Hauses einen Einbruchversuch. Und hier hilft eine vorgeschaltete Passwortabfrage per htaccess. Also um beim Beispiel zu bleiben: Gitter am Fenster und der Tür …

Man liest derzeit sehr oft, dass man das „wp-admin“-Verzeichnis per htaccess schützen soll. Ich rate Euch: Macht dies so nicht! WordPress greift intern auch auf Files in diesem Ordner zu und je nach Konstellation kann es sein, dass Eure WordPress-Installation so in einen Fehler läuft. Die Lösung ist per htaccess nicht das Verzeichnis, sondern die Datei „wp-login.php“ per htaccess zu sichern. Im nachfolgenden Codefragment ist der notwenige Eintrag in der htaccess abgebildet.

<Files wp-login.php>
  AuthName "WP-Admin-Bereich"
  AuthType Basic
  AuthUserFile /euer/pfad/zur/.htpasswd
  require valid-user
</Files>

Da es immer wieder ein Thema ist – hier kurz erklärt, wie man eine htacces und htpasswd Datei anlegt:

  1. Man erstellt mit einem einfachen Texteditor (notepad.exe – NICHT Microsoft Word oder Wordpad!) eine Datei und kopiert den obigen Code hinein. Lokal abspeichern unter „htaccess“ (ohne Punkt)
  2. Um die htpasswd zu erstellen, kann man auf diesen Passwortgenerator zurückgreifen. Vergebt einen Usernamen und Passwort, klickt „create“ und ihr erhaltet eine Zeile die dieser ähnelt: „blubb:$apr1$Nj4a6My4$Isb1wtXV8AlmIl8PgfiwV.“ Vergebt bitte ein vom eigentlichen WordPress-Admin Konto abweichender User und Passwort (doppelter Passwort-Schutz). Diese Zeile wird in einer leeren Datei unter „htpasswd“ gespeichert.
  3. In der Datei „htaccess“ muss nun der Pfad zur „.htpasswd“ angepasst werden („AuthUserFile /euer/pfad/zur/.htpasswd“). Dies aus Sicht des Webservers! Hilfreich ist dieser Artikel.
  4. Beide Dateien (htpasswd und htaccess) werden nun in den Ordner „wp-admin“ geladen (ASC-II-Modus!) und jeweils umbenannt, dass die Dateien „.htaccess“ und „.htpasswd“ lauten.
  5. Nun sollte dem Aufruf „deine-Domain.de/wp-admin“ ein Passwort-Dialog vorgeschaltet sein.

9. WordPress: Generator, Versionsnummer und readme.html verstecken

WordPress › liesmich
Auch die im Root befindliche liesmich.html oder readme.html verrät die installierte Programmversion

Durch ständige sicherheitsrelevante Updates gibt es als WordPress Admin eigentlich jede Woche etwas zu erledigen. Der folgende Tipp erhöht nicht die Sicherheit, er kann jedoch nur eine Angriffsfläche minimieren. Um es deutlich zu sagen: Man kann das Folgende ausführen, darf sich dadurch nicht in falscher Sicherheit wiegen!

Wenn eine Sicherheitslücke bekannt wird und eine neue Version veröffentlich wird, gibt es zeitgleich auch genügend Bots, die speziell nach diesen abgelösten Versionen suchen.

WordPress selbst schreibt im HTML-Code (meta) die Versionsnummer der WordPress-Installation. Eine einfache Möglichkeit für einen Bot ist, diese auszulesen und bei bekannten Sicherheitslücken dann die Seite zu attackieren. Kein Schutz im eigentlichen Sinne, aber vielleicht eine Möglichkeit einem Bot die eigene Seite nicht auf dem Silbertablett zu präsentieren ist die Möglichkeit, die WordPress-Generator Angabe zu entfernen.

Hierzu muss einfach in der functions.php der nachfolgende Code hinzugefügt werden:

remove_action('wp_head', 'wp_generator');

Allerdings sollte auch die Datei liesmich.html und readme.html aus dem root entfernt werden, da hier ebenfalls die Programmversion ersichtlich ist. Da diese jedoch bei neuen WordPress Versionen erneut in das Root kopiert werden, kann ein Zugriff auf diese Dateien (wie auch auf die Datei wp-config.php aus Punkt 6) verhindert werden:

<FilesMatch "(.htaccess|.htpasswd|wp-config.php|liesmich.html|readme.html)">
order deny, 
allow deny from all 
</FilesMatch>

10. Meine vhost-Datei (Plesk > 10) für einen WordPress Blog

Auch basieren auf meinen Artikel „Ranking Faktor Page Speed“ sieht eine vhosts-Datei in meinem Fall wie nachfolgend aus. Dieser Code kann auch (mit eventuellen Anpassungen) in einer zentralen htaccess-Datei im Root einer gehosteten WordPress-Blogs eingesetzt werden:

<Directory /var/www/vhosts/DOMAIN.de/httpdocs>
Options +FollowSymLinks
Options –Indexes
php_admin_flag engine on
php_admin_flag safe_mode off
<FilesMatch "(.htaccess|.htpasswd|wp-config.php|liesmich.html|readme.html)">
Order deny,allow
deny from all
</FilesMatch>
<Files wp-login.php>
  AuthName "WP-Admin-Bereich"
  AuthType Basic
  AuthUserFile /euer/pfad/zur/.htpasswd
  require valid-user
</Files>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/xml text/css text/plain
AddOutputFilterByType DEFLATE image/svg+xml application/xhtml+xml application/xml
AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml
AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript
AddOutputFilterByType DEFLATE application/x-font-ttf application/x-font-otf
AddOutputFilterByType DEFLATE font/truetype font/opentype
</IfModule>
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
</Directory>

Abschließend bleibt der Hinweis, dass jeder WordPress-Admin darauf zu achten hat, dass er die WP-Installation sowie alle verwendeten Plugins stets aktuell hält. Um es wieder mit einem zu sichernden Haus zu beschreiben: Es macht keinen Sinn, die Tür optimal abzusichern, wenn ein Fenster ebenerdig stets gekippt ist ….

WordPress absichern: 10 Tipps für ein sicheres WP

Aufgeschreckt durch Meldungen bei heise.de oder spiegel.de versuchen der Tage alle WordPress-Administratoren ihr WordPress-CMS abzusichern. Aus diesem Grund möchte ich hier meinen Leitfaden veröffentlichen, den ich bei einer Neuinstallation einer WP-Instanz anwende. Ich versuche aus aktuellem Anlass auch darauf einzugehen, eine bestehende WordPress-Installation nachträglich abzusichern. Beachten Sie bitte, dass die aktuelle Problematik „wp-admin“ per htaccess abzusichern erst im Punkt 8 ausführlich erläutert wird.

1. Kein Administrator namens „admin“

Zuerst sollte jeder WP-Admin sich in WordPress einloggen und unter Benutzer die Konten prüfen. Viele Administratoren gibt es? Sind es plötzlich mehr? Wenn ja, dürfte ein Angriff bereits stattgefunden haben. Wenn nein, sind nach Möglichkeit die Anzahl der Administratoren auf ein Minimum zu reduzieren. Auch zu prüfen: Gibt es einen Administrator namens „admin“? Wenn ja, ist dieser unbedingt zu ändern! Genau darauf bau der aktuelle Angriff: Wenn bekannt ist, dass das Administrationskonto „admin“ lautet, ist dies schon die halbe Miete. Nun muss nur noch das Passwort per Brute Force ermittelt werden.
Also unbedingt den Benutzernamen des „admin“ in einen individuellen Benutzer-Namen ändern. Dass alle Passwörter auch entsprechend lang und kryptisch sein sollen, ist hoffentlich selbstverständlich.

2. Ein Administrator veröffentlicht keine Artikel. NIE!

Warum? WordPress hinterlässt bei jedem Artikel den Namen des Autors. Dies ist sehr sinnvoll aus vielerlei Gründen. Aus Sicherheitsgründen ist dies jedoch eine Katastrophe, da darüber in den meisten Fällen auch der Benutzername bekannt ist. Somit muss nur noch dem passenden Passwort gesucht werden. Hat dieser Benutzer auch noch Admin-Rechte, ist der Blog schnell in fremder Hand.

3. Die ID des Administrators ändern

Bei der Installation des Blogs wird automatisch das Administrationskonto angelegt und erhält in der Datenbank die User-ID 1. Dies ist ein weiterer Ansatzpunkt beim Hacken einer WordPress Installation. Die ID ist nachträglich mit nachfolgenden SQL-Statements änderbar:

UPDATE wp_users SET ID = '[NEUE ID]' WHERE wp_users.ID = 1;
UPDATE wp_usermeta SET user_id = '[NEUE ID]' WHERE wp_usermeta.user_id = 1;
UPDATE wp_posts SET post_author = '[NEUE ID]' WHERE wp_posts.post_author = 1;
UPDATE wp_links SET link_owner = '[NEUE ID]' WHERE wp_links.link_owner = 1;

Nachdem so die Sicherheit für das Administrations-Konto erhöht wurde, sollte man sich nun an die „wp-config.php“ machen. Diese Datei liegt gewöhnlich im Root der Installation.

4. Prüfen, ob die Sicherheitsschlüssel in der „wp-config.php“ gesetzt sind

Mit wenig Aufwand kann hier die Sicherheit ein gutes Stück erhöht werden. In den Abschnitt „Sicherheitsschlüssel“ gehören zu jedem Define-Block ein „gehörig gesalzener Key“. WordPress macht es sehr einfach. Der Aufruf unter https://api.wordpress.org/secret-key/1.1/salt/ über den Browser erlaubt die Generierung der Keys. Diese Ausgabe im Broser kann einfach via Copy & Paste in die wp-config.php übernommen (bzw. überschieben) werden.

define('AUTH_KEY', '%Sc#(H|Zo5p+wn=>i.yM*!k%w8[zh9K{d5-v2>`:^>M%B:(tr{2;VkGMxTE+NFD)');
define('SECURE_AUTH_KEY', 'ZXsorW!Z_6qi/PC^tDUUftZo*sQ6Ej!MzW9,Ze3z6>W?H+G}*$<iddH/a#Z7+&EO');
define('LOGGED_IN_KEY', 's@X$cZ(*.wCI1tU.Lw$|Ion<Os5+~^F<u,+zo,!|}u#n e6+aJG|dn/u!x`;8k,k');
define('NONCE_KEY', 'aM3~ZibX) Jz4IB+RW%m4M4cY*LHtOwN|Z1kQ/-GVyWOD. RD,n<>:,_GF)A`p:>');
define('AUTH_SALT', ']-tLB^g{c$+NL$%=Npsn,-P~t%9jH0h-t-e-A=-m@!Wh7y-6f1D|;LvU5qG');
define('SECURE_AUTH_SALT', '4p mo_GfknF1>zk$PY[_V_e#f9@![wsE9?head7ZJkMJ..^]S|fZ6+3Zw+ZoV/bq');
define('LOGGED_IN_SALT', 'ACKwio1l@N8P#H|T:$NC-9- 6;geaT2CPp+b[acWxI;Gona}eKl2w5wZZ2KA5I;B');
define('NONCE_SALT', 'Gbl4l,Eu%;|F4/9EMq7%$D-xzuNuc[[+Xn.#dk#|CR:6iu*l{7B62Mzq17!vPO8T');

 

5. Datenbank-Präfix ändern

Immer wieder ist zu lesen, dass dies die Sicherheit extrem erhöhen sollte. Ich persönlich habe den Fokus auf andere Punkte. Doch wenn ein neuer Blog angelegt wird, schadet es auf keinen Fall, die Variable $table_prefix = ‚wp_‘; in der wp-config.php beispielsweise in ‚wp3h3r6_’ zu ändern. Warum das ganze? Wenn das Datenbank-Prefix „wp_“ nicht geändert wird, ist bekannt, wie die Tabellen in der MySQL-Datenbank lauten. Somit kann speziell nach diesen Tabellen gesucht werden bzw. Datenbank-Sicherheitslücken, die nur beschränkten Zugriff auf eine Datenbank erlauben, sind gegebenenfalls von größerer Gefahr.

Wer nachträglich die Datenbanknamen ändern möchte, kann dies in der wp-config.php erledigen und muss dann zusätzlich auf der Datenbank die Tabellen umbenennen (alle!)

-- Beispiel für die Tabelle wp_posts. Beispiel-Prefix: ‚wpSic#er_’
RENAME TABLE wp_posts to wpSic#er_posts;

Hier geht es weiter zu den Tipps 6 – 10 >>

HowTo: WordPress auf hohen PageSpeed optimieren – Teil 3

Im einleitenden Artikel und in den beiden vorangegangenen Artikel haben wir uns bereits über die Möglichkeiten ausgelassen, einen WordPress Blog in der Geschwindigkeit zu optimieren. Google legt uns nahe, auf die Geschwindigkeit Wert zu legen. Dieser nicht zu unterschätzende Fingerzeig soll heißen, dass eine Seite noch so guten und einzigartigen Inhalt haben kann – wenn die Seite langsam im Aufbau ist, verärgert dies den Leser und Google straft die Seite ab. Grund genug also, die Hinweise der Mutter aller Suchmaschinen nicht auf die leichte Schulter zu nehmen.

JavaScript später parsen
Man war es gewohnt, im Header einer Seite auch alle JavaScript-Blöcke zu definieren. Inzwischen geht man dazu über, die nachzuladenden JavaScript -Dateien erst am Ende der HTML-Seite einzubinden. Man erhöht dadurch zwar nicht die Ladezeit, doch die Darstellung der HTML-Seite erfolgt schneller. Denn erst zum Schluss werden die meist für die Bedienung benötigten JS-Dateien geladen. Wenn man eine Seite selbst erstellt, sollte man dies heute beachten. Bei WordPress bedeutet dies wieder einen tiefen Eingriff und die Gefährdung des Updates. Gerade Plugins binden gerne ihr JavaScript im Header ein. Nur wenig neuere Plugins haben diesen Modus umgestellt. Wer möchte, kann in ein solches Plugin eingreifen.

mod_rewrite.c: Der WordPress-Klassiker zur Definition der Permalinks. Das Mod_rewrite-Modul schreibt online eine lesbare URL („domain.de/kategorie/mein-artikel“) in die von WordPress verwendete interne Struktur um.
ACHTUNG: Den genauen Syntax innerhalb dieses Blockes an die eigene Permalink-Struktur anpassen. WordPress selbst gibt unter den Einstellungen die Struktur vor.

Anfragezeichenfolgen aus statischen Ressourcen entfernen
Google mag es gar nicht, wenn man beispielsweise einer CSS-Datei einen Parameter übergibt. Was das soll? Beispielsweise das WordPress-Plugin Rating Star erstellt das auszuliefernde CSS per PHP-Code. Dies kann Sinn machen, denn dann wird nur der Teil des Codes ausgeliefert, der auch für den Client notwendig ist. Doch Google findet einen Aufruf wie „rating.css?KEY“ wenig schick. Lösung? Aufgrund der getroffenen Konfiguration des Plugins könnte man – wie von Google gewünscht – ein statisches CSS erstellen und veröffentlichen. Problem: Bei jedem Plugin-Update muss ich den neuen Output untersuchen und ggfls. nachbessern.

Da ich seit Monaten auf ein Update des Plugins warte, habe ich den schnellen Weg gewählt: Ich habe die entsprechende PHP Datei geöffnet und den CSS-Teil optimiert (Kompressor). Somit habe ich die Ladezeit schon „ein wenig“ optimiert und lebe weiterhin mit dem „Low priority“-Makel.

Zeichensatz angeben
Zu Recht eine „Low Priority“, doch schnell erledigt. Im Header der HTML-Seite einfach den Meta-Aufruf korrekt einfügen.

Ich hoffe ich konnte hier einige Anregungen und Tipps für die Optimierung geben. Wie ich mehrfach erwähnt habe, folge ich nicht jeder Empfehlung von Google in Sachen PageSpeed. Gerade im Bereich der „Low priority“-Meldungen wäge ich zwischen Aufwand und Nutzen ab. „Rote“ und „gelbe“ Meldungen sind jedoch auf jeden Fall wert, im Sinne der Empfehlung von Google umzusetzen.

HowTo: WordPress auf hohen PageSpeed optimieren – Teil 2

Nachdem wir uns bereits um die optimale Bildergröße gekümmert und den HTML und CSS Code per Kompressor verkleinert haben (HowTo: WordPress auf hohen PageSpeed optimieren – Teil 1), kommen wir nun noch zu weiteren Punkten, die wir verbessern können:

„Browser Caching nutzen“, Dateien online komprimieren
Die folgenden Änderungen können in einer htaccess-Datei vorgenommen werden. Bei Hosting-Paketen ist dies meistens auch die einzige Möglichkeit. Wer einen eigenen (V) Server hat, sollte die Änderungen direkt in der vhost-Datei vornehmen.

Warum? Nun, die htaccess-Datei wird bei jedem Aufruf einer Datei gelesen. Egal ob die Index-Seite, den eingebetteten Bildern, JavaScript und CSS-Dateien. Also alleine für eine Startseite gut und gerne fünf bis zehn Aufrufe. Wenn die Änderung zentral in der vhost vorgenommen werden, wird dies nur beim Start des Apache einmalig gelesen.

Hinweis für Plesk: Bei meinen Plesk-Servern ist das Apache Modul „mod_expires“ nicht aktiviert. Dies kann jedoch über die Plesk-Oberfläche erledigt werden. Aber auch der klassische Weg, das Modul über Apache zu aktivieren, ist möglich.

# BEGIN WordPress

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 week"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/x-icon "access plus 1 year"
</IfModule>

<IfModule mod_deflate.c>
 AddOutputFilterByType DEFLATE text/html text/xml text/css text/plain
 AddOutputFilterByType DEFLATE image/svg+xml application/xhtml+xml application/xml
 AddOutputFilterByType DEFLATE application/rdf+xml application/rss+xml application/atom+xml
 AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript
 AddOutputFilterByType DEFLATE application/x-font-ttf application/x-font-otf
 AddOutputFilterByType DEFLATE font/truetype font/opentype
</IfModule>

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

Was macht unser Code? Der Code besteht aus drei Blöcken, die jeweils das Vorhandensein eines bestimmten Apache-Modules (<IfModule) prüfen. Wenn das Modul vorhanden ist, wird der Code innerhalb der IF-Tags ausgeführt. Fehler im Code quittiert der Apache mit einem Fehler 500.

mod_expires.c: Wir aktivieren dieses Modul und weisen den Apache an, bestimmten Dateitypen wie Grafiken, JS oder CSS-Dateien ein bestimmtes Ablaufdatum im Header mitzusenden. Somit kann der aufzurufende Browser die Datei cachen und läd sie nicht immer erneut vom Webserver. Dies spart sehr viel Bandbreite, wenn immer wiederkehrende Bilder oder eine zentrale CSS-Datei nicht bei jedem Blättern einer Seite erneut geladen wird.

mod_deflate.c: Obwohl wir unseren HTML und CSS Code bereits über einen Kompressor optimiert haben, weisen wir nun den Apache an, die definierten Dateien per gzip online zu komprimieren und so verkleinert an den Browser zu senden. Was aufwändig klingt, zeigt sich in der Praxis als gängig und optimal. Die Zeit, die der Server für die Komprimierung und der Ziel-Browser für das Entpacken benötigt, ist durch den geringeren Datenaustausch von Server zum Client gut angelegt. Auch kommen heute alle gängigen Browser mit einer solch online optimierten Übertragung zu Recht. In unserer Definition werden nur Textdateien (HTML, CSS etc) und keine Bilder komprimiert.

Im dritten und letzten Teil unserer kurzen Artikelserie über PageSpeed gehen wir auf JavaScript, den Zeichensatz und Mod_rewrite ein.

HowTo: WordPress auf hohen PageSpeed optimieren – Teil 1

Im einleitenden Artikel „Ranking Faktor Page Speed: WordPress optimieren“ dieser kleinen Artikel-Reihe bin ich auf die Gründe, warum man seine Webseite auf Geschwindigkeit optimieren sollte, bereits eingegangen. Hier beschäftigen wir uns nun mit den gängigen Vorschlägen von Google für einen schnelleren WordPress Blog.

404-Fehler: Wenn eine Webseite auf eine Grafik verweist, die nicht vorhanden ist, schickt der Webserver einen Fehler „404“ zurück. Die ist unnötig und bremst die Seite aus. Zumeist sind solche „Leichen“ in der CSS-Datei (style.css) verborgen. Also: Analysieren und entweder den Aufruf entfernen oder die Datei (Image) korrekt zur Verfügung stellen.

GIF-Bilder: Google kritisiert sehr gerne GIFs, die auf der Webseite bzw. in der Theme verwendet werden. Hier erreicht man sehr schnell Abhilfe, indem man das GIF mittels der freien Grafiksoftware „Gimp“ öffnet und als PNG (Kompression 9) exportiert. Diese PNG-Datei dann in den HTML-Code oder CSS-Datei einbinden.

HTML, CSS und JS-Dateien optimieren: Ziel ist es, den nachzuladenden Code so kurz wie möglich bereit zu stellen. Kommentare im Code, so sinnvoll sie für Änderungen sind, sind unnötiger Ballast bei der Übertragung zum Besucher. Auch Einrückungen in HTML oder CSS erhöhen die Lesbarkeit, sind aber unnötig. Also wird man zwei Versionen dieser Dateien benötigen. Die lesbare Ur-Version und eine auf dem produktiven Server zur Auslieferung bereitstehenden optimierten Code.
Zur Optimierung gibt es diverse CSS und HTML online Kompressoren. Für alle gilt: Immer die Original-Version zuerst sichern! Den optimierten Code im Web einsetzen und testen. Hier muss man probieren und experimentieren.
Tipp: Oftmals gibt es Probleme mit WordPress-Dateien, die HTML und PHP-Code beinhalten. Kommentare im PHP-Block bereiten je nach Kompressor hier Probleme. Meist entferne ich zuerst diese Kommentare per Hand und nutze dann einen Kompressor. Je nach Datei optimiere ich auch nur den HTML-Code und lasse den PHP-Block unverändert.

  1. YUI Compressor: JS und CSS Kompressor
  2. HTML Compress: HTML Kompressor

Bilder optimieren, skalierte Version bereitstellen
Viele Punkte Abzug in Rechung stellt Google, wenn man zu große Bilder auf einer Webseite einfügt und diese mittels „width“ und „height“-Tag herunterskaliert. Google hat schon Recht: Es ist unnötige Ressourcenverschwendung. Beispielsweise stellen wir im Artikel „WordPress: Recent Post (Letzte Artikel) mit Thumbnail-Images in der Sidebar anzeigen“ per WordPress eine Sidebar mit Thumbs zur Verfügung. Hier nutzen wir 150px große Bilder. Im Code reduzieren wir die Anzeige auf 70px. Ein böses Foul und Google moniert dies mit einer roten Karte.

Wie man individuelle WordPress-Bildgrößen (automatische Erstellung) generiert, zeige ich in einem weiteren Artikel. Auch sei erwähnt, dass Google die Kompression von JPGs mittels WordPress anmäkelt. Laut Google kann die Kompression noch weiter erhöht werden. Da ich mit 96 Punkten PageSpeed zufrieden bin, ignoriere ich diese „Low priority“-Meldung.

Wichtig auch: WordPress bietet beim Einbinden eines Bildes an, eine vorliegende Bildgröße herunterzurechnen. Dies mit Bedacht wählen. Besser: Die endgültige Größe des einzubindenden Bildes optimal komprimiert via WordPress hoch laden und einbinden. Zuvor das optimale Grafikformat PNG/JPG für Grafiken/Bilder wählen.

Zum Teil 2: HowTo: WordPress auf hohen PageSpeed optimieren – Teil 2

Ranking Faktor Page Speed: WordPress optimieren

Um eine Webseite oder ein Blog optimal für die Suchmaschinen zur Verfügung zu stellen, verwenden wir viel Zeit. Die Texte werden optimiert, der Aufbau der Seite wird entsprechend angepasst und irgendwann (und nicht allzu spät) sollte man sich mit dem Thema „Page Speed“ beschäftigen.
Google (Slogan: „Make the Web faster!“) selbst sagt, dass die Geschwindigkeit einer Webseite ein wichtiger Ranking-Faktor ist. Dass die Zufriedenheit eines Besuchers mit einer Website nicht nur mit dem Inhalt und dem Design, sondern auch mit der Ladezeit zusammenhängt, sollte jedem Surfer bekannt sein. Wer hat schon Geduld, Sekunden um Sekunden zu warten, bis sich eine Webseite aufgebaut hat? Der Onlinehändler Amazon hat nach eigenen Angaben eine Untersuchung durchgeführt die besagt, dass mit jeder Sekunde, die eine Webseite länger zum Aufbau benötigt, 10 Prozent Umsatz weg brechen. Zehn Prozent Umsatz bei Amazon: Das dürfte den einen oder anderen Webmaster animieren, seine Webseite auf die Ladezeit hin zu optimieren.

Aber auch mehr Besucher von Google sollte ein Anreiz sein, den Page Speed seiner Webseite zu kontrollieren und wenn nötig die Seite anzupassen. Google stellt hierfür im Developers Bereich eine Seite bereit, über die man seine Webseite online prüfen kann. Google vergibt bis zu 100 Punkte für den Speed der Seite. Ziel ist also, den Page Speed an 100 anzunähern. Ob wirklich 100 erreicht werden müssen, sei dahingestellt. Viele Webmaster können auch mit einem Page Speed im Bereich 80 bis 90 Punkte gut schlafen. Für viele Seiten habe ich ohne großen Aufwand 96 Punkte erreicht. Die Optimierung um vier Punkte auf 100 habe ich bewusst gelassen. Wer mit seiner Webseite eine Page Speed von unter 80 oder gar unter 70 Punkten hat, sollte auf jeden Fall die Vorschläge von Google angehen.

Webseite oder WordPress-Blog auf Page Speed testen
Nun ist es also endlich an der Zeit, die eigene Webseite einem ersten Speed-Test zu unterziehen. Über das Google Tool PageSpeed wird nach Eingabe der URL die Seite getestet. Neben dem Score (Punkte) erfährt man in den Kategorien „High priority“ (rot), „Medium priority“ (gelb) und „Low priority“, welche Vorschläge Google zur Optimierung vorschlägt. Ziel ist es meiner Meinung nach auf jeden Fall, die Punkte „Rot“ und „Gelb“ abzuarbeiten. Unter den Punkten „Low priority“ und „Experimental rules“ wird es – gerade bei WordPress Blogs – schwer, alle Vorschläge von Google zu befolgen. Hier wären zum Teil tiefe Eingriffe in WordPress Plugins notwendig, die ein Update desselben dann wieder unnötig erschweren. Da viele Updates aus Sicherheitsgründen schnell und unkompliziert eingespielt werden sollen, muss man abwägen, ob einem diese Hürde der „letzte Punkt zum PageSpeed von 100“ wert ist.

Page Speed: Dieser Blog
Diesen Blog sehe ich nicht als Referenz für den PageSpeed an. Bei Speed Test erreichte er 82 von 100 Punkten, ohne eine Anpassung. Die Mängelliste von Google ist lang und zu gegebener Zeit werde ich auch hier die Optimierung angehen.

Hier geht es weiter: In der kurzen Artikel-Serie „HowTo: WordPress auf hohen PageSpeed optimieren – Teil 1“ beschreibe ich die gängigen Änderungen für einen besseren PageSpeed

WordPress to Twitter Plugin installieren

WordPress to Twitter Plugin
WordPress to Twitter Plugin

Wer einen Blog betreibt und bei Twitter aktiv ist, kommt schnell auf den Gedanken, jeden neuen Artikel auch automatisiert bei Twitter zu veröffentlichen. Der Mikro-Blog-Dienst Twitter bietet dank umfangreicher API die Möglichkeit, einen solchen Datenaustausch zu erledigen. Wie wir per PHP und C# auf die API von Twitter zugreifen und Tweets und Nachrichten senden, darauf gehe ich in weiteren Artikeln ein. In diesem Artikel nutzen wir ein fertiges WordPress-Plugin für diese Aufgabe: „WP to Twitter“.

Vorbereitung: API-Keys für Twitter und den Bit.ly generieren
Um auf Twitter und den URL-Shortener Bit.ly zugreifen zu können, benötigen wir entsprechende API-Key. Auch das WP-Plugin „WP to Twitter“ benötigt diese Keys, da das Plugin die Schnittstellen bedient. Wir müssen uns also entsprechend bei diesen Diensten anmelden und die Keys generieren.

API-Keys für Twitter generieren

  1. Registrierung der Application bei Twitter („Twitter´s application registration page“); ausfüllen der Pflichtfelder.
  2. „Application-Name“: Vergeben Sie einen beliebigen Namen. Das Wort „Twitter“, auch als Bestandteil, ist nicht erlaubt
  3. „Description“: Vergeben Sie eine beliebige Beschreibung.
  4. Callback-URL: Geben Sie hier die URL Ihres WordPress-Blogs ein (beispielsweise: http://sirmark.de)
  5. Bestätigen Sie die „Developer Rules“, senden Sie das Formular ab.
  6. Wechseln Sie auf den Reiter „Settings“
  7. Hier ist es wichtig im „Application Type“ das Recht „Read and Write“ zu vergeben.
  8. Ein Update nicht vergessen!
  9. Nun erhalten Sie den „Twitter Consumer Key“, den „Twitter Consumer Secret“ Schlüssel, einen „Access Token“ sowie einen „Access Token Secret“-Schlüssel.
  10. Mit diesen Schlüsseln haben Sie Zugriff auf Ihr Twitter-Konto. Behandeln Sie also die Daten mit gewisser Sorgfalt.

API-Key für Bit.ly generieren
Da Twitter nur 140 Zeichen pro Nachricht erlaubt und ein Link bereits ein Großteil dieser Zeichen beansprucht, sollte man einen URL-Shortener einsetzen. Das Plugin kommt mit diversen Shortener zurecht. Ich nutze und beschreibe hier „Bit.ly.“

  1. Erstellen Sie ein Konto bei bit.ly
  2. Es ist nur ein Mindestmaß an Angaben notwendig.
  3. Wechseln Sie auf die Seite http://bitly.com/a/your_api_key/

WordPress-Plugin „WordPress to Twitter“ installieren

Wordpress to Twitter Plugin installieren
WordPress to Twitter Plugin installieren

Nun kann es endlich losgehen. Suchen Sie in WordPress unter Plugins das Plugin „WP to Twitter“. Zum Zeitpunkt des Artikels ist die Version „2.4.5“ aktuell. Die Installation erfolgt wie bei jedem anderen Plugin in WordPress.

„WP to Twitter“-Plugin konfigurieren

WP to Twitter Einstellungen
WP to Twitter Einstellungen

Nun erfolgt die Konfiguration des Twitter-Plugins. Das Plugin bringt eine eigene und umfangreiche Konfigurations-Seite mit. Im oberen Bereich tragen wir die zuvor bei Twitter erstellten API-Keys („Twitter Consumer Key“, „Twitter Consumer Secret“, „Access Token“ und „Access Token Secret“) ein.

WP to Twitter bit.ly
WP to Twitter bit.ly

Weiter unten auf der Konfigurationsseite finden wir die vom Plugin unterstützen URL-Shortener. Wenn wir Bit.ly nutzen, tragen wir im entsprechenden Block unseren zuvor stellten Bit.ly-API-Schlüssel ein.

Basic-Settings

WP to Twitter Übersicht
WP to Twitter Übersicht

Die „Basic-Settings“ können zuerst einmal nahezu unverändert übernommen werden. Ich habe lediglich einige „Eindeutschungen“ (siehe „Screenshot“) vorgenommen. Für den ersten Test genügen jedoch die Standardeinstellungen.

Was passiert nun?
Wie wir in den „Basic-Settings“ festlegen können, reagiert das Plugin auf neue Artikel, Artikel-Updates und auch neue Kommentare mit einem automatisierten Tweet. Eine Möglichkeit, bestehende Artikel sukzessive per Tweet auf Twitter zu veröffentlichen, habe ich „auf die Schnelle“ nicht gefunden. Für wenige Artikel kann dies über ein Update erledigt werden. Wenn jedoch viele Artikel über Tage verteilt „getweetet“ werden sollen, muss eine andere Lösung her. Diese werden wir im folgenden Artikel in C# programmieren.