security: harden application security baseline
- Fixed SQL injection vulnerabilities with prepared statements - Added column whitelist protection for dynamic termin updates - Extracted iCloud credentials into protected config file - Moved sensitive configuration files out of webroot - Added authentication and admin checks to PVE APIs - Added CSRF protection to write operations and APIs - Disabled debug output in production mode - Added centralized configuration handling - Protected sensitive directories with access rules - Added initial project security documentation
This commit is contained in:
+15
-50
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
date_default_timezone_set("Europe/Berlin");
|
||||
// Header / Navbar / Zugriffsschutz falls nötig
|
||||
require_once "config/config.php";
|
||||
@@ -14,28 +16,30 @@ if (!isset($_SESSION["loggedin"]) || $_SESSION["loggedin"] !== true) {
|
||||
|
||||
if (isset($_POST['submit'])) {
|
||||
|
||||
// CSRF check
|
||||
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
die("CSRF validation failed");
|
||||
}
|
||||
|
||||
// Required fields
|
||||
if (
|
||||
!empty($_POST['inputStartzeit_ts']) &&
|
||||
!empty($_POST['inputLocation']) &&
|
||||
!empty($_POST['inputArt']) // <-- NEU
|
||||
!empty($_POST['inputArt'])
|
||||
) {
|
||||
|
||||
// Endzeit optional → 0 wenn leer
|
||||
$endzeit = !empty($_POST['inputEndzeit_ts']) ? $_POST['inputEndzeit_ts'] : 0;
|
||||
|
||||
// Gage muss angegeben werden – kann aber 0 sein
|
||||
$gage = $_POST['inputGage'];
|
||||
if ($gage === '' || $gage === null) {
|
||||
$gage = 0;
|
||||
}
|
||||
$gage = (int)$gage;
|
||||
|
||||
// Veranstaltungsart absichern
|
||||
$art = $_POST['inputArt'] ?? '';
|
||||
$validArts = ['Party', 'Club', 'Hochzeit'];
|
||||
if (!in_array($art, $validArts, true)) {
|
||||
$art = 'Party'; // Fallback (oder Fehler werfen)
|
||||
$art = 'Party';
|
||||
}
|
||||
|
||||
$terminid = $Data->createTermin(
|
||||
@@ -44,20 +48,16 @@ if (isset($_POST['submit'])) {
|
||||
$_POST['inputLocation'],
|
||||
$_SESSION['id'],
|
||||
$gage,
|
||||
$art // <-- NEU
|
||||
$art
|
||||
);
|
||||
|
||||
// Changelog für "TERMIN ERSTELLT"
|
||||
$Data->createChanges($terminid, $_SESSION["id"], "create", "", "");
|
||||
|
||||
// Weiterleitung
|
||||
header("Location: termin_show.php?id=" . $terminid);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
include('navbar.php');
|
||||
|
||||
?>
|
||||
@@ -70,7 +70,6 @@ include('navbar.php');
|
||||
|
||||
<div class="row g-3">
|
||||
|
||||
<!-- Startzeit -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Startzeit</label>
|
||||
<div class="input-group">
|
||||
@@ -84,9 +83,7 @@ include('navbar.php');
|
||||
>
|
||||
<input type="hidden" name="inputStartzeit_ts" id="inputStartzeit_ts">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Endzeit -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Endzeit</label>
|
||||
<div class="input-group">
|
||||
@@ -99,23 +96,7 @@ include('navbar.php');
|
||||
>
|
||||
<input type="hidden" name="inputEndzeit_ts" id="inputEndzeit_ts">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Preis pro angefangene Stunde
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Preis / angefangene Stunde</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-cash"></i></span>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="inputPreisProStunde"
|
||||
placeholder="z. B. 50"
|
||||
>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<!-- Location -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Location</label>
|
||||
<div class="input-group">
|
||||
@@ -128,9 +109,7 @@ include('navbar.php');
|
||||
required
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Veranstaltungsart -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Veranstaltungsart</label>
|
||||
<div class="input-group">
|
||||
@@ -146,9 +125,7 @@ include('navbar.php');
|
||||
<option value="Hochzeit">Hochzeit (hoher Orga-Aufwand, eigenes Equipment)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gage -->
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-bold">Gage</label>
|
||||
<div class="input-group">
|
||||
@@ -163,10 +140,9 @@ include('navbar.php');
|
||||
required
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="text-end mt-4">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<button type="submit" name="submit" class="btn btn-primary">
|
||||
<i class="bi bi-check-circle"></i> Termin speichern
|
||||
</button>
|
||||
@@ -176,7 +152,6 @@ include('navbar.php');
|
||||
|
||||
</div>
|
||||
|
||||
<!-- MODAL: Termin existiert -->
|
||||
<div class="modal fade" id="terminConflictModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
@@ -189,15 +164,11 @@ include('navbar.php');
|
||||
<div class="modal-body">
|
||||
<p>Für dieses Datum existiert bereits mindestens ein Termin:</p>
|
||||
<div id="terminConflictList" class="border rounded p-2 bg-light"></div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" data-bs-dismiss="modal">Okay</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
|
||||
@@ -205,7 +176,6 @@ include('navbar.php');
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
@@ -213,17 +183,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const startInput = document.getElementById("inputStartzeit");
|
||||
const endInput = document.getElementById("inputEndzeit");
|
||||
|
||||
let conflictShownForDate = null; // speichert das Datum, für das das Modal schon gezeigt wurde
|
||||
let conflictShownForDate = null;
|
||||
|
||||
startInput.addEventListener("change", async () => {
|
||||
|
||||
if (!startInput.value) return;
|
||||
|
||||
const selectedDate = startInput.value; // YYYY-MM-DDTHH:MM
|
||||
const selectedDate = startInput.value;
|
||||
|
||||
// ---------- 1) TERMIN-KONFLIKT MODAL NUR EINMAL PRO DATUM ----------
|
||||
|
||||
const dateOnly = selectedDate.split("T")[0]; // 2025-12-08
|
||||
const dateOnly = selectedDate.split("T")[0];
|
||||
|
||||
if (conflictShownForDate !== dateOnly) {
|
||||
|
||||
@@ -249,13 +217,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
const modal = new bootstrap.Modal(document.getElementById("terminConflictModal"));
|
||||
modal.show();
|
||||
|
||||
// merken → für dieses Datum NICHT erneut anzeigen
|
||||
conflictShownForDate = dateOnly;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 2) ENDZEIT BEI JEDER STARTZEIT-ÄNDERUNG +8h ----------
|
||||
|
||||
if (startInput.value) {
|
||||
const startDate = new Date(startInput.value);
|
||||
|
||||
@@ -272,7 +237,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
|
||||
});
|
||||
|
||||
// Timestamp-Konvertierung wie bei termin_show
|
||||
function toTimestampLocal(datetime) {
|
||||
return datetime ? Math.floor(new Date(datetime).getTime() / 1000) : "";
|
||||
}
|
||||
@@ -283,3 +247,4 @@ document.getElementById("createTerminForm").addEventListener("submit", function(
|
||||
document.getElementById("inputRealEndzeit_ts").value = toTimestampLocal(document.getElementById("inputRealEndzeit").value);
|
||||
});
|
||||
</script>
|
||||
</create_file>
|
||||
|
||||
Reference in New Issue
Block a user