CSV-Upload gegen CSRF und unsichere Ablage härten

- Upload-Formular mit CSRF-Token absichern
- CSV-Dateien temporär unter var/uploads speichern und nach Import löschen
- Dateityp, Dateigröße und Dateiendung prüfen
- CSV-Ausgabe escapen und PayPal-Name-Lookup korrigieren
- M2- und Sicherheitsdokumentation aktualisieren
This commit is contained in:
2026-07-12 10:13:56 +02:00
parent e5fd47ecf6
commit 30378683e6
4 changed files with 144 additions and 40 deletions
+112 -30
View File
@@ -1,6 +1,7 @@
<?php
include "functions.php";
app_require_csrf();
include "header.php";
include "headerline.php";
include "nav.php";
@@ -15,12 +16,79 @@ include "nav.php";
<?php
if(checkKaffeelisteAdmin($conn, $mailadress)){
function csv_h($value): string
{
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}
function csv_upload_dir(): string
{
return APP_ROOT . '/var/uploads';
}
function csv_prepare_upload(array $file): array
{
if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
return [null, 'Fehler beim Hochladen der Datei.'];
}
if (($file['size'] ?? 0) <= 0 || $file['size'] > 5 * 1024 * 1024) {
return [null, 'Die CSV-Datei ist leer oder groesser als 5 MB.'];
}
$originalName = (string)($file['name'] ?? '');
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
if ($extension !== 'csv') {
return [null, 'Es sind nur CSV-Dateien erlaubt.'];
}
$tmpName = (string)($file['tmp_name'] ?? '');
if (!is_uploaded_file($tmpName)) {
return [null, 'Die hochgeladene Datei konnte nicht verifiziert werden.'];
}
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = $finfo ? finfo_file($finfo, $tmpName) : false;
if ($finfo) {
finfo_close($finfo);
}
$allowedMimeTypes = [
'text/plain',
'text/csv',
'text/x-csv',
'application/csv',
'application/vnd.ms-excel',
'application/octet-stream',
];
if (is_string($mime) && !in_array($mime, $allowedMimeTypes, true)) {
return [null, 'Der Dateityp wurde nicht als CSV erkannt.'];
}
}
$uploadDir = csv_upload_dir();
if (!is_dir($uploadDir)) {
@mkdir($uploadDir, 0700, true);
}
if (!is_dir($uploadDir) || !is_writable($uploadDir)) {
return [null, 'Der Upload-Ordner ist nicht beschreibbar.'];
}
$targetFile = $uploadDir . '/paypal_' . date('Ymd_His') . '_' . bin2hex(random_bytes(8)) . '.csv';
if (!move_uploaded_file($tmpName, $targetFile)) {
return [null, 'Die CSV-Datei konnte nicht gespeichert werden.'];
}
return [$targetFile, null];
}
// Funktion zum Überprüfen, ob der Mitarbeiter in der Tabelle vorhanden ist
function isMitarbeiterExist($conn, $name)
{
$sql = "SELECT MitarbeiterID FROM kl_Mitarbeiter WHERE Name = ?";
$params = array($name);
$sql = "SELECT MitarbeiterID FROM kl_Mitarbeiter WHERE Name = ? or paypalname = ?";
$params = array($name, $name);
$stmt = sqlsrv_query($conn, $sql, $params);
if ($stmt === false) {
@@ -36,7 +104,12 @@ function isMitarbeiterExist($conn, $name)
function isDuplicateEntry($conn, $mitarbeiterID, $betrag, $datum)
{
// Um nur den gleichen Tag zu vergleichen, konvertieren wir den Datum-String zu einem DateTime-Objekt und verwenden die Funktion CONVERT
$convertedDatum = date_format(date_create($datum), 'Y-m-d');
$date = date_create($datum);
if ($date === false) {
return false;
}
$convertedDatum = date_format($date, 'Y-m-d');
#$sql = "SELECT count FROM dbo.kl_Einzahlungen WHERE MitarbeiterID = ? AND Betrag = ? AND CONVERT(VARCHAR, Datum, 23) = ?";
$sql = "SELECT * FROM dbo.kl_Einzahlungen WHERE MitarbeiterID = ? AND Betrag = ? AND CONVERT(VARCHAR, Datum, 23) = ?";
@@ -49,7 +122,7 @@ function isDuplicateEntry($conn, $mitarbeiterID, $betrag, $datum)
$row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
return ($row['MitarbeiterID'] > 0);
return $row !== null && (int)$row['MitarbeiterID'] > 0;
}
// Funktion zum Verarbeiten der CSV-Datei und Rückgabe der Ergebnisse
@@ -63,6 +136,11 @@ function processCSV($conn, $file)
fgetcsv($handle);
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
if (count($data) < 8) {
$failed_entries[] = array($data[0] ?? '', $data[3] ?? '', $data[7] ?? '', 4);
continue;
}
$name = $data[3]; // Index 3 entspricht dem "Name"-Feld in der CSV
$betrag = $data[7]; // Index 7 entspricht dem "Brutto"-Feld in der CSV
$betrag = str_replace(",", ".", $betrag);
@@ -108,7 +186,7 @@ function processCSV($conn, $file)
function getMitarbeiterID($conn, $name)
{
$sql = "SELECT MitarbeiterID FROM kl_Mitarbeiter WHERE Name = ? or paypalname = ?";
$params = array($name);
$params = array($name, $name);
$stmt = sqlsrv_query($conn, $sql, $params);
@@ -127,20 +205,20 @@ function getMitarbeiterID($conn, $name)
// Überprüfen, ob das Formular abgeschickt wurde
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Überprüfen, ob eine Datei hochgeladen wurde
if (isset($_FILES["csv_file"]) && $_FILES["csv_file"]["error"] == 0) {
$file_name = $_FILES["csv_file"]["name"];
$file_tmp = $_FILES["csv_file"]["tmp_name"];
if (isset($_FILES["csv_file"])) {
[$csvFile, $uploadError] = csv_prepare_upload($_FILES["csv_file"]);
// Datei in den Upload-Ordner verschieben
move_uploaded_file($file_tmp, "uploads/" . $file_name);
if ($uploadError !== null) {
echo csv_h($uploadError);
} else {
// CSV-Datei verarbeiten
$result = processCSV($conn, $csvFile);
@unlink($csvFile);
// CSV-Datei verarbeiten
$result = processCSV($conn, "uploads/" . $file_name);
echo "CSV-Datei erfolgreich verarbeitet.\n";
echo "CSV-Datei erfolgreich verarbeitet.\n";
echo "<h3>Auswertung</h3>";
echo "<table>
echo "<h3>Auswertung</h3>";
echo "<table>
<tr>
<th>Name</th>
@@ -149,19 +227,19 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
<th>Ergebnis</th>
</tr>";
foreach ($result['success'] as $eintrag){
echo "<tr>
<td>$eintrag[0]</td>
<td>$eintrag[1]</td>
<td>$eintrag[2]</td>
foreach ($result['success'] as $eintrag){
echo "<tr>
<td>" . csv_h($eintrag[0]) . "</td>
<td>" . csv_h($eintrag[1]) . "</td>
<td>" . csv_h($eintrag[2]) . "</td>
<td>Erfolgreich gespeichert</td>
</tr>";
}
foreach ($result['failed'] as $eintrag){
echo "<tr>
<td>$eintrag[0]</td>
<td>$eintrag[1]</td>
<td>$eintrag[2]</td>
}
foreach ($result['failed'] as $eintrag){
echo "<tr>
<td>" . csv_h($eintrag[0]) . "</td>
<td>" . csv_h($eintrag[1]) . "</td>
<td>" . csv_h($eintrag[2]) . "</td>
<td>";
if($eintrag[3] == 1){
echo "SQL Fehler";
@@ -169,12 +247,15 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
echo "Eintrag schon vorhanden";
}elseif($eintrag[3] == 3){
echo "Benutzer nicht gefunden";
}elseif($eintrag[3] == 4){
echo "Ungueltige CSV-Zeile";
}
echo "</td>
</tr>";
}
echo "</table>";
}
echo "</table>";
}
} else {
echo "Fehler beim Hochladen der Datei.";
@@ -193,6 +274,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
</head>
<body>
<form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="post" enctype="multipart/form-data">
<?php echo app_csrf_field(); ?>
<label for="csv_file">CSV-Datei auswählen:</label>
<input type="file" name="csv_file" accept=".csv" required>
<button type="submit">Datei hochladen</button>
@@ -215,4 +297,4 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
</section>
<?php include "footer.php"; ?>
<?php include "footer.php"; ?>