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:
+12
-1
@@ -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
@@ -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
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user