Das Jahr 2019 hat ja „gut“ angefangen: Anfang Januar ein großer Leak im Bereich Politik und öffentliche Personen sowie Ende Januar ein weiterer Leak mit 2,7 Milliarden Zeilen an Daten. Obwohl Letzterer wohl „nur“ rund 770 Millionen valide Datensätze enthielt, war es einer der größten Accountdaten-Leaks der letzten Jahre. 2018 war bereits mit Leaks und den großen CPU-Lücken Meltdown und Spectre recht kritisch.
Das zeigt doch, dass es selten wichtiger war als heutzutage, seine digitalen Accounts und Netzwerke sowie Technik und Geräte zu schützen. Aus diesem Grund habe ich mal ein paar Tipps und Links zusammengetragen, die zu mehr Sicherheit verhelfen, wenn ordentlich umgesetzt:

  1. Prüft auf den bekanntesten Internetseiten, ob eure Daten bereits von Leaks/Hacks betroffen sind. Schaut mal in meinen Leak-Check-Artikel, den habe ich auch nochmal aktualisiert. Die wichtigsten Anlaufstellen bleiben der Identity Leak Checker meines aktuellen Arbeitgebers 😉 (HPI) und HaveIBeenPwned.com (ins Deutsche übersetzte Infos dieser Seite gibt es auf dieser Seite).
  2. Setzt sichere und lange Passwörter und am besten unterschiedliche Passwörter für jeden eurer Logins/Seiten/Dienste! Dazu gehören Passwörter mit mehr als 12, besser mehr als 14 Zeichen aus allen Zeichengruppen (Groß- und Kleinbuchstaben, Sonderzeichen, Zahlen).
    Somit wäre ein öffentliches Passwort nur riskant für diese 1 Seite, das Passwort wäre nicht weiterzuverwenden. Ich verwende schon ewig unterschiedliche Passwörter mit mehr als 16 Zeichen für jeden meiner Logins. Da sich das auf Dauer und bei der Anzahl (ich habe über 300 Zugangsdaten…) niemand merken kann, empfehle ich hier einen Passwortmanager. Ich selber nutze KeePass, da dieser kostenlos und gut erweiterbar ist, aber es gibt etliche Alternativen, die nur ein paar Euro kosten. Zusätzlich können im Passwortmanager auch wichtige und kritische Infos, beispielsweise Kreditkarteninformationen, hinterlegt werden. Das Masterpasswort für den Safe sollte bezüglich Länge und Komplexität euer Meisterwerk werden 😉 Die meisten Programme bieten auch an, den Safe mit mehreren Sicherheitsfeatures zu schützen. Neben dem Masterpasswort kann somit ein Key File oder USB Dongle erforderlich sein, um den Safe zu öffnen. Prüft hier mal, was eure Software kann und erfordert im besten Fall mindestens zwei Zugangsmethoden.
  3. Aktiviert alle möglichen Sicherheitsfeatures eurer Accounts: Je nach Anbieter werden euch meistens verschiedenste Features zur Verfügung gestellt, welche den Zugang zum Account im Normalfall oder im Notfall (Passwort vergessen, E-Mail-Adresse nicht mehr verfügbar) gewährleisten sollen. Aktiviert diese Features und füllt sie mit euren Daten.
    1. 2-Faktor-Authentifizierung ist sowieso ein Muss für alle wirklich wichtigen Dienste, alles was mit Geld und großen Mengen eurer persönlichen Informationen zu tun hat.
    2. E-Mail-Benachrichtigungen aktivieren bei bestimmten Events, beispielsweise wenn Geld abgehoben, das Passwort geändert wird oder fehlerhafte Logins erfolgten.
    3. Konto-/Passwort-Wiederherstellungsfunktionen aktivieren; hier müssen meist weitere Infos wie Handynummern bereitgestellt werden oder Sicherheitsfragen festlegen, um im Notfall alternativen Zugriff zu bekommen. Vorsicht bei den Sicherheitsfragen: Die Antworten darauf solltet nur ihr selbst wissen und nicht irgendwo im Netz über euch herausfindbar sein.
  4. Wenn ihr Google und dessen Dienste so intensiv nutzt wie ich, empfehle ich vor allem hier alle Sicherheits- und Datenschutzeinstellungen durchzugehen. Ihr könnt hier wirklich sehr viel anpassen. Lest dazu meinen Artikel, dort liste ich alle Informationen kurz und knapp auf.
  5. Schützt alle Geräte und Hardware ebenso gut wie die digitalen Accounts: Gute PINs und Passwörter, Backups (vielleicht in einer sicheren deutschen Cloud?), Geräteverschlüsselung, zusätzlich Fingerabdruck/Gesichtserkennung, Anti-Diebstahl-Schutz eurer Smartphones sowie allgemein gute Sicherheitseinstellungen, Sicherheit und Backups einzelner Apps prüfen. Ein paar Android Basics noch, die können irgendwann mal helfen.
  6. Als Webseitenbetreiber habt ihr zudem zusätzliche Verantwortung mit euren Seiten und solltet auch dort die Sicherheit eurer Besucher gewährleisten und optimieren. Dazu gehört ein gut abgesichertes Backend/Logins, Optimierungen ausgehender Mails, optimierte HTTP-Header, regelmäßige Backups, verschlüsselte Passwörter in der DB, unterschiedliche Passwörter für Backend/DB/FTP/SSH oder Zugriff nur per SSH Key statt Passwort.
  7. Schützt eure Betriebssysteme und Software und haltet diese auf dem neuesten Stand. Die aktuellste Version von OS, Software und App schützt euch bestmöglich gegen aktuell bekannte Schwachstellen dieser. Zieht euch die Updates im besten Falle immer von den offiziellsten Quellen – Software von der Webseite der Software/des Betreibers, Apps aus dem offiziellen App-Store. Vermeidet Software-Downloader oder Portale wie CHIP, Netzwelt oder freeware.de – dort wird gerne ungewollter Kram untergejubelt. Installiert zusätzlich Sicherheitssoftware gegen Viren und ggf. gegen Werbung, wenn ihr euch unsicher fühlt oder bestmöglich vorsorgen wollt.
  8. Spam und Phishing ist zwar ein alter Hut aber noch immer nicht ausgestorben. Ganz im Gegenteil erhalten wir mittlerweile zwar wenig aber dafür teilweise recht gute unerwünschte E-Mails. Achtet also auch heute immernoch sehr kritisch und mit der nötigen Vorsicht bei Mails auf ihre Echtheit. In meinem vorherigen Artikel habe ich über die Standard-Tipps bei der Erkennung von Spam und Schadmails geschrieben. Wer sich bei dem Thema noch unsicher fühlt, sollte mal reinlesen.
  9. Nutzt Verschlüsselung wo immer möglich. Verschlüsselte Messenger wie Threema oder Signal (und an sich auch Whatsapp) übermitteln die Kommunikations mit Ende-zu-Ende-Verschlüsselung. Mails lassen sich beispielsweise mit PGP verschlüsseln und manche Anbieter wie ProtonMail haben das direkt schon unsichtbar integriert. Verschlüsselte Daten-Safes beispielsweise mit VeraCrypt sind sichere Ablageorte für kritische Dokumente und Daten. Sichere Cloudanbieter mit Verschlüsselungs-/Passwortfeature sind ebenfalls praktisch.
  10. Auch der physische Schutz der Hardware selbst darf nicht vernachlässigt werden. Letztlich ist ein Ausfall der Hardware im schlimmsten Falle auch mit Datenverlust verbunden, was zumindest emotional fast schon ein größerer Schaden sein kann als die Arten von digitalem Datendiebstahl der oberen Punkte. Sorgt deswegen für eine fachgerechte Installation aller Hardware in eurem Computer, schließt das Gehäuse ab wenn dieses in öffentlich zugänglichen Bereichen steht (zusätzlich ist dann ein BIOS-seitiges IDS sinnvoll), schützt schützenswerte Komponenten beispielsweise mit einem Schutzlack von RS Components und reinigt das ganze System regelmäßig beispielsweise durch Aussaugen, trocken Auswischen und Reinigen der Lüfter.

