Implement CSRF protection for user invitation and management actions; add QR code generation script; secure internal includes directory with .htaccess

This commit is contained in:
2026-09-11 14:46:08 +02:00
parent 55618ffaed
commit cf0ceeca82
18 changed files with 413 additions and 163 deletions
+12 -1
View File
@@ -431,7 +431,9 @@ try {
$targetLaneId = (int)($input['target_lane_id'] ?? 0);
$targetPosition = (int)($input['target_position'] ?? 0);
$card = $db->query("SELECT * FROM board_cards WHERE id = {$cardId}")->fetch();
$stmtCard = $db->prepare("SELECT * FROM board_cards WHERE id = ?");
$stmtCard->execute([$cardId]);
$card = $stmtCard->fetch();
if (!$card) {
echo json_encode(['success' => false, 'error' => 'Taak niet gevonden.']);
exit;
@@ -443,6 +445,15 @@ try {
exit;
}
// Valideer of de doellane daadwerkelijk bij hetzelfde bord hoort (voorkom IDOR/lane hijacking)
$stmtLane = $db->prepare("SELECT id FROM board_lanes WHERE id = ? AND board_id = ?");
$stmtLane->execute([$targetLaneId, (int)$card['board_id']]);
if (!$stmtLane->fetch()) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Ongeldige doellane voor dit bord.']);
exit;
}
$stmt = $db->prepare("UPDATE board_cards SET lane_id = ?, position = ? WHERE id = ?");
$stmt->execute([$targetLaneId, $targetPosition, $cardId]);
+40 -2
View File
@@ -47,11 +47,25 @@ try {
exit;
}
// 2. Stemmen / Upvote op kaart
// 2. Stemmen / Upvote op kaart (met bescherming tegen oneindig stemmen)
if ($action === 'vote_card') {
$data = json_decode(file_get_contents('php://input'), true);
$cardId = (int)($data['card_id'] ?? 0);
if (!isset($_SESSION['voted_cards'])) {
$_SESSION['voted_cards'] = [];
}
if (in_array($cardId, $_SESSION['voted_cards'])) {
$stmtVotes = $db->prepare("SELECT votes FROM brainstorm_cards WHERE id = ?");
$stmtVotes->execute([$cardId]);
$votes = (int)$stmtVotes->fetchColumn();
echo json_encode(['success' => false, 'error' => 'U heeft al op deze kaart gestemd.', 'votes' => $votes]);
exit;
}
$_SESSION['voted_cards'][] = $cardId;
$stmt = $db->prepare("UPDATE brainstorm_cards SET votes = votes + 1 WHERE id = ?");
$stmt->execute([$cardId]);
@@ -63,11 +77,35 @@ try {
exit;
}
// 3. Kaart verwijderen
// 3. Kaart verwijderen (met autorisatiecontrole: auteur, sessie-eigenaar of beheerder)
if ($action === 'delete_card') {
$data = json_decode(file_get_contents('php://input'), true);
$cardId = (int)($data['card_id'] ?? 0);
$stmtCheck = $db->prepare("
SELECT c.*, s.created_by_id as session_owner_id
FROM brainstorm_cards c
JOIN brainstorm_sessions s ON c.session_id = s.id
WHERE c.id = ?
");
$stmtCheck->execute([$cardId]);
$card = $stmtCheck->fetch();
if (!$card) {
echo json_encode(['error' => 'Kaart niet gevonden']);
exit;
}
$isAuthor = ($card['author_name'] === $user['name']);
$isSessionOwner = ((int)$card['session_owner_id'] === (int)$user['id']);
$isAdmin = ($user['role'] === 'ADMIN');
if (!$isAuthor && !$isSessionOwner && !$isAdmin) {
http_response_code(403);
echo json_encode(['error' => 'Geen toestemming om deze kaart te verwijderen.']);
exit;
}
$stmt = $db->prepare("DELETE FROM brainstorm_cards WHERE id = ?");
$stmt->execute([$cardId]);
+12 -8
View File
@@ -9,15 +9,19 @@ require_once __DIR__ . '/../../config/database.php';
header('Content-Type: application/json');
// Beveiligingscontrole: Verifieer geheime webhook token indien geconfigureerd in .env
// Beveiligingscontrole: Verifieer geheime webhook token. Weiger toegang als secret ontbreekt.
$configuredSecret = fociz_env('JIRA_WEBHOOK_SECRET', '');
if (!empty($configuredSecret)) {
$incomingSecret = $_GET['secret'] ?? ($_SERVER['HTTP_X_ATLASSIAN_WEBHOOK_SECRET'] ?? '');
if (!hash_equals($configuredSecret, $incomingSecret)) {
http_response_code(401);
echo json_encode(['error' => 'Onbevoegde webhook aanroep']);
exit;
}
if (empty($configuredSecret)) {
http_response_code(403);
echo json_encode(['error' => 'Jira webhooks zijn niet ingeschakeld of geconfigureerd op deze server.']);
exit;
}
$incomingSecret = $_GET['secret'] ?? ($_SERVER['HTTP_X_ATLASSIAN_WEBHOOK_SECRET'] ?? '');
if (empty($incomingSecret) || !hash_equals($configuredSecret, $incomingSecret)) {
http_response_code(401);
echo json_encode(['error' => 'Onbevoegde webhook aanroep']);
exit;
}
$rawPayload = file_get_contents('php://input');