Datenbank-Backup erstellen und Datenbank-Backup wieder einspielen

Heute erkläre ich wie man eine Datenbank von einem zum anderen Server transferiert bzw. wie man ein Datenbank-Backup erstellen und dieses wieder einspielt.

Datenbank-Backup erstellen

Variante #1: Konsole

Damit man ein Datenbank-Backup über die Konsole erstellen kann, muss man sich zunächst per SSH auf dem Server anmelden.

ssh benutzer@serveradresse

Anschließend braucht man nur einen einzigen Befehl um das Backup der DB anzulegen:

mysqldump -u *benutzername* -p *datenbankname* > /pfad/mysql_backup.sql

Wer möchte kann das Backup dann noch komprimieren, damit die Datei schneller übertragen wird:

tar cfz mysql_backup.sql.tar.gz mysql_backup.sql

Zum Schluss kann die Datei noch heruntergeladen werden oder – wenn das Backup auf einen anderem Server eingespielt werden soll – auch direkt auf den Ziel-Server:

scp benutzer@serveradresse:/pfad/mysql_backup.sql.tar.gz  mysql_backup.sql.tar.gz
scp mysql_backup.sql.tar.gz benutzer@serveradresse2:/anderer/pfad/mysql_backup.sql.tar.gz

Ersteres erfordert natürlich, dass zuvor die SSH-Verbindung wieder getrennt wird.

Variante #2: phpMyAdmin

Diese Variante ist wesentlich einfacher als über die Konsole,  aufgrund der schlechten Geschwindigkeit für größere Datenbanken allerdings weniger geeignet. Alles was gebraucht wird, ist über phpMyAdmin die Datenbank auszuwählen, auf den Reiter Exportieren zu klicken und folgende Einstellungen zu wählen:

  • Häkchen bei Kommentare entfernen
  • Senden anhaken
  • Als Kompression GZip wählen

PhpMyAdmin bietet die Datenbank anschließend als Download an.
Die Export-Datei muss jetzt noch auf den anderen Server hochgeladen werden. Siehe dazu die Befehle am Ende von Variante #1.

Datenbank-Backup einspielen

Um das DB-Backup wieder einzuspielen, muss man sich auch hier wieder Konsolen-Zugriff auf dem Zielsystem haben (z.B. per SSH). Doch bevor wir das Backup einspielen sollte in erst ein Backup der alten Datenbank angelegt werden (man weiß ja nie).

mysqldump -u *benutzername* -p *datenbankname* > /pfad/mysql_backup_orig.sql

Jetzt muss die Backup-Datei natürlich noch dekomprimiert werden:

tar xfz mysql_backup.sql.tar.gz

Achtung: Wenn ein komprimierter PhpMyAdmin-Export eingespielt werden soll, lautet der Befehl zum Dekomprimieren (Dateiendung .sql.gz):

gunzip mysql_backup.sql.gz

Und jetzt noch der finale Befehl zum Einspielen des Backups:

mysql -u *benutzername* -p *datenbankname* < mysql_backup.sql

Nach der Passworteingabe ist das Backup fertig eingespielt.

Warum nicht per phpMyAdmin?

Jetzt fragen sich natürlich einige zu Recht, warum soll ich das Datenbank-Backup nicht wieder über phpMyAdmin einspielen?
Wenn die Datenbank recht klein ist, macht das durchaus Sinn, weil es so natürlich komfortabler und schneller geht, aber sobald man größere Datenbanken bewegen muss, ist die Konsole die wesentlich performantere und sichere Variante. Der PHP-Interpreter und der HTTP-Upload verlangsamen den Import über phpMyAdmin ungemein.

Macports OSX: Shell-Skript zum starten Apache und MySQL

Wenn man auf Mac OSX via Macports Apache und MySQL installiert hat, ist es relativ umständlich jedes mal beide Dienste zu starten. Es gibt zwar die Möglichkeit Apache & MySQL beim Starten des Betriebssystems mit starten zu lassen, aber dann laufen diese immer und verbrauchen entsprechend Ressourcen. Aus dem Grund habe ich ein kleines Shell-Skript geschrieben, das diese Aufgabe erleichtert:

#! /bin/sh
 
if [[ $EUID -ne 0 ]]; then
    echo "This script must be run as root." 1>&2
    exit 1
fi
 
command=$1
 