Ich denke das reicht erstmal als Grundlage, da geht sicher noch einiges mehr. Schickt mir gerne weitere Hinweise und Punkte, die ich hier dann noch ergänzen werde. Bis dahin: Stay safe! 🙂

Über

Meinen Blog gibt es nun schon seit fast 10 Jahren, verrückte Sache. Und obwohl ich grob über 300 Tutorials und How-To’s geschrieben habe, ist es wie so oft: Der Schuster hat gerne mal die schlechtesten Leisten. Wartung und Pflege des eigenen Systems geraten bei viel Stress und wenig freier Zeit in den Hintergrund. So kommt es, dass mich die Firma Enginsight letztens kontaktiert hat. Der Inhalt war ein weniger positiv – man übergab mir einen Sicherheitsbericht meines Blogs. Dieser enthielt Bewertungen in mehreren Kategorien und leider nicht nur gute, dazu später mehr. Das war der Anlass, dass ich mich mit der Sicherheits-Suite von Enginsight und der Sicherheit meines Blogs detaillierter auseinandergesetzt habe.

Erster Eindruck

Die Suite von Enginsight möchte mit ihren Tools die Sicherheit und Verfügbarkeit von IT-Netzwerken erhöhen und den Aufwand der Wartung und Überwachung verringern. Das Netzwerk kann hier beliebig verschieden und groß sein – von 1 Webseite, einem PC oder Server bis hin zu einem aus mehreren Nodes und Netzwerkkomponenten bestehenden Netzwerk.

Dank des 14-tägigen Probeaccounts lässt sich die Suite auch direkt selbst ausprobieren. Nach dem Login lande ich auf einem sehr übersichtlichen Dashboard, in dem mir meine eingerichteten Assets (in diesem Moment natürlich noch keine) angezeigt werden. Über eine Menüleiste habe ich schnellen Zugriff auf die wichtigsten Funktionen: Umgebungen, Hosts, Endpunkte, Alarme, Einstellungen, Suche, mein Profil sowie einigen weiteren Kleinigkeiten.

webseiten-netzwerke-analysieren-kontrollieren-enginsight-suite-dashboard
Übersichtliches Enginsight Dashboard mit ersten angelegten Assets

Detaillierte Analysen

Ich habe hier direkt meinen Blog als einen Endpunkt angelegt. Endpunkte sind Webseiten oder Webapplikationen, für die verschiedene Funktionen zur Verfügung gestellt werden: Webseite (Verfügbarkeitstests aus EU/USA), SSL/TLS, Web Thread Intelligence, Portscan, HTTP-Headers und Redirects. Die Funktionen können flexibel hinzugefügt werden. Anschließend starten direkt die ausgewählten Untersuchungen auf das eingegebene Ziel und nur wenige Sekunden später erhalte ich eine ausführliche Analyse meiner Seite:

Schnell ist klar: Hier muss was gemacht werden! Ich gehe mal kurz die Features durch und was jeweils untersucht wird:

  • Webseite: Stündliche Verfügbarkeitstests von Servern aus Frankfurt (EU) oder East-Virginia (USA), Analyse der Verbindungszeiten (wieviel Millisekunden Lookup, Connect, Transfer usw.)
  • HTTP-Headers: Analyse der Antwort-Header-Eigenschaften und speziell der Security-Header. Dazu mehr im Kapitel weiter unten
  • SSL/TLS: Untersuchung des verwendeten SSL-Zertifikats, der unterstützten Protokolle und Chiffren sowie möglicher Sicherheitslücken bzgl. SSL
  • Redirects: Weiterleitungen der Domain
  • Web Threat Intelligence: Prüfung von Sicherheitslücken beim Server, benutzten Anwendungen, der Webseite, benutzter Frameworks und Errechnung eines Scores
  • Portscan: Offene Ports und deren Service, ggf. Informationen zu eingesetzter Software und Version hinter einem Port

Bei der dauerhaften Verwendung von externen Dienstleistern, vor allem wenn (personenbezogene) Daten der eigenen Plattform dort verarbeitet werden, müsst ihr darauf achten, das Verzeichnis von Verarbeitungstätigkeiten zu ergänzen. Ansonsten wäre das im worst-case ein Datenschutzverstoß.

Ein Bericht hat mir dabei besonders wenig gefallen: Die Bewertung F bei den HTTP-Header. Also habe ich hier mal einen detaillierteren Blick geworfen:

Crashkurs HTTP-Headers

HTTP-Header sind Anweisungen, die vom Server- oder Webseitenbetreiber konfiguriert werden, um die Sicherheit des Webangebots sowie der Besucher zu erhöhen
Die Anweisungen können, je nach Hosting Setup, recht einfach über die .htaccess (empfohlen beim Shared Webhosting) gesetzt werden.
Im folgenden nenne ich zwei beispielhafte Security-Header, es gibt jedoch noch mehr:

HTTP Strict Transport Security (HSTS)

HSTS gibt vor, dass und für welchen Zeitraum in der Zukunft alle Anfragen über HTTPS/SSL vom Client zum Server geschickt werden müssen. Anfragen über HTTP werden innerhalb dieses Zeitfensters dann komplett geblockt. Eine Beispielkonfiguration wäre:

<IfModule mod_headers.c>
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</IfModule>

Anzupassen: max-age als Zeitangabe in Sekunden, wie lange der Zugriff in Zukunft für diesen gerade aufrufenden Client ausschließlich über SSL erfolgen muss. Der Wert 31536000 steht demnach für 1 Jahr. includeSubDomains erweitert dieses Verfahren auf alle Subdomains. Weitere Informationen und Details zu HSTS und die preload-Eigenschaft in meinem separaten Artikel zum Thema HSTS und XSS-Protection hier.

Content-Security-Policy (CSP)

Der Content-Security-Policy Header überprüft die Art und Herkunft von Daten und Anfragen während des Ladens und kann darauf reagieren. Damit wird verhindert, dass unerwünschte, fremde oder gefährliche Inhalte geladen und ausgeführt werden. Das ist für gewöhnlich das Ziel von XSS-Angriffen und Code-Injections, schädliche Inhalte in vertrauenswürdige Seiten einzubinden.

Die Kontrolle der Inhalte wird hierbei in unterschiedliche Kategorien (auch Direktiven genannt) aufgeteilt und jeweils können erlaubte Datenquellen definiert werden. Beispielsweise gibt die Direktive script-src an, von welchen Domains und Quellen Skripte in die geschützte Seite eingebunden werden dürfen. Ein CSP-Beispiel:

Header always set Content-Security-Policy: "default-src 'self'; img-src 'self' https://secure.gravatar.com https://wordpress.org data:; script-src 'self'; font-src 'self' fonts.gstatic.com; report-uri https://report-uri.de/report.php"

Diese CSP definiert erlaubte Datenquellen für die Direktiven default-src, img-src, script-srcfont-src und definiert einen Bericht-Endpunkt mit report-uri. Die Angabe ’self‘ erlaubt Daten von der eigenen Domain, in der die CSP benutzt wird. Zusätzlich wurden für Bilder die Domains von gravatar.com und wordpress.org sowie die Einbindung von data-uri Bildern als Ausnahmen definiert. Ebenso das Laden von Schriften vom Google Repository. Wenn ein Angreifer nun versucht, beispielsweise über einen Kommentar ein Skript von seiner Angreiferdomain einzubetten, würde die CSP dies blocken, da nur die eigene Domain Skripte einbinden darf.

Mehr zum Aufbau und Einrichtung des CSP Header in meinem Artikel zu diesem Thema hier.

Sicherheit von Hosts auch mit Künstlicher Intelligenz überwachen

