Instanzname in die Config, Setup-Luecke geschlossen
Instanz: - $instanz in der externen Config bestimmt INSTANZ, UPLOADS_PATH und SESSION_NAME. Der Verzeichnisname waehlt nur noch die Config-Datei aus und ist Fallback, solange $instanz fehlt. - savePDF() schreibt nicht mehr fest nach DOCUMENT_ROOT."kunden", sondern in das Verzeichnis, das es auch aufraeumt. .gitkeep wird nicht mehr mitgeloescht. Sicherheit: - index.php?action=setup war ohne Login auch bei gueltiger Config erreichbar. rebuild-settings.php schrieb $_POST ungeprueft als PHP-Code in die Config (Codeausfuehrung, sofern die Datei schreibbar war). - Setup nur noch ohne gueltige Config, firststart.php und rebuild-settings.php pruefen das zusaetzlich selbst. - Config-Werte werden per var_export() maskiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,19 +14,22 @@ Syntaxprüfung der eigenen Dateien:
|
|||||||
for f in $(git ls-files "*.php" | grep -vE "^(incs/(tcpdf|pChart|PHPMailer|WebAuthn|drawer|font)|smarty|spellcheck|js)/"); do php -l "$f"; done
|
for f in $(git ls-files "*.php" | grep -vE "^(incs/(tcpdf|pChart|PHPMailer|WebAuthn|drawer|font)|smarty|spellcheck|js)/"); do php -l "$f"; done
|
||||||
```
|
```
|
||||||
|
|
||||||
Zum Ausführen braucht es einen Webserver mit PHP, eine MariaDB/MySQL-Instanz und die externe Konfigurationsdatei (siehe unten). Ohne die Konfiguration springt `index.php` in den Setup-Modus (`update/firststart.php`).
|
Zum Ausführen braucht es einen Webserver mit PHP, eine MariaDB/MySQL-Instanz und die externe Konfigurationsdatei (siehe unten). Ohne die Konfiguration springt `index.php` in den Setup-Modus (`update/firststart.php`). **Nur** dann: Das Setup läuft ohne Login, schreibt die Config und kann die Datenbank leeren. Deshalb wird `action=setup` bei gültiger Config verworfen, und `firststart.php` und `rebuild-settings.php` brechen selbst ab. Werte, die in die Config geschrieben werden, immer über `var_export()` maskieren – die Config ist ausführbarer PHP-Code.
|
||||||
|
|
||||||
## Die zwei zentralen Mechanismen
|
## Die zwei zentralen Mechanismen
|
||||||
|
|
||||||
### 1. Das Verzeichnis bestimmt die Instanz
|
### 1. Das Verzeichnis bestimmt die Instanz
|
||||||
|
|
||||||
`incs/constants.php` leitet aus `getcwd()` den Namen des Installationsverzeichnisses ab und baut daraus:
|
`incs/constants.php` leitet aus `getcwd()` den Namen des Installationsverzeichnisses ab. Der wählt aber nur noch die Konfigurationsdatei aus. Der eigentliche Instanzname steht in der Config als `$instanz` und wird zur Konstante `INSTANZ`:
|
||||||
|
|
||||||
| Konstante | Wert bei Installation in `…/kunden` |
|
| Konstante | abgeleitet aus | Wert bei Installation in `…/kunden` |
|
||||||
|---|---|
|
|---|---|---|
|
||||||
| `CONFIG_FILE` | `kunden_settings.conf` |
|
| `CONFIG_FILE` | Verzeichnisname | `kunden_settings.conf` |
|
||||||
| `UPLOADS_PATH` | `uploads_kunden/` |
|
| `INSTANZ` | `$instanz` in der Config | `kunden` |
|
||||||
| `SESSION_NAME` | `KUNDENSESS` |
|
| `UPLOADS_PATH` | `INSTANZ` | `uploads_kunden/` |
|
||||||
|
| `SESSION_NAME` | `INSTANZ` | `KUNDENSESS` |
|
||||||
|
|
||||||
|
Fehlt `$instanz` in der Config (ältere Configs, Ersteinrichtung), fällt `INSTANZ` auf den Verzeichnisnamen zurück. Erlaubt sind nur Buchstaben und Ziffern, sonst bricht der Request ab. Die Config wird dafür schon in `constants.php` einmal eingelesen, also **vor** `session_start()`. Code in der Config darf deshalb nichts ausgeben und keine Verbindung voraussetzen.
|
||||||
|
|
||||||
Aus **einer** Codebasis laufen dadurch mehrere getrennte Instanzen — die `uploads_dev`, `uploads_ingwa`, `uploads_kunden`, `uploads_lsg` im Baum sind die Spuren davon. Jede Instanz hat eigene Config, eigene Session, eigene Uploads.
|
Aus **einer** Codebasis laufen dadurch mehrere getrennte Instanzen — die `uploads_dev`, `uploads_ingwa`, `uploads_kunden`, `uploads_lsg` im Baum sind die Spuren davon. Jede Instanz hat eigene Config, eigene Session, eigene Uploads.
|
||||||
|
|
||||||
@@ -42,7 +45,7 @@ cd /home/intranet/kunden && php db-backup.php
|
|||||||
|
|
||||||
`CONFIG_PATH` zeigt auf `/volume1/homes/wagner/kundenconfig/` (Linux) bzw. `c:/kundenverwaltung/config/` (Windows). Geschrieben wird die Datei von `update/rebuild-settings.php` beim Ersteinrichten.
|
`CONFIG_PATH` zeigt auf `/volume1/homes/wagner/kundenconfig/` (Linux) bzw. `c:/kundenverwaltung/config/` (Windows). Geschrieben wird die Datei von `update/rebuild-settings.php` beim Ersteinrichten.
|
||||||
|
|
||||||
Die Datei deklariert `$mysql_user`, `$mysql_pw`, `$mysql_server`, `$mysql_db`, `$firma` und weitere ausdrücklich als `global` und weist sie erst danach zu. Nur deshalb funktioniert `readsettings()` in `incs/settings.php`: Das `include` passiert im Funktionsrumpf, ohne die `global`-Zeilen blieben die Variablen dort lokal. Wer die Config-Struktur ändert, muss diese Eigenheit erhalten.
|
Neben den Zugangsdaten steht dort auch `$instanz` (siehe oben). Die Datei deklariert `$mysql_user`, `$mysql_pw`, `$mysql_server`, `$mysql_db`, `$firma` und weitere ausdrücklich als `global` und weist sie erst danach zu. Nur deshalb funktioniert `readsettings()` in `incs/settings.php`: Das `include` passiert im Funktionsrumpf, ohne die `global`-Zeilen blieben die Variablen dort lokal. Wer die Config-Struktur ändert, muss diese Eigenheit erhalten.
|
||||||
|
|
||||||
Zugangsdaten gehören **nie** in den Code — `db-backup.php` zeigt das Muster für Skripte außerhalb des Request-Zyklus (Config einlesen, Passwort über eine temporäre Optionsdatei an `mysqldump`, nicht per Kommandozeile).
|
Zugangsdaten gehören **nie** in den Code — `db-backup.php` zeigt das Muster für Skripte außerhalb des Request-Zyklus (Config einlesen, Passwort über eine temporäre Optionsdatei an `mysqldump`, nicht per Kommandozeile).
|
||||||
|
|
||||||
|
|||||||
+28
-2
@@ -12,8 +12,34 @@ if(DIRECTORY_SEPARATOR == "/")
|
|||||||
else
|
else
|
||||||
define("CONFIG_PATH", "c:/kundenverwaltung/config/");
|
define("CONFIG_PATH", "c:/kundenverwaltung/config/");
|
||||||
define("CONFIG_FILE", $dir."_settings.conf");
|
define("CONFIG_FILE", $dir."_settings.conf");
|
||||||
define("UPLOADS_PATH", "uploads_".$dir."/");
|
|
||||||
define("SESSION_NAME",strtoupper($dir)."SESS");
|
/*
|
||||||
|
* Der Verzeichnisname wählt nur noch aus, welche Konfigurationsdatei
|
||||||
|
* geladen wird. Der Name der Instanz steht dort als $instanz und bestimmt
|
||||||
|
* Uploads-Verzeichnis und Session-Name. Fehlt $instanz (ältere Configs,
|
||||||
|
* Ersteinrichtung), gilt wie bisher der Verzeichnisname.
|
||||||
|
*/
|
||||||
|
function instanzAusKonfiguration($datei, $fallback)
|
||||||
|
{
|
||||||
|
if(!is_file($datei))
|
||||||
|
return $fallback;
|
||||||
|
// Leerzeilen hinter dem schließenden PHP-Tag der Config würden sonst
|
||||||
|
// ausgegeben und den Session-Cookie verhindern, weil das hier vor
|
||||||
|
// session_start() läuft.
|
||||||
|
ob_start();
|
||||||
|
include($datei);
|
||||||
|
ob_end_clean();
|
||||||
|
if(!isset($instanz) || $instanz === "")
|
||||||
|
return $fallback;
|
||||||
|
// Landet in Dateipfaden und im Session-Namen (dort nur alphanumerisch zulässig).
|
||||||
|
if(!is_string($instanz) || !preg_match('/^[A-Za-z0-9]+$/', $instanz))
|
||||||
|
die("Ungültiger Instanzname in ".$datei.": nur Buchstaben und Ziffern erlaubt.");
|
||||||
|
return $instanz;
|
||||||
|
}
|
||||||
|
|
||||||
|
define("INSTANZ", instanzAusKonfiguration(CONFIG_PATH.CONFIG_FILE, $dir));
|
||||||
|
define("UPLOADS_PATH", "uploads_".INSTANZ."/");
|
||||||
|
define("SESSION_NAME",strtoupper(INSTANZ)."SESS");
|
||||||
define("RIGHTS_KUNDEN", 0);
|
define("RIGHTS_KUNDEN", 0);
|
||||||
define("RIGHTS_TERMINE", 1);
|
define("RIGHTS_TERMINE", 1);
|
||||||
define("RIGHTS_ANGEBOTE", 2);
|
define("RIGHTS_ANGEBOTE", 2);
|
||||||
|
|||||||
+12
-8
@@ -1107,13 +1107,19 @@ function writeZahlziel($ktonr,$zahlziel,$gespreis,$wahrung,$rechdate,$kundeid)
|
|||||||
|
|
||||||
function savePDF($pdf,$PDFpath)
|
function savePDF($pdf,$PDFpath)
|
||||||
{
|
{
|
||||||
$PDFpathpart = dirname($PDFpath)."/";
|
// TCPDF stellt beim Schreiben file:// voran und braucht deshalb einen
|
||||||
$PDFfilepart = basename($PDFpath,".pdf");
|
// absoluten Pfad. Aufgelöst wird relativ zum Arbeitsverzeichnis, also in
|
||||||
|
// der Installation, aus der der Aufruf kommt - früher stand hier fest
|
||||||
|
// DOCUMENT_ROOT."kunden".
|
||||||
|
$PDFpathpart = realpath(dirname($PDFpath));
|
||||||
|
if($PDFpathpart === false || !is_dir($PDFpathpart))
|
||||||
|
die("PDF-Verzeichnis nicht gefunden: ".dirname($PDFpath));
|
||||||
|
$PDFpathpart = str_replace("\\", "/", $PDFpathpart)."/";
|
||||||
$handle=opendir($PDFpathpart);
|
$handle=opendir($PDFpathpart);
|
||||||
while ($file = readdir($handle))
|
while ($file = readdir($handle))
|
||||||
{
|
{
|
||||||
if (preg_match("=^\.{1,2}$=", $file))
|
if (preg_match("=^\.=", $file))
|
||||||
continue; # . und .. Dateien ignorieren
|
continue; # ., .. und versteckte Dateien (.gitkeep) ignorieren
|
||||||
elseif(is_file($PDFpathpart.$file))
|
elseif(is_file($PDFpathpart.$file))
|
||||||
{ #Dateien prüfen, und löschen, wenn älter als $delFile - Minuten
|
{ #Dateien prüfen, und löschen, wenn älter als $delFile - Minuten
|
||||||
$lastTime = ceil((time() - filemtime($PDFpathpart.$file)));
|
$lastTime = ceil((time() - filemtime($PDFpathpart.$file)));
|
||||||
@@ -1121,11 +1127,9 @@ function savePDF($pdf,$PDFpath)
|
|||||||
unlink($PDFpathpart.$file);
|
unlink($PDFpathpart.$file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
while(str_starts_with($PDFpath, ".")){
|
|
||||||
$PDFpath = substr($PDFpath,1);
|
|
||||||
}
|
}
|
||||||
$pdf->Output($_SERVER['DOCUMENT_ROOT']."kunden".$PDFpath,"F");
|
closedir($handle);
|
||||||
|
$pdf->Output($PDFpathpart.basename($PDFpath),"F");
|
||||||
}
|
}
|
||||||
|
|
||||||
function outputPDF($pdf)
|
function outputPDF($pdf)
|
||||||
|
|||||||
@@ -46,8 +46,13 @@ if($version == -1)
|
|||||||
|
|
||||||
|
|
||||||
$settings=readsettings();
|
$settings=readsettings();
|
||||||
|
// Die Ersteinrichtung schreibt die Config neu und kann die Datenbank leeren.
|
||||||
|
// Sie läuft ohne Login und darf deshalb nur erreichbar sein, solange es
|
||||||
|
// noch keine gültige Config gibt.
|
||||||
if($settings != "valid")
|
if($settings != "valid")
|
||||||
$action = "setup";
|
$action = "setup";
|
||||||
|
else if($action == "setup")
|
||||||
|
$action = "";
|
||||||
|
|
||||||
if($settings == "valid" && $action != "setup")
|
if($settings == "valid" && $action != "setup")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// Nur als Teil von index.php?action=setup gültig und nur, solange es noch
|
||||||
|
// keine gültige Config gibt (siehe index.php).
|
||||||
|
if(!defined("CONFIG_PATH") || readsettings() == "valid")
|
||||||
|
exit;
|
||||||
$err = array();
|
$err = array();
|
||||||
|
|
||||||
require_once("update/database-update.php");
|
require_once("update/database-update.php");
|
||||||
|
|||||||
+28
-11
@@ -1,8 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// Nur als Teil von index.php?action=setup gültig, nie direkt aufrufbar.
|
||||||
|
if(!defined("CONFIG_PATH") || readsettings() == "valid")
|
||||||
|
exit;
|
||||||
require_once("incs/import.php");
|
require_once("incs/import.php");
|
||||||
set_time_limit(60000);
|
set_time_limit(60000);
|
||||||
$err = 0;
|
$err = 0;
|
||||||
if(!file_put_contents (CONFIG_PATH.CONFIG_FILE,
|
|
||||||
|
/*
|
||||||
|
* Formularwert als PHP-Literal für die Config. var_export() erzeugt einen
|
||||||
|
* korrekt maskierten String in einfachen Anführungszeichen - früher wurde
|
||||||
|
* $_POST ungeprüft zwischen doppelte Anführungszeichen gesetzt, womit sich
|
||||||
|
* beliebiger PHP-Code in die Config schreiben ließ.
|
||||||
|
*/
|
||||||
|
function configWert($name, $html = false)
|
||||||
|
{
|
||||||
|
$wert = isset($_POST[$name]) && is_string($_POST[$name]) ? $_POST[$name] : "";
|
||||||
|
if($html)
|
||||||
|
$wert = htmlspecialchars($wert);
|
||||||
|
return var_export($wert, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!file_put_contents (CONFIG_PATH.CONFIG_FILE,
|
||||||
'<?php
|
'<?php
|
||||||
global $mysql_user;
|
global $mysql_user;
|
||||||
global $mysql_db;
|
global $mysql_db;
|
||||||
@@ -13,16 +31,15 @@ if(!file_put_contents (CONFIG_PATH.CONFIG_FILE,
|
|||||||
global $dbconn;
|
global $dbconn;
|
||||||
global $geodbconn;
|
global $geodbconn;
|
||||||
|
|
||||||
|
$instanz = '.var_export(INSTANZ, true).';
|
||||||
$dbconn = 0;
|
$dbconn = 0;
|
||||||
$geodbconn = 0;
|
$geodbconn = 0;
|
||||||
$mysql_user = "'.$_POST["mysqluser"].'";
|
$mysql_user = '.configWert("mysqluser").';
|
||||||
$mysql_pw = "'.$_POST["mysqlpw"].'";
|
$mysql_pw = '.configWert("mysqlpw").';
|
||||||
$mysql_db = "'.mysql_escape_string($_POST["mysqldb"]).'";
|
$mysql_db = '.configWert("mysqldb").';
|
||||||
$mysql_server = "'.$_POST["mysqlserver"].'";
|
$mysql_server = '.configWert("mysqlserver").';
|
||||||
$mysql_opengeodb = "'.mysql_escape_string($_POST["opengeodb"]).'";
|
$mysql_opengeodb = '.configWert("opengeodb").';
|
||||||
$firma = "'.htmlspecialchars($_POST["firma"]).'";
|
$firma = '.configWert("firma", true).';
|
||||||
$mysql_pw = "'.$_POST["mysqlpw"].'";
|
|
||||||
$mysql_pw = "'.$_POST["mysqlpw"].'";
|
|
||||||
?>'))
|
?>'))
|
||||||
{
|
{
|
||||||
$err= error("Konfiguration konnte nicht geschrieben werden!");
|
$err= error("Konfiguration konnte nicht geschrieben werden!");
|
||||||
@@ -73,7 +90,7 @@ else
|
|||||||
$version = $status[1];
|
$version = $status[1];
|
||||||
}
|
}
|
||||||
if(!$err)
|
if(!$err)
|
||||||
ok("<font size='+1'><b>Installation komplett!!</b></font><br /> Bitte entfernen Sie nun die Schreibrechte für die Datei /kundenverwaltung/config/settings.conf (chmod 555)");
|
ok("<font size='+1'><b>Installation komplett!!</b></font><br /> Bitte entfernen Sie nun die Schreibrechte für die Datei ".htmlspecialchars(CONFIG_PATH.CONFIG_FILE)." (chmod 444)");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
$err = error("Konfigurationsdatei fehlerhaft!");
|
$err = error("Konfigurationsdatei fehlerhaft!");
|
||||||
|
|||||||
Reference in New Issue
Block a user