T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- workflows/02-booking-confirmation.json:5
- Finding
- Unauthenticated Booking Status Modification<![CDATA[ ## Vulnerability Details **File Location**: `workflows/02-booking-confirmation.json:5-11, 21-35, 79-119` **Vulnerability Type**: Missing authentication and authorization on a state-changing webhook **Risk Level**: High ### Vulnerable Code ```json { "parameters": { "httpMethod": "POST", "path": "booking/confirm", "responseMode": "responseNode", "options": {} }, "id": "n1", "name": "Confirm Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 2 } ``` ```javascript const body = $input.first().json.body || $input.first().json; const bookingId = (body.booking_id || '').trim(); const action = (body.action || 'confirm').trim(); if (!bookingId) { return [{ json: { valid: false, error: 'booking_id is required' } }]; } return [{ json: { valid: true, booking_id: bookingId, action, new_status: action === 'cancel' ? 'cancelled' : 'confirmed', updated_at: new Date().toISOString() } }]; ``` ```json { "parameters": { "operation": "appendOrUpdate", "documentId": { "__rl": true, "value": "YOUR_BOOKING_SHEET_ID", "mode": "list" }, "sheetName": { "__rl": true, "value": "Appointments", "mode": "list" }, "columns": { "mappingMode": "defineBelow", "value": { "booking_id": "={{ $json.booking_id }}", "status": "={{ $json.new_status }}", "updated_at": "={{ $json.updated_at }}" }, "matchingColumns": [ "booking_id" ] } }, "name": "Update Status", "type": "n8n-nodes-base.googleSheets" } ``` ### Technical Analysis The `booking/confirm` webhook performs a privileged state-changing operation without authenticating the caller or checking whether the caller owns or is authorized to manage the specified booking. Possession of a booking ID is treated as sufficient authority. The request parser accepts any action string and maps only the exact value `cancel` to `cancelled`; every other val ...[truncated 1673 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require authentication on the webhook, such as an authenticated staff session or a verified service-to-service credential. 2. For client-accessible confirmation or cancellation links, issue a unique, high-entropy, booking-specific token and store only a cryptographic hash of it. 3. Alternatively, sign the booking ID, permitted action, and expiration time with an HMAC secret and verify the signature before performing an update. 4. Authorize the requested action against the relevant booking rather than treating knowledge of its ID as authorization. 5. Restrict `action` to an explicit allowlist such as `confirm` and `cancel`; reject all other values. 6. Read and verify that the booking exists before modifying it. 7. Replace `appendOrUpdate` with update-only behavior so unknown identifiers cannot create partial rows. 8. Add expiration, replay protection, rate limiting, audit logging, and alerts for repeated invalid requests. 9. Return a generic error response that does not reveal whether a guessed booking ID exists. ]]>