Zurück zur Enginsight-Suite, denn hier gibt es noch viel mehr zu entdecken! Aus Gründen der Komplexität reiße ich die folgenden Features nur grob an.

Wie eingangs erwähnt, lassen sich auch Computer und Server in das System einfügen und durchleuchten. Dafür wird ein Client auf den Geräten eingerichtet und schon werden etliche Systemkomponenten untersucht: Auslastung von CPU, RAM, SWAP, integrierte Partitionen, Netzwerkschnittstellen, installierte Software und Updates sowie laufende Prozesse. Daran erkennt Enginsight dann mögliche Sicherheitsrisiken und gibt Handlungsempfehlungen.

Außerdem wacht eine auf Neuralen Netzwerken basierende Künstliche Intelligenz über die erfassten Daten. Auf Wunsch erkennt diese beispielsweise wenn personenbezogene Daten das Netzwerk verlassen oder bestimmte Metriken wie CPU oder RAM ihre Normalwerte verlassen. Basierend auf den gelernten Daten erkennt die KI Anomalien und alarmiert den Admin. Dieser kann den Vorfall dann einstufen und die KI lernt für zukünftig Entscheidung aus diesem Handeln. Somit wird die KI immer intelligenter und der Admin hat weniger Verwaltungsaufwand.

Im Peacemaker werden die Systemupdates aller angelegten Hosts in einer Übersicht angezeigt und können direkt verwaltet werden. So lassen sich beispielsweise unter Linux Updates direkt aus Enginsight heraus installieren.

Unter dem Menüpunkt Umgebung versteckt sich ein Visualisierungstool, mit dem die angelegten Assets miteinander in Verbindung gebracht werden können. Außerdem lassen sich hier auch Notizen oder ganze Dokumentationen für jedes Asset hinzufügen. So fehlt nie der Überblick bei komplexen Infrastrukturen.

Automatisierung über Alarme und Plugins

Zwei weitere Funktionen lassen sich gut kombinieren, um eine Automatisierung von Maßnahmen im Problemfall zu realisieren: 
Alarme können erstellt werden, um regelmäßige Überprüfungen durchzuführen und im Problemfall Maßnahmen zu ergreifen. Mögliche Überwachungen bei einem Endpunkt: Response Time, Tage bis Zertifikatsablauf, Webseite nicht erreichebar, Neue Sicherheitslücke, Datenschutzverstoß. Für Hosts gibt es noch mehr, hier nur ein paar: Temperatur, neue oder entfernte Programme, Verdächtiger Netzwerkverkehr. Natürlich lassen sich hier Benachrichtigungen einstellen, aber als besonderes Extra gibt es auch Plugins, die im Problemfall ausgeführt werden können.

Plugins sind im Skripte, die selbst geschrieben werden können. Zur Verfügung stehen die drei Sprachen: Bash, Powershell und Python. Plugins können per Cronjob regelmäßig ausgeführt oder eben von Alarmen getriggert werden. Dem Troubleshooting sind mit diesen drei Sprachen kaum Grenzen gesetzt. Beispielsweise könnten per SSH im Falle des Ausfalls bestimmter Webseiten die Netzwerksettings vom Server geändert oder repariert werden.

Fazit

Die Enginsight-Suite bietet eine große Palette an Sicherheits- und Überwachungsfeatures (nicht alle konnte ich in diesem Artikel vorstellen), mit denen sich einzelne Assets oder ganze Netzwerke analysieren und steuern lassen können. Dank KI-Metrik-Überwachung, Alarmen, Plugins und anderen kombinierbaren Lösungen ist hier eine großes Potential für Automatisierung, wenn alles richtig eingerichtet wird. Das Produkt ist immernoch stark in der Entwicklung und der Kontakt zum Enginsight-Team war äußerst angenehm – hier steckt viel Herzblut im Produkt. 

Dieser Beitrag ist eine Ergänzung bzw. Erweiterung des vorherigen Posts „FTP Backup Skript in PHP“.

Folgende Änderungen werde ich hier besprechen:

  • Erweiterung des Skripts
  • Sicherheit: Absicherung mit .htpasswd
  • Sicherheit: Absicherung mit .htaccess
  • Automatisierung mit All-Inkl Cronjobs

Erweiterung des Backup Skripts

Das neue Skript bietet nun neue Funktionalitäten:

  • Farbliche Hervorhebung
  • Verbesserungen des Backup Prozesses, zusätzliche Überprüfungen und Debug Infos bei Fehlern
  • Verbinden eines externen FTP Server und Kopieren aller neuen Backups
  • Angabe eines beliebigen Ports
  • Verbindung über FTPs (SSL FTP) Port 21 wird verwendet, unsicheres FTP nur noch als Fallback
  • detailliertere Informationen über die Backups in der Benachrichtigungsmail

Nun werden also von beliebig vielen Ordner des FTP-Root Backups erstellt, gegebenenfalls aufgeräumt wenn mehr Backups existieren als aufgehoben werden sollen und anschließend alle neuen Backups auf einen externen FTP Server kopiert. Für den Upload wird der passive FTP Modus verwendet, da dieser in den seltensten Fällen Probleme macht. Sollte der Wechsel zum passiven FTP fehlschlagen, wird dennoch aktives FTP probiert.
Alle nötigen Informationen werden in den Variablen in Zeile 16 bis 25 angegeben. Format und Hilfe steht jeweils dabei, eigentlich sollte da alles klar sein.

Die Erweiterungen machen aus dem Skript eine beispielhafte Backup-Lösung für FTP Inhalte. Auch hier am Beispiel von All-Inkl als Hoster. Wer die Lösung unabhängig von All-Inkl einsetzen möchte, wird das Archive_Tar PHP Modul und irgendeine Art von Cronjob-Funktionalität brauchen.

Screenshot

php-ftp-backup-neu

Code

Achtung: Update (09.06.2015): Dieses Skript ist Version 1.1, basierend auf dem grundlegenden Backup Skript aus diesem Artikel. In Version 1.2 (diesen Artikel) lassen sich nun auch MySQL Datenbanken mitsichern.

Update (02.06.2015): Anpassung der Variablennamen zur besseren Lesbarkeit, Fehlerkorrekturen, verbesserte FTP-URI-Verarbeitung, Portangabe möglich, Verbindung über FTPs (FTP über SSL über Port 21) möglich (mit Fallback zu unsicherem FTP wenn FTPs nicht funktioniert), erweiterte Informationen über die erfolgten Backups in der Benachrichtigungsmail, kleine Optimierungen
Code anzeigenDen Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.

<?
// PHP-Konfiguration optimieren
// if no errors are shown, please check htaccess restrictions by "php_flag display_errors off"
// in this or parent folders
@error_reporting(E_ALL);
@ini_set("max_execution_time", 300);
@ini_set("memory_limit", "256M");
header('Content-Type: text/html; charset=utf-8');
include "Archive/Tar.php";
$pfad = preg_replace('/(\/www\/htdocs\/\w+\/).*/', '$1', realpath(__FILE__));
$alltime = 0;
$newbackups = array();
$backupinfo = array();
echo "<style>.ok{color:#478F47;}.err{color:#DA3833;}.grey{color:grey;}.warn{color:#f0ad4e;}th,td{border-bottom: 1px solid #aaa;}</style>";

// ########## EDIT THIS VARIABLES ###################
$foldertobackup = array("tools", "reports"); // which root folders should get backed up?
$copytoexternalftp = 1; // copy new backup files to external ftp server? should be 1/"yes"/"ja" or 0/"no"/"nein"
// external (ftp) servers to copy new backups to, format:
// in general: ftp://username:password@url:port/path (port is required!)
// ftp://user:pw@ftp.server.com:21/
// ftp://user:pw@serverurl.com:21/optional/path
// secure sftp connection (port 22) in preparation but not ready yet
$externalftpuri = "ftp://admin:password@firma.dns.com:21/Data/FTP-Backups";
$backupfilemaximum = 2; // how many archives should be stored?
$dir = $pfad."backup/"; // in which subfolder is this backup php file? this would be: "root/backup/"
$sendmail = 1; // send notification mail when all backups are done - should be 1/"yes"/"ja" or 0/"no"/"nein"
$sendmailto = "adminmail@firma.de"; // valid mail address to send the mail to
// ##################################################

