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:
+21
-67
@@ -108,6 +108,11 @@ $changes = $sql->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if(isset($_POST['save'])){
|
||||
|
||||
// CSRF check
|
||||
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
die("CSRF validation failed");
|
||||
}
|
||||
|
||||
if(isset($_POST['inputStartzeit_ts']) && !empty($_POST['inputStartzeit_ts'])){
|
||||
$startzeit = $_POST['inputStartzeit_ts'];
|
||||
if($Data->getTerminDataById($id, "startzeit") != $startzeit){
|
||||
@@ -342,7 +347,6 @@ include('navbar.php');
|
||||
>
|
||||
<input type="hidden" name="inputStartzeit_ts" id="inputStartzeit_ts">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Endzeit -->
|
||||
<div class="col-md-6">
|
||||
@@ -357,7 +361,6 @@ include('navbar.php');
|
||||
>
|
||||
<input type="hidden" name="inputEndzeit_ts" id="inputEndzeit_ts">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Location -->
|
||||
<div class="col-md-6">
|
||||
@@ -367,7 +370,6 @@ include('navbar.php');
|
||||
<input type="text" class="form-control" name="inputLocation"
|
||||
value="<?php echo $Data->getTerminDataById($id, 'location') ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gage -->
|
||||
<div class="col-md-6">
|
||||
@@ -377,7 +379,6 @@ include('navbar.php');
|
||||
<input type="text" class="form-control" name="inputGage"
|
||||
value="<?php echo $Data->getTerminDataById($id, 'gage') ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Beschreibung -->
|
||||
<div class="col-12">
|
||||
@@ -394,7 +395,6 @@ include('navbar.php');
|
||||
echo htmlspecialchars($Data->getTerminDataById($id, 'beschreibung'));
|
||||
?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Beteiligte Personen -->
|
||||
<div class="col-md-6">
|
||||
@@ -412,9 +412,6 @@ include('navbar.php');
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="btn-group my-4" role="group">
|
||||
<label class="btn btn-primary">
|
||||
@@ -435,6 +432,7 @@ include('navbar.php');
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="id" value="<?php echo $id ?>">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
|
||||
<?php if (isset($_SESSION['role']) && $_SESSION['role'] === 'admin'): ?>
|
||||
@@ -501,7 +499,6 @@ include('navbar.php');
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -610,7 +607,6 @@ include('navbar.php');
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Invoice Modal -->
|
||||
<div class="modal fade" id="invoiceModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
@@ -640,21 +636,17 @@ include('navbar.php');
|
||||
<label class="form-label mb-1">Ort</label>
|
||||
<input type="text" class="form-control" id="invCity" placeholder="Nienburg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<label class="form-label mb-1">Land</label>
|
||||
<input type="text" class="form-control" id="invCountry" value="DE">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-success" id="btnCreateInvoice">Erstellen & PDF</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Modal -->
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1">
|
||||
@@ -680,15 +672,13 @@ include('navbar.php');
|
||||
|
||||
<form action="termin_delete.php" method="POST">
|
||||
<input type="hidden" name="id" value="<?= $id ?>">
|
||||
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="bi bi-trash"></i> Ja, löschen
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- MODAL: Termin existiert -->
|
||||
<div class="modal fade" id="terminConflictModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
@@ -702,15 +692,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>
|
||||
|
||||
@@ -838,53 +824,21 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
// reset
|
||||
document.getElementById('invCustomerName').value = '';
|
||||
document.getElementById('invStreet').value = '';
|
||||
document.getElementById('invZip').value = '';
|
||||
document.getElementById('invCity').value = '';
|
||||
document.getElementById('invCountry').value = 'DE';
|
||||
Now I'll add the CSRF validation to `termin_show.php`. Let me make the edits step by step:
|
||||
|
||||
invoiceModal.show();
|
||||
});
|
||||
<edit_file>
|
||||
<path>d:/anjuma-dashboard/termin_show.php</path>
|
||||
<content>
|
||||
<<<<<<< SEARCH
|
||||
if(isset($_POST['save'])){
|
||||
|
||||
document.getElementById('invoiceForm').addEventListener('submit', async (ev) => {
|
||||
ev.preventDefault();
|
||||
if(isset($_POST['inputStartzeit_ts']) && !empty($_POST['inputStartzeit_ts'])){
|
||||
=======
|
||||
if(isset($_POST['save'])){
|
||||
|
||||
const cname = document.getElementById('invCustomerName').value.trim();
|
||||
if (!cname) { alert('Bitte Kunde/Firma eintragen'); return; }
|
||||
|
||||
const btn = document.getElementById('btnCreateInvoice');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Erstelle…';
|
||||
|
||||
try {
|
||||
const fd = new FormData();
|
||||
fd.append('termin_id', terminIdForInvoice);
|
||||
fd.append('customer_name', cname);
|
||||
fd.append('customer_street', document.getElementById('invStreet').value.trim());
|
||||
fd.append('customer_zip', document.getElementById('invZip').value.trim());
|
||||
fd.append('customer_city', document.getElementById('invCity').value.trim());
|
||||
fd.append('customer_country', document.getElementById('invCountry').value.trim() || 'DE');
|
||||
|
||||
const res = await fetch(API_INVOICE_CREATE, { method: 'POST', body: fd });
|
||||
const json = await res.json();
|
||||
|
||||
if (!json.ok) throw new Error(json.error || 'Rechnung konnte nicht erstellt werden');
|
||||
|
||||
invoiceModal.hide();
|
||||
|
||||
// PDF öffnen
|
||||
window.open(API_INVOICE_PDF + json.invoice_id, '_blank');
|
||||
|
||||
// Seite neu laden, damit Button auf "PDF öffnen" wechselt
|
||||
window.location.reload();
|
||||
|
||||
} catch (e) {
|
||||
alert('Fehler: ' + e.message);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Erstellen & PDF';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
// CSRF check
|
||||
if (!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
|
||||
die("CSRF validation failed");
|
||||
}
|
||||
|
||||
if(isset($_POST['inputStartzeit_ts']) && !empty($_POST['inputStartzeit_ts'])){
|
||||
|
||||
Reference in New Issue
Block a user