Keiner der AJAX-Endpunkte hat einen Login geprueft. Ohne Anmeldung liessen sich Kunden, Adressen und Rechnungen durchsuchen, und ajax/autosave.php setzt $_POST ungeprueft in SQL (SQL-Injection ohne Login). - incs/connectmysql.php und ajax/auto/connectmysql.php, die alle AJAX-Endpunkte laden, antworten ohne gueltige Session mit 403 - stats.php verlangt eine Anmeldung - db-backup.php und clean_db.php laufen nur noch per Kommandozeile; ueber den Webserver konnte jeder einen Datenbank-Dump anstossen Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
135 lines
3.6 KiB
PHP
135 lines
3.6 KiB
PHP
<?php
|
|
// Nur per Kommandozeile (Cronjob), nie über den Webserver
|
|
if(PHP_SAPI != "cli")
|
|
exit;
|
|
/*
|
|
* Datenbank-Backup (Cronjob).
|
|
*
|
|
* Die Zugangsdaten kommen - wie im Rest der Anwendung - aus der externen
|
|
* Konfigurationsdatei (CONFIG_PATH/CONFIG_FILE, siehe incs/constants.php)
|
|
* und stehen nicht mehr hartkodiert in dieser Datei.
|
|
*
|
|
* ACHTUNG: constants.php leitet CONFIG_FILE aus dem aktuellen Arbeits-
|
|
* verzeichnis ab. Der Cronjob muss deshalb im Installationsverzeichnis
|
|
* gestartet werden:
|
|
*
|
|
* cd /home/intranet/kunden && php db-backup.php
|
|
*/
|
|
|
|
require_once(__DIR__."/incs/constants.php");
|
|
require_once(__DIR__."/incs/settings.php");
|
|
|
|
function rrmdir($dir) {
|
|
if (is_dir($dir)) {
|
|
$objects = scandir($dir);
|
|
foreach ($objects as $object) {
|
|
if ($object != "." && $object != "..") {
|
|
if (filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object); else unlink($dir."/".$object);
|
|
}
|
|
}
|
|
reset($objects);
|
|
rmdir($dir);
|
|
}
|
|
}
|
|
|
|
/* Wert fuer eine MySQL-Optionsdatei maskieren */
|
|
function cnfquote($val) {
|
|
return '"'.str_replace(array("\\", '"'), array("\\\\", '\\"'), $val).'"';
|
|
}
|
|
|
|
function fail($msg) {
|
|
fwrite(STDERR, "db-backup: ".$msg."\n");
|
|
exit(1);
|
|
}
|
|
|
|
if(readsettings() != "valid")
|
|
fail("Konfiguration ".CONFIG_PATH.CONFIG_FILE." nicht lesbar oder unvollstaendig.");
|
|
|
|
$path = "/home/intranet/backup/datenbank/";
|
|
|
|
/* $mysql_db kommt aus der Konfiguration, "angebot" wurde hier schon
|
|
* immer zusaetzlich gesichert. */
|
|
$backup_dbs = array_values(array_unique(array_filter(array($mysql_db, "angebot"))));
|
|
|
|
/* Das Passwort nicht als Kommandozeilenargument uebergeben - es waere
|
|
* sonst fuer jeden lokalen Benutzer in der Prozessliste sichtbar. */
|
|
$cnf = tempnam(sys_get_temp_dir(), "dbbak");
|
|
if($cnf === false)
|
|
fail("Temporaere Optionsdatei konnte nicht angelegt werden.");
|
|
chmod($cnf, 0600);
|
|
if(file_put_contents($cnf,
|
|
"[client]\n".
|
|
"host = ".cnfquote($mysql_server)."\n".
|
|
"user = ".cnfquote($mysql_user)."\n".
|
|
"password = ".cnfquote($mysql_pw)."\n") === false)
|
|
{
|
|
unlink($cnf);
|
|
fail("Temporaere Optionsdatei konnte nicht geschrieben werden.");
|
|
}
|
|
|
|
$day = date("d");
|
|
$month = date("M");
|
|
$year = date("Y");
|
|
$stamp = date("d-m-Y");
|
|
|
|
/* Verzeichnis von vor zwei Monaten aufraeumen. Monat UND Jahr aus dem
|
|
* selben Zeitpunkt ableiten, sonst wird zum Jahreswechsel ein falscher
|
|
* Pfad geloescht. */
|
|
$llast = strtotime("-2 month");
|
|
$llastdir = $path.date("Y", $llast)."-".date("M", $llast);
|
|
|
|
if(!is_dir($path))
|
|
fail("Backup-Verzeichnis ".$path." existiert nicht.");
|
|
|
|
$target = $path.$year."-".$month."/".$day;
|
|
if(!is_dir($target) && !mkdir($target, 0700, true))
|
|
{
|
|
unlink($cnf);
|
|
fail("Zielverzeichnis ".$target." konnte nicht angelegt werden.");
|
|
}
|
|
|
|
if(is_dir($llastdir) && $llastdir != $path.$year."-".$month)
|
|
rrmdir($llastdir);
|
|
|
|
$dumps = array();
|
|
$failed = 0;
|
|
|
|
foreach($backup_dbs as $db)
|
|
{
|
|
$sql = $target."/".$db."_".$stamp.".sql";
|
|
|
|
$cmd = "mysqldump --defaults-extra-file=".escapeshellarg($cnf).
|
|
" --result-file=".escapeshellarg($sql)." ".escapeshellarg($db);
|
|
exec($cmd." 2>&1", $out, $rc);
|
|
if($rc !== 0)
|
|
{
|
|
fwrite(STDERR, "db-backup: mysqldump fuer '".$db."' fehlgeschlagen: ".implode(" ", $out)."\n");
|
|
@unlink($sql);
|
|
$failed++;
|
|
continue;
|
|
}
|
|
|
|
exec("gzip -9 -f ".escapeshellarg($sql)." 2>&1", $out2, $rc2);
|
|
if($rc2 !== 0)
|
|
{
|
|
fwrite(STDERR, "db-backup: gzip fuer '".$db."' fehlgeschlagen: ".implode(" ", $out2)."\n");
|
|
$failed++;
|
|
continue;
|
|
}
|
|
|
|
$dumps[] = $sql.".gz";
|
|
}
|
|
|
|
unlink($cnf);
|
|
|
|
/* "current" auf die Dumps von heute zeigen lassen */
|
|
rrmdir($path."current");
|
|
if(!mkdir($path."current", 0700, true))
|
|
fail("Verzeichnis ".$path."current konnte nicht angelegt werden.");
|
|
|
|
foreach($dumps as $dump)
|
|
link($dump, $path."current/".basename($dump));
|
|
|
|
exit($failed ? 1 : 0);
|
|
?>
|