echo "<span class='ok'>".date("d.m.Y G:i:s")."</span><br>";

foreach ($foldertobackup as $verzeichnis) {
   $jobtime = time();
   echo "<br><br>########################################<br>";
   echo "<strong>Verzeichnis $verzeichnis wird gesichert...</strong><br>";
   flush();

   // check if folder exists
   if(!file_exists($pfad.$verzeichnis)) {
      echo "<span class='err'>Sicherung fehlgeschlagen. Zu sichernder Ordner $pfad$verzeichnis existiert nicht.</span>";
      continue;
   }

   // Name: [verzeichnis]_[Datum]_[Uhrzeit].tar.gz
   $archivname = $verzeichnis.date('_Y-m-d_His').".tar.gz";
   // Name: [All-Inkl-Accountname]_[Datum]_[Uhrzeit].tar.gz
   //$archivname = preg_replace('/.+\/(.+)\/$/', '$1', $pfad).date('_Y-m-d_His').".tar.gz";

   // Auszuschließende Ressourcen
   $ignorieren = array("*.sql.gz", "*.tar.gz", "usage", "logs");

   // ######### create backup
   $archiv = new Archive_Tar($archivname, true);
   $archiv->setIgnoreList($ignorieren);
   $archiv->createModify($pfad.$verzeichnis, "", $pfad);
   $backuptime = time() - $jobtime;
   $archivsize = round(filesize($dir.$archivname)/1000000);
   if (!is_numeric($archivsize)) {
      $archivsize = "filesize error";
   }
   if (is_int($backuptime)) {
      echo "<span class='ok'>Backup fertig: $archivname (Größe: $archivsize MB, Dauer: $backuptime Sekunden)</span><br>";
   } else {
      echo "<span class='ok'>Backup fertig: $archivname</span><br>";
   }

   // check created archive
   if (!is_object($archiv)==1 || !is_numeric($archivsize) || !$archivsize>50) {
      // abort process due to wrong type or too small filesize (which likely is an error)
      echo "<span class='err'>Sicherung fehlgeschlagen. Erstelltes Archiv ist fehlerhaft.</span><br>Mehr Infos <a href='http://hannes-schurig.de/09/05/2015/ftp-backup-skript-in-php/' target='blank'>hier</a><br>";
      // debug
      echo "<p class='grey'>Debug:<br>";
      echo "Pfad: $dir.$archivname<br>";
      echo "Typ: ".gettype($archiv)."<br>";
      echo "Größe: $archivsize MB</p>";
      continue;
   }
   $newbackups[$archivname] = $dir.$archivname;

   echo "Aufräumen der Backups...<br>";
   flush();
   // integer starts at 0 before counting
   $i = 0;
   $backupfiles = array();
   // ######### collect valid backup files
   if ($handle = opendir($dir)) {
      while (($file = readdir($handle)) !== false) {
         if (  is_int(strpos($file, $verzeichnis)) == true &&
                  preg_match('/\.tar.gz$/i', $file) &&
                  !in_array($file, array('.', '..')) &&
                  !is_dir($dir.$file)
         ) {
            $backupfiles[$dir.$file] = filectime($dir.$file);
         }
      }
   }
   echo count($backupfiles)." valide Backups dieses Ordners gefunden, ";
   echo "$backupfilemaximum Backups sollen behalten werden. ";
   $backupcountdif = count($backupfiles)-$backupfilemaximum;
   if ($backupcountdif<=0) {
      echo "Kein Backup wird gelöscht.<br>";
   } else if ($backupcountdif==1) {
      echo "1 Backup wird gelöscht:<br>";
   } else if ($backupcountdif>=2) {
      echo "$backupcountdif Backups werden gelöscht:<br>";
   }
   flush();

   // ######### sort and delete oldest backups
   // sort backup files by date
   arsort($backupfiles);
   // reset counter variable
   $i = 0;
   // delete oldest files
   foreach ($backupfiles as $filepath => $value) {
      if($i>=$backupfilemaximum) {
         echo "$filepath wird gelöscht...<br>";
         if (unlink($filepath)) {
            echo "<span class='ok'>Datei erfolgreich gelöscht.</span><br>";
         } else {
            echo "<span class='err'>Fehler beim Löschen der Datei.</span><br>";
         }
      }
      $i++;
   }
   $jobendtime = time() - $jobtime;
   array_push($backupinfo, array($verzeichnis, $archivname, $archivsize." MB", $jobendtime." Sekunden", date("d.m.Y G:i:s")));
   echo "<span class='ok'>Backup für Verzeichnis $verzeichnis abgeschlossen.</span><br>";
   if (is_int($jobendtime)) {
      echo "######################################## (Dauer: $jobendtime Sekunden)<br>";
      $alltime += $jobendtime;
   } else {
      echo "########################################<br>";
   }
}

if(count($newbackups)>0) {
   echo "<br><span class='ok'>Die automatische Sicherung des Skripts '".pathinfo(__FILE__, PATHINFO_BASENAME)."' hat ".count($newbackups)." Verzeichnisse in insgesamt $alltime Sekunden gesichert.</span><br><br>";
} else {
   echo "<br><span class='err'>Es scheint leider so als wenn keine Backups erfolgreich erstellt wurden.</span><br><br>";
}
flush();

// ######### copy backups to external storages
if (!isset($copytoexternalftp) || $copytoexternalftp== 0 && in_array($copytoexternalftp, array("no", "nein"))) {
   echo "<span class='warn'>Backups werden nicht auf einen externen FTP kopiert. Option ist deaktiviert.</span><br>";
} else {
   $ftptime = time();
   echo "########################################<br>";
   echo "<strong>".count($newbackups)." Backups werden auf externen FTP kopiert...</strong><br>";
   flush();
   $ftpcon = getFtpConnectionByURI($externalftpuri);
   flush();
   if(gettype($ftpcon)=="resource") {
      foreach ($newbackups as $filename => $fullpath) {
         $uploadtime = time();
         echo "Kopiere .$filename. (Größe: $archivsize MB) auf den FTP...<br>";
         flush();
         if (ftp_put($ftpcon, $filename, $filename, FTP_ASCII)) {
            $uploadendtime = time() - $uploadtime;
            if (is_int($uploadendtime)) {
               echo "<span class='ok'>Backup erfolgreich kopiert. (Dauer: $uploadendtime Sekunden)</span><br>";
            } else {
               echo "<span class='ok'>Backup erfolgreich kopiert.</span><br>";
            }
         } else {
            echo "<span class='err'>Fehler beim Kopieren des Backups.</span><br>";
         }
         flush();
      }
   }
   $ftpendtime = time() - $ftptime;
   if (is_int($ftptime)) {
      echo "######################################## (Dauer: $ftpendtime Sekunden)<br>";
   } else {
      echo "########################################<br>";
   }
}
flush();