if [ $# -eq 0 ]
then
    echo "Please provide a command: start, restart, stop"
    read command
fi
 
if [ $command = "start" -o $command = "restart" -o $command = "stop" ]
then
    /opt/local/etc/LaunchDaemons/org.macports.apache2/apache2.wrapper $command
    /opt/local/etc/LaunchDaemons/org.macports.mysql5/mysql5.wrapper $command
else
    echo "Unkown command provided."
fi

Anschließend lässt sich das Skript über folgenden Terminal-Befehl ausführen:

sudo /bin/sh server.sh start
Starting MySQL
... SUCCESS!

Jetzt laufen sowohl Apache, als auch MySQL.

Statt start können natürlich auch die Parameter restart und stop verwendet werden. Fehlt der Parameter komplett, fragt das Skript nochmal nach.

PHP > SQL-Injection > Best Practice

Zur Beginn erst noch eine kleine Einführung in SQL-Injections.

Was ist eine SQL-Injection?
Wenn Jemand versucht eine Benutzereingabe so zu manipulieren, dass der generierte SQL-Befehl etwas anderes tut, als er soll. Nehmen wir mal folgendes Szenario an: Wir schicken eine URL an unseren WordPress-Blog, die einen zusätzlichen SQL-Befehl ausführt – nämlich die Tabelle xyz löschen.

http://web-union.de/?id=1; DROP TABLE xyz; --

Das generierte SQL-Query würde dann so aussehen:

$sql = "SELECT * FROM posts WHERE id = '{$_GET['id']}'";
SELECT * FROM posts WHERE id = '1'; DROP TABLE xyz; --';

Mit den letzten beiden Strichen wird das schließenden Anführungszeichen und das Semikolon am Ende der SQL-Query einfach auskommentiert.

Und wie lassen sich SQL-Injections effektiv verhindern?
Dazu gibt es einige Methoden, die ich unter anderem auch für Yumee verwende.

1. Benutzereingaben escapen
Das ist relativ schnell gemacht und sollte im Normalfall genug Schutz gegen Angreifer bieten. Ein blinder Angriff wird somit eigentlich ausgeschlossen.

$_GET = array_map('mysql_real_escape_string', $_GET);
$_POST = array_map('mysql_real_escape_string', $_POST);
$_COOKIE = array_map('mysql_real_escape_string', $_COOKIE);

Wer möchte kann so zusätzlich noch $_REQUEST und $_FILES escapen.

Damit diese Methode wirklich sicher funktioniert, muss bei der Verwendung noch einiges beachtet werden:

  • Die Benutzereingaben innerhalb der SQL-Query sollten immer mittels Hochkommata umschlossen werden. Wenn dies nicht möglich ist, unbedingt den Inhalt vorher prüfen (z.B. bei Feldernamen).
  • Auf keinen Fall die Eingaben mit einer anderen Funktion escapen. addslashes() und magic_quotes_gpc sind nicht sicher können ausgetrickst werden.

2. Value Binding mit PDO
Die sicherste Lösung ist wahrscheinlich das Value-Binding. Dabei werden die Werte erstmal durch Platzhalter vorbelegt und später von der PDO-Erweiterung aus PHP eigenständig ersetzt und vorher natürlich escaped. Hier mal ein kleines Beispiel:

$pdo = new PDO('mysql:dbname=DatenbankName;host=localhost', 'benutzer', 'ultra_sicheres_passwort');
$stmt = $pdo->prepare('SELECT * FROM blog_posts WHERE post_title LIKE ?');
$stmt->bindValue(1, $_POST['suche'], PDO::PARAM_STR);
if($stmt->execute())
{
	$result = $stmt->fetchAll();
}

Hier müssen keine Anführungszeichen oder Hochkommata verwendet werden. Die werden später von PDO automatisch – abhängig vom angegebenen Datentyp – gesetzt. Die MySQLi-Extension besitzt nebenbei bemerkt eine ähnliche Funktion.
Diese Lösung bedeutet zwar etwas mehr Code, dafür ist sie aber sauberer und bedeutend sicherer.

3. PHP IDS
Ich hatte bereits vor einiger Zeit über PHP IDS berichtet. Dabei handelt es sich um eine Bibliothek, die vor die eigentliche Anwendung geschaltet wird und vor dem Start sämtliche Benutzereingaben überwacht. Dabei werden nicht nur SQL-Injections erkannt, sondern auch XSS und andere schädliche Eingaben. Eventuelle Angriffe können so geloggt werden, oder auch direkt per Mail an einen Administrator verschickt werden.

Weitere Tipps und Best Practice:

  • Wenn ihr sicher seid, dass die Eingabe eine Zahl sein muss, reicht es auch die Variable in eine Zahl umzuwandeln (z.B. mit dem (int)-Cast). Das ist sicherer als jedes Escapen.
  • Schreibt sauberes SQL und umschließt am besten alles in Hochkommata was möglich ist. Nur so kann das Escapen wirklich funktionieren.
  • Verlasst euch nicht auf die PHP-internen Funktionen magic_quotes_runtime und magic_quotes_gpc. Diese sind seit PHP 5.3 sowieso veraltet und werden in PHP 6 mangels Sicherheit komplett entfernt.
  • Wer wirklich sicher gehen will, überprüft immer alle Eingaben vom Benutzer, ob diese überhaupt einen gültigen Wert haben. Unbedingt auch Tabellen- und Spaltennamen beachten, eben alles was vom Benutzer zur Datenbank geleitet wird.