// ######### send mail
if (!isset($sendmail) || $sendmail== 0 && in_array($sendmail, array("no", "nein"))) {
   echo "<br><span class='warn'>Benachrichtigungsmail wurde nicht verschickt. Option ist deaktiviert.</span><br>";
} else {
   if(!preg_match( '/^([a-zA-Z0-9])+([.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-]+)+/' , $sendmailto)) {
      echo "<br><span class='err'>FEHLER: Mail konnte nicht versendet werden, da die Adresse ungültig ist!</span><br>";
   } else {
      $mailsubject = "Automatische FTP Sicherung abgeschlossen";
      $mailtext = "Die automatische Sicherung des FTP-PHP-Backup-Skripts ".pathinfo(__FILE__, PATHINFO_BASENAME)." hat ".count($foldertobackup)." Verzeichnisse in insgesamt $alltime Sekunden gesichert.<br><br>";
      
      // add backup informations as table to mailtext
      $mailtext .= "<table border=0 style='border-spacing:0;'><thead><tr><th>Verzeichnis</th><th>Dateiname</th><th>Größe</th><th>Dauer</th><th>Timestamp</th></tr></thead><tbody>";
      for($i=0;$i<count($backupinfo);$i++) {
         $mailtext .= "<tr>";
         for($j=0;$j<count($backupinfo[$i]);$j++) {
            $mailtext .= "<td>" . $backupinfo[$i][$j] . "</td>";
         } 
         $mailtext .="</tr>";
      }
      $mailtext .= "</tbody></table>";
      mail(
         $sendmailto,
         $mailsubject,
         $mailtext,
         "From: backupscript@{$_SERVER['SERVER_NAME']}\r\n" . "Reply-To: backupscript@{$_SERVER['SERVER_NAME']}\r\n" . "Content-Type: text/html\r\n"
      ) or die("<br><span class='err'>FEHLER: Mail konnte wegen eines unbekannten Fehlers nicht versendet werden.</span><br>");
      echo "<br><span class='ok'>Benachrichtigungsmail wurde erfolgreich verschickt!</span><br>";
   }
}
flush();

// function to get ftp connection object from URI
// basics were from: http://php.net/manual/de/function.ftp-connect.php#89811
function getFtpConnectionByURI($uri)
{
   // Split FTP URI into:
   // $match[0] = ftp://admin:password@barketing.dns.com:21/Data/FTP-Backups
   // $match[1] = ftp
   // $match[2] = admin
   // $match[3] = password
   // $match[4] = barketing.dns.com
   // $match[5] = 21
   // $match[6] = /Data/FTP-Backups
   preg_match("/([a-z]*?):\/\/(.*?):(.*?)@(.*?):(.*?)(\/.*)/i", $uri, $match);
   
   $ftpcon = null;

   // check if port is set and uri is formatted correctly
   if(is_int(intval($match[5]))) {
      $port = intval($match[5]);

      // check if ftp(s) or sftp is chosen
      if($match[1]=="ftp") {
         // set up and ftp(s) connection, login
         echo "Stelle (FTPs über SSL - Port $port) Verbindung zu FTP Server $match[4] her...<br>";
         // try ftps over ssl, usally through  port 21
         $ftpcon = ftp_ssl_connect($match[4], $port, 30);
         if (!gettype($ftpcon)=="resource") {
            echo "<span class='warn'>FTP über SSL - Port $port - Verbindung fehlgeschlagen!</span><br>";
            echo "Stelle (unsicheres FTP - Port $port) Verbindung zu FTP Server $match[4] her...<br>";
            // try normal insecure ftp
            $ftpcon = ftp_connect($match[4], $port, 30);
         }
         if (gettype($ftpcon)=="resource") {
            $login = ftp_login($ftpcon, $match[2], $match[3]);
            $pasv = ftp_pasv($ftpcon, true);
         }
      } else if($match[1]=="sftp") {
         echo "<span class='err'>SFTP Unterstützung noch nicht implementiert.</span><br>";
         // if(!$port==22) {
         //    echo "<span class='warn'>SFTP Übertragung aber Port ist nicht 22. Ist der gewählte Port $port korrekt?</span><br>";
         // }
         // echo "Stelle (sichere sFTP - Port $port) Verbindung zu FTP Server $match[4]$match[5]:$match[6] her...<br>";
         // $ftpcon = ssh2_connect($match[4], $port, 30);
         // ssh2_auth_password($ftpcon, $match[2], $match[3]);
         // $sftp = ssh2_sftp($ftpcon);
      } else {
         echo "<span class='err'>Kein gültiger Verbindungstyp (ftp/sftp) angegeben.</span><br>";
      }
   } else {
      echo "<span class='err'>Der Port ist fehlerhaft angegeben. Bitte URI prüfen.</span><br>";
   }
   
   if ($ftpcon && gettype($ftpcon)=="resource")
   {
      echo "<span class='ok'>Verbindung hergestellt</span>";
      if ($login) {
         echo "<span class='ok'>, Login erfolgreich</span>";
         if ($pasv) {
            echo "<span class='ok'>, passiver Modus aktiviert</span>";
            if(!isset($match[6]) || $match[6] == "") {
               echo ".<br>";
               return $ftpcon;
            } else if (ftp_chdir($ftpcon, $match[6])) {
               echo "<span class='ok'>, Verzeichniswechsel zu $match[6] erfolgreich.</span><br>";
               return $ftpcon;
            } else {
               echo "<span class='err'>, Verzeichniswechsel zu $match[6] fehlerhaft.</span><br>";
               return null;
            }
         } else {
            echo "<span class='err'>, passiver Modus konnte nicht aktiviert werden. Upload wird trotzdem probiert.</span><br>";
            return $ftpcon;
         }
      } else {
         echo "<span class='err'>, Login fehlgeschlagen.</span><br>";
         return null;
      }
   }
   echo "<span class='err'>Fehler beim Verbinden mit dem FTP Server $match[4]$match[5]$match[6].</span><br>";
   echo "<p class='grey'>Debug:<br>";
   echo "URI (komplett): $match[0]<br>";
   echo "Typ: $match[0]<br>";
   echo "URI ohne Typ: $match[4]<br>";
   echo "Username: $match[2]<br>";
   echo "Passwort: $match[3]<br>";
   echo "Port: $match[5]<br>";
   echo "Unterordner: $match[6]<br>";
   echo "</p>";
   // Or retun null
   return null;
}
// from: http://stackoverflow.com/a/10473026/516047
function startsWith($haystack, $needle) {
   // search backwards starting from haystack length characters from the end
   return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE;
}

?>

Sicherheit: Absicherung mit .htpasswd

Das Verzeichnis, in dem die backup.php und die Backups liegen, sollte natürlich mit einer .htpasswd abgesichert werden. Mit einer eingerichteten .htpasswd Datei ist zuerst ein Login nötig, eh man auf bestimmte Bereiche des Webspaces zugreifen darf:
Das Bild zeigt eine Login Datenabfrage beim Aufruf der Backup URL
Die Datei .htpasswd enthält hierbei die Login Daten und in der .htaccess des Backup Unterordners wird festgelegt, dass eine .htpasswd diesen Ordner schützt. Die .htpasswd generiert ihr euch am besten mit diesem Generator und baut sie dann folgendermaßen in die .htaccess dieses Ordners ein:

AuthType Basic
AuthName &quot;Backups&quot;
AuthUserFile /www/htdocs/all-inkl-account/backup/.htpasswd
Require valid-user

Sicherheit: Absicherung mit .htaccess

Da wir schonmal bei .htaccess sind, erhöhen wir die Sicherheit mit ein paar weiteren grundlegenden Zeilen:

#block access to certain file types
&lt;FilesMatch &quot;.(htaccess|htpasswd|ini|phps|log|sh|tar.gz)$&quot;&gt;
 Order Allow,Deny
 Deny from all
&lt;/FilesMatch&gt;

# disable directory browsing
Options All -Indexes

# prevent basic url hacking stuff
# from: http://www.queness.com/post/5421/17-useful-htaccess-tricks-and-tips
RewriteEngine On
# proc/self/environ? no way!
RewriteCond %{QUERY_STRING} proc/self/environ [OR]
# Block out any script trying to set a mosConfig value through the URL
RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|\%3D) [OR]
# Block out any script trying to base64_encode crap to send via URL
RewriteCond %{QUERY_STRING} base64_encode.*(.*) [OR]
# Block out any script that includes a &lt;script&gt; tag in URL
RewriteCond %{QUERY_STRING} (&lt;|%3C).*script.*(&gt;|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL
RewriteCond %{QUERY_STRING} GLOBALS(=|[|\%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL
RewriteCond %{QUERY_STRING} _REQUEST(=|[|\%[0-9A-Z]{0,2})
# Send all blocked request to homepage with 403 Forbidden error!
RewriteRule ^(.*)$ /index.htm [F,L]

ErrorDocument 401 /backup/index.htm
ErrorDocument 403 /backup/index.htm
ErrorDocument 404 /backup/index.htm
ErrorDocument 500 /backup/index.htm

Dadurch werden Zugriffe auf bestimmte Dateitypen (auch die Backup Dateien), Verzeichnisse und Zugriffe mit sicherheitskritischen Merkmalen unterbunden. Alle diese nicht validen Zugriffe bekommen die index.htm serviert, welches einfach nur eine leere HTML Datei ist. Somit wird den Abfragenden auch kein detaillierter Grund gegeben, warum der Zugriff fehlschlug.

Automatisierung mit All-Inkl Cronjobs

Zu guter Letzt hilft diese Sicherungslösung natürlich nur, wenn sie automatisiert wird. Auch dies ist stark abhängig von eurem Hoster, System, dem Anwendungsbereich usw.
Im Falle von All-Inkl als Webhoster, könnt ihr die Cronjob Funktionalität im KAS (KAS -> Tools -> Cronjobs) benutzen:
Das Bild zeigt die Cronjob Einrichtungsoberfläche von All-Inkl

All-Inkl bietet sogar das Zusenden aller Skriptausgaben. Ihr erhaltet also zusätzlich zu dem kurzen E-Mail-Bericht, der im Skript generiert werden kann, noch eine weitere Mail mit allen Ausgaben. Diese sind dann zwar nicht mehr farbig, aber was solls:
Das Bild zeigt die Cronjob E-Mail mit den Skriptausgaben

Danke an Kenny für das wertvolle Feedback im letzten Artikel, das dazu beigetragen hat, dass ich diese Erweiterung nochmal gepostet habe. Meinst du, dass das jetzt eine Basic Backup Lösung sein könnte? Das einzige, was noch fehlt, ist das Backup Management auf dem externen FTP Server, damit der nicht überläuft. Aber das ist nun wirklich Aufgabe der Admins zu entscheiden und zu verwalten, wie lange Backups aufgehoben werden sollen.

Achtung: Diese Backup Lösung habe ich im nächsten Artikel noch einmal stark erweitert. Ich empfehle diese komplexere Lösung zu benutzen und die darin enthaltenen Sicherheitshinweise zu beachten.

FTP Backups (bisher)

Mein Blog läuft auf dem Hoster ALL-INKL.com, den ich nebenbei gesagt nach mehreren Jahren immernoch empfehlen kann.
Bisher habe ich halbwegs (un)regelmäßig manuell, von Hand, FTP Backups erstellt. Also alle paar Monate habe ich über einen FTP Tool wie FileZilla bestimmte Ordner heruntergeladen, Datei für Datei, anschließend in ein Archiv gepackt und irgendwo verstaut. Dieser Prozess ist natürlich in vielerlei Hinsicht nicht zuverlässig, zeitintensiv und aufwändig.
Auch das Sichern der Ordner automatisiert mit Tools wie SyncBack (mein Artikel dazu) wird immer zeitintensiver, je größer die zu sichernde Datenmenge wird. Außerdem muss immer ein PC, auf dem die Tools laufen, an sein.

FTP Backups via PHP Skript

Sinnvoller ist es, diese Backups auf dem Server des Webhosters erstellen zu lassen. So wird der heimische PC nicht belastet.
All-Inkl bietet dafür ein recht einfaches PHP Skript zum Sichern eines einzelnen FTP Ordners oder des gesamten All-Inkl FTP Accounts.

Ich habe dieses Skript ausgebaut und um verschiedene Funktionen ergänzt:

  • beliebig viele Ordner des All-Inkl Accounts in einzelne .tar.gz Archive sichern
  • Einschränkung der Anzahl aufgehobener Backups – älteste Backups werden automatisch gelöscht
  • detaillierte Ausgabe inklusive benötigter Zeit
  • E-Mail Benachrichtigung

Screenshot

Das Bild zeigt die Ausgaben des Backup Skripts und die versendete E-Mail Benachrichtigung

Code

Achtung: Dieses Backup Skript habe ich in Version 1.1 noch einmal stark erweitert. Ich empfehle diese komplexere Lösung zu benutzen. Besser wäre sogar die Version 1.2, mit der sich zusätzlich MySQL Datenbanken mitsichern lassen.

Code anzeigenDen Code könnt ihr bequem mit den Links/Rechts Pfeiltasten horizontal bewegen.

<?
  // PHP-Konfiguration optimieren
  // if no errors are shown, please check htaccess restrictions by "php_flag display_errors off"
  // in this or parent folders
  @error_reporting(E_ALL);
  @ini_set("max_execution_time", 300);
  @ini_set("memory_limit", "256M");
  header('Content-Type: text/html; charset=utf-8');
  include "Archive/Tar.php";
  $pfad = preg_replace('/(\/www\/htdocs\/\w+\/).*/', '$1', realpath(__FILE__));
  $alltime = 0;

  // ########## EDIT THIS VARIABLES ###################
  $foldertobackup = array("bonnie", "tests", "locationmap", "blog"); // which root folders should get backed up?
  $backupfilemaximum = 2; // how many archives should be stored?
  $dir = $pfad."backup/"; // in which subfolder is this backup php file? this would be: "root/backup/"
  $sendmail = 1; // send notification mail when all backups are done - should be 1/"yes"/"ja" or 0/"no"/"nein"
  $sendmailto = "admin@yourdomain.com"; // valid mail address to send the mail to
  // ##################################################

  foreach ($foldertobackup as $verzeichnis) {
    $jobtime = time();
    echo "<br><br>########################################<br>";
    echo "<strong>Verzeichnis ".$verzeichnis." wird gesichert...</strong><br>";
    flush();

    // Name: [verzeichnis]_[Datum]_[Uhrzeit].tar.gz
    $archivname = $verzeichnis.date('_Y-m-d_His').".tar.gz";
    // Name: [All-Inkl-Accountname]_[Datum]_[Uhrzeit].tar.gz
    //$archivname = preg_replace('/.+\/(.+)\/$/', '$1', $pfad).date('_Y-m-d_His').".tar.gz";

    // Auszuschließende Ressourcen
    $ignorieren = array("*.sql.gz", "*.tar.gz", "usage", "logs");

    // ######### create backup
    $archiv = new Archive_Tar($archivname, true);
    $archiv->setIgnoreList($ignorieren);
    $archiv->createModify($pfad.$verzeichnis, "", $pfad);
    $backuptime = time() - $jobtime;
    if (is_int($backuptime)) {
      echo "Backup fertig: ".$archivname." (Dauer: ".$backuptime." Sekunden)<br>";
    } else {
      echo "Backup fertig: ".$archivname."<br>";
    }

    echo "Aufräumen der Backups...<br>";
    flush();
    // integer starts at 0 before counting
    $i = 0;
    $backupfiles = array();
    // ######### collect valid backup files
    if ($handle = opendir($dir)) {
      while (($file = readdir($handle)) !== false) {
        if (  is_int(strpos($file, $verzeichnis)) == true &&
              preg_match('/\.tar.gz$/i', $file) &&
              !in_array($file, array('.', '..')) &&
              !is_dir($dir.$file)
        ) {
          $backupfiles[$dir.$file] = filectime($dir.$file);
        }
      }
    }
    echo count($backupfiles)." valide Backups dieses Ordners gefunden, ";
    echo $backupfilemaximum." Backups sollen behalten werden. ";
    $backupcountdif = count($backupfiles)-$backupfilemaximum;
    if ($backupcountdif<=0) {
      echo "Kein Backup wird gelöscht.<br>";
    } else if ($backupcountdif==1) {
      echo "1 Backup wird gelöscht:<br>";
    } else if ($backupcountdif>=2) {
      echo $backupcountdif." Backups werden gelöscht:<br>";
    }
    flush();

    // ######### sort and delete oldest backups
    // sort backup files by date
    arsort($backupfiles);
    // reset counter variable
    $i = 0;
    // delete oldest files
    foreach ($backupfiles as $key => $value) {
      if($i>=$backupfilemaximum) {
        echo $key." wird gelöscht...<br>";
        if (unlink($key)) {
          echo "Datei erfolgreich gelöscht.<br>";
        } else {
          echo "Fehler beim Löschen der Datei.<br>";
        }
      }
      $i++;
    }
    $jobendtime = time() - $jobtime;
    if (is_int($jobendtime)) {
      echo "######################################## (Dauer: ".$jobendtime." Sekunden)<br>";
      $alltime += $jobendtime;
    } else {
      echo "########################################<br>";
    }
  }

  echo "<br><br>Die automatische Sicherung des FTP-PHP-Backup-Skripts '".pathinfo(__FILE__, PATHINFO_BASENAME)."' hat ".count($foldertobackup)." Verzeichnisse in insgesamt ".$alltime." Sekunden gesichert.<br><br>";

  // ######### send mail
  if (!isset($sendmail) || $sendmail== 0 && in_array($sendmail, array("no", "nein"))) {
    echo "Benachrichtigungsmail wurde nicht verschickt.";
  } else {
    if(!preg_match( '/^([a-zA-Z0-9])+([.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-]+)+/' , $sendmailto)) {
      echo "FEHLER: Mail konnte nicht versendet werden, da die Adresse ungültig ist!";
    } else {
      mail(
        $sendmailto,
        "Automatische FTP Sicherung abgeschlossen",
        "Die automatische Sicherung des FTP-PHP-Backup-Skripts ".pathinfo(__FILE__, PATHINFO_BASENAME)." hat ".count($foldertobackup)." Verzeichnisse in insgesamt ".$alltime." Sekunden gesichert.",
        "From: backupscript@{$_SERVER['SERVER_NAME']}\r\n" . "Reply-To: backupscript@{$_SERVER['SERVER_NAME']}\r\n" . "Content-Type: text/html\r\n"
      ) or die("FEHLER: Mail konnte wegen eines unbekannten Fehlers nicht versendet werden");
      echo "Benachrichtigungsmail wurde erfolgreich verschickt!";
    }
  }
?>

Wichtige Anmerkungen

Bitte beachtet, dass einige Zeilen angepasst werden müssen und der Code nur bei dem Webhoster All-Inkl getestet wurde. Die Zeile

include "Archive/Tar.php"

, die ein externes Modul einbindet, könnte auf anderen Websern zu Problemen führen. Am besten testet ihr es einfach.
Thema Sicherheit: Wie Kenny in den Kommentaren korrekt angemerkt hat, müssen noch Anpassungen erfolgen, wenn das Skript als tatsächliche Backup-Lösung zum Einsatz kommen soll.

  • Die Sicherungen landen bei diesem Skript direkt in dem Skript-Ordner. Die Sicherung(en) sollte(n) nach der Sicherung an einen anderen Ort, beispielsweise auf einen anderen Server oder ein NAS, kopiert werden. Oder ihr automatisiert den Download der Sicherungen nach dem Backupprozess.
  • Sowohl das Skript als auch die Sicherungen können ohne weitere Maßnahmen direkt angesprochen werden. Ihr solltet natürlich den Backup Ordner beispielsweise durch eine htpasswd absichern und den Zugriff auf die Backups über die htaccess einschränken.

Ich werde dieses Online-Backup Thema in Zukunft vermutlich nochmal aufgreifen, erweitern und optimieren. Ich verlinke das dann hier.

In eigener Sache möchte ich heute kurz den Dienst servercheck24.de unter die Lupe nehmen. Der Dienst ist einer von vielen kostenpflichtigen Server Monitoring Diensten, kann sich in dem Preissegment aber sehen lassen. Im Gegensatz zu vielen Server Monitoring Anbietern hat Servercheck24 gleich 3 Preismodelle und ist somit für viele Anwendungsbereiche geeignet. Es gibt zwar leider kein kostenloses Angebot (was ich definitiv empfehlen würde), eine 14-tägige Testphase kann jedoch einen guten Einblick in die Features geben.
Ich habe mir das mal angesehen.

Coolerweise haben die Betreiber von servercheck24.de direkt nach der Veröffentlichung meines Tests einige angemerkte Stellen ausgebessert und mich informiert. Entsprechende Stellen habe ich im Beitrag angepasst.

Die Registrierung ist schnell gemacht und unkompliziert. Nach der Registrierung bekommt man direkt einen Überblick über die Features. Der „Server“ Menüpunkt ist natürlich die erste Station; hier kann man seinen ersten Server erstellen, bzw. dessen Überwachung. Die Eingabe einer URL ist erforderlich und ein Name wird vergeben.

Vorher: Die Eingabe kann anfangs knifflig werden. Meine ersten Versuche mit http:// am Anfang schlugen direkt fehl, egal welche Kombinationen ich probierte. Viel Hilfe gibt es beim Eingeben auch nicht, so eine Art Formatbeschreibung wie www.[name].[tld] hätte mir ja gereicht. Die URL Eingabe soll nämlich ohne Protokoll sein, nur www. am Anfang. Etwas ungewöhnlich aber verständlich, denn erst im nächsten Schritt wird erst das Protokoll gewählt, dass überwacht werden soll.
Nachher: Die Eingabe der URL sollte problemloser verlaufen, seit dem nun ein kleiner Hinweis unter dem Eingabefeld auf das Format hinweist.
serverueberwachung-uptimechecks-und-mehr-servercheck24-url-neu

serverueberwachung-uptimechecks-und-mehr-servercheck24-overview serverueberwachung-uptimechecks-und-mehr-servercheck24-protocols

Die Protokolle, die man überwachen kann, sind zahlreich. HTTP(S), SSH, FTP, DNS, Mailsprotokolle, TCP, PING, MYSQL, Domains, da ist alles wichtige mit dabei. Von normalen HTTP Abfragen bis hin zu stark modifizierten HTTPS Requests mit Suchbegriffen, Login Credentials, User-Agents, modifiziertem Header und POST Parametern ist da alles einstellbar. Die Möglichkeiten reichen also auch tief in interne, abgesicherte Webbereiche.

Vorher: Traurig: sowohl bei der Eingabe als auch in der Übersicht wird das Passwort der Login Credentials als Klartext dargestellt. C’mon, r’lly? Wenn ein

type="password"

im Eingabefeld schon zu viel Code ist, möchte ich nicht wissen wie die Passwörter in der Datenbank gespeichert sind. Unbedingt ändern!
Nachher: Die Passwörter werden nun maskiert angezeigt, so wie es sich gehört. Die erhöhte Sicherheit wird hoffentlich in der gesamten Programmierung des Dienstes so angestrebt wie bei den Textfeldern.
serverueberwachung-uptimechecks-und-mehr-servercheck24-password1-neu serverueberwachung-uptimechecks-und-mehr-servercheck24-password2-neu

Nach der Eingabe erscheint die Übersicht der Einstellungen, mit den Einstellungen für die Benachrichtigung und den Kontakten, die Benachrichtigungen erhalten sollen. Benachrichtigungen können entweder als E-Mail oder als SMS rausgehen, abhängig vom Status der Webseite. Eine Pager Nachricht ist ebenfalls möglich, sollte jemand noch einen Pager haben.
serverueberwachung-uptimechecks-und-mehr-servercheck24-new-http-pro-server serverueberwachung-uptimechecks-und-mehr-servercheck24-server-overview

Vorher: Leider scheinen SMS bisher nur in den USA möglich zu sein, deutsche Mobilfunkbetreiber sucht man vergeblich.
Nachher: Zugegeben, an dieser Stelle habe ich vermutlich nicht aufmerksam genug geschaut. Die Landesvorwahl +49 für Deutschland ist vorhanden und enthält auch die größeren hiesigen Netzbetreiber. Neu ist jetzt, dass sowohl beim Erstellen eines Accounts als auch beim Bearbeiten von Kontakteinstellungen automatisch die passende Landesvorwahl ausgewählt wird. Ich musste also +49 nicht einmal auswählen; das geschieht jetzt automatisch. Es funktioniert alles problemlos, TOP!
serverueberwachung-uptimechecks-und-mehr-servercheck24-sms serverueberwachung-uptimechecks-und-mehr-servercheck24-sms2

Weitere Protokolle und Server sind schnell erstellt und die eigene Weblandschaft in wenigen Minuten zur Überwachung konfiguriert. Dabei werden alle Protokolle eines Servers in eine „Gruppierung“ gesteckt. Ein neuer Server ist mit seinen Protokollen eine neue Gruppe. Mehrere logisch zusammengehörige Server zu einer Gruppe oder Art Kategorie zusammenzufügen geht leider nicht.
Für die Übersicht aller Server gibt es sowohl eine ausführliche und eine kurze Version.
serverueberwachung-uptimechecks-und-mehr-servercheck24-short-overview serverueberwachung-uptimechecks-und-mehr-servercheck24-detailed-overview

Zwei weitere nette Features sind der grafische Verlauf und der Auswertungsversand.
Ersteres zeigt den Status eines Servers in einem bestimmten Zeitraum grafisch an. Leider lassen sich hier nicht mehrere Protokolle oder Server in eine Grafik packen. Die Daten lassen sich in typische Dateiformate exportieren. Und die Auswertungen sind konfigurierbare regelmäßige Reports per E-Mail.
serverueberwachung-uptimechecks-und-mehr-servercheck24-daily-report serverueberwachung-uptimechecks-und-mehr-servercheck24-grafic-status

Fazit? Servercheck24 bietet ein recht angenehmes Preis-Leistungs-Verhältnis. Wer nicht viele Seiten zu überwachen hat kann mit 5€/Monat schon alle Features nutzen, mit einem 10 Minuten Intervall allerdings. Ich habe mal nach der Konkurrenz gesehen und nur sehr wenige (und auch wenig brauchbare) kostenlose Angebote, dafür aber auch viele recht teure Angebote gefunden.

Vorher: Wenn Servercheck24 jetzt noch die paar Kinderkrankheiten (ich glaube der Dienst ist erst vor ca 2, 3 Jahren online gegangen) ausbessern kann, sehe ich große Chancen in dem Markt.
Nachher: Innerhalb von 1 Tag haben die Entwickler einige Punkte ausgebessert und ich habe das Gefühl, dass in diesen Dienst noch sehr viel Energie fließen wird. Hut ab, Daumen hoch, go for it!

Interessiert? Einfach für einen Tarif registrieren und den Testzeitraum nutzen.

Was Google erschafft ist grundsätzlich erstmal einen Blick wert und weiß dann oft zu überzeugen. Also, werfen wir einen Blick auf das neue WebP Bildformat-Standard, entwickelt von Google. Vor wenigen Tagen wurde die neue Version 0.2 veröffentlicht.

Die Vorteile:

  • verlustfreie Kompression von Bildern im Web
  • 26% kleiner als PNG, 25-34% kleiner als JPG bei gleichem SSIM Index
  • verlustfreie Transparenz mit nur 22% mehr Bytes

Der neue Standard verspricht also nichts weiter als die Weiterentwicklung des Internets durch die Verbesserung der verwendeten Bilder. Bei gleicher Qualität sollen diese kleiner werden und somit die Geschwindigkeit des Seitenaufbaus auf Seiten des Nutzers erhöhen und eine Reduzierung von Traffic und damit auch Kosten auf Seiten des Webseitenbetreibers erreichen.

Wie werden WebP Bilder erstellt?

Einige Bildbearbeiter können bereits mit WebP umgehen: Pixelmator, ImageMagick, GIMP (Plugin), Leptonica, XnConvert, XnView, Irfan View, Picasa (ab 3.9), Photoshop (Plugin) und Weitere.
Oder auch: der WebP Codec für Windows ermöglichst durch die Installation das Betrachten von WebP Bildern mit jedem Programm und die WebP Tools für x86 oder x64 (inkl. header und library files) ermöglichen das Erstellen von WebP Bildern ohne spezielle Software.
Das Toolpaket enthält ein Programm

cwebp.exe

, welches gängige Bildformate in Webp umwandelt. Siehe Readme Datei für mehr Informationen.

Ich habe hier eine BMP Grafik in JPG (80% Qualität), PNG (Compression Stufe 6) und WebP (80% Qualität) umgewandelt. Das WebP Bild ist 30% kleiner als das JPG und 60% kleiner als das PNG.

Wie wird WebP verwendet?

Webseitenbetreiber können in Zukunft einfach WebP Bilder statt PNG/JPG in ihre Webseiten einbauen. Problem ist nur: die Unterstützung von WebP ist noch relativ klein. Google Chrome 9+ und Opera 11.10+ unterstützen direkt den neuen Standard, Firefox lässt sich mit einem Patch dazu bewegen. Für alle anderen Browser gibt es Javascript Lösungen aber das soll natürlich kein Zustand bleiben.
Ich als Webmaster hoffe, dass bald mehr und mehr Browser WebP integrieren und somit den Trend dieses Formats unterstützen.

Wie sieht das dann aus?

Nicht anders als sonst, wenn alles klappt.
Auch hier in meinem Blog möchte ich jetzt mal ein WebP Bild nutzen. Wer es sieht; herzlichen Glückwunsch, du nutzt einen modernen Browser 😉
[Bild]
ea-logo-webp-test
[/Bild]

Mein Blog entstand Januar 2009 aus einem Hauptgrund: ich wollte Probleme, Anleitungen und Tricks&Tipps zu Themen niederschreiben, mit denen ich mich befasst habe. Hilfesuchende aus allen Teilen des Internets sollten in meinem Blog eine Lösung oder Herangehensweisen finden. Mit monatlich über 40.000 Besuchern, davon über 80% aus Suchmaschinen, habe ich das Ziel in meinen Augen erreicht.

Mein Haupttraffic kommt aus den Suchmaschinen. Gewollt.
Wie optimiert man den eigenen Blog für Suchmaschinen?

3 Wege bieten sich spontan an:

  • Plugins installieren, die automatisch oder mit deren Unterstützung Suchmaschinenoptimierung am Blog und jedem einzelnen Artikel durchgeführt werden.
  • Manuelles Coding in den tiefen von WordPress; Metatags, saubere Struktur, HTML5 Optimierung, Code säubern.
  • Professionelle Suchmaschinenoptimierung von externen Firmen oder Freelancern/Dienstleistern, die auf hohem Niveau in mehreren Schritten analysieren und optimieren.

Ich habe die beiden ersten Punkte angwandt, da kostenpflichtige Optimierungen für diesen No-Budget Blog für mich nicht möglich sind.

Meine Plugins für diese Zwecke sind:

  • All in One SEO Pack – optimiert mehrere Blogeigenschaften, Anzeigeformate der Artikellinks, ermöglicht das Setzen von Headers usw
  • Dagon Design Sitemap Generator – generiert eine konfigurierbare Sitemap mit allen Unterseiten, Kategorien und Artikeln (FYI: Sitemaps sind gut für SEO)
  • SEO Friendly Images – ergänzt Bilder um SEO relevante Informationen

Am Code habe ich nur grundlegend etwas gefummelt, META Tags aller Art und solche einfachen Dinge. Quellcode lesen wenn es euch interessiert.

Welche Methoden und Plugins nutzt ihr in eurem Blog für die SEO?