T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- workflows/aethon-blog-crosspost.json:25
- Finding
- Webhook Authentication Occurs After a Privileged Internal API Request<![CDATA[ ## Vulnerability Details **File Location**: `workflows/aethon-blog-crosspost.json`, lines 25-48 **Vulnerability Type**: Authentication-order flaw and unauthorized use of an internal service credential **Risk Level**: High ### Complete Code Snippet ```json { "parameters": { "url": "=http://YOUR_BLOG_ADMIN_HOST:3000/api/posts/{{ $json.body.slug }}?lang={{ $json.body.lang || 'en' }}", "sendHeaders": true, "headerParameters": { "parameters": [ { "name": "X-API-Key", "value": "YOUR_BLOG_ADMIN_API_KEY" } ] }, "options": {} }, "id": "n2", "name": "Fetch Post", "type": "n8n-nodes-base.httpRequest" } ``` The webhook secret is validated only in the subsequent node: ```js const webhook = $('Webhook').first().json; const post = $input.first().json; // C4: Verify webhook authentication via body secret const secret = webhook.body?._secret || webhook._secret || ''; const expectedSecret = 'YOUR_CROSSPOST_SECRET'; if (expectedSecret && secret !== expectedSecret) { throw new Error('Unauthorized: invalid crosspost secret'); } ``` ### Technical Analysis The workflow performs an API-key-authenticated request to the internal blog administration service before authenticating the webhook caller. Consequently, possession of the webhook URL is sufficient to make the n8n service invoke the internal endpoint using `YOUR_BLOG_ADMIN_API_KEY`. The caller-controlled `slug` and `lang` values are also interpolated directly into the URL without an allowlist or explicit URL encoding. Depending on the blog service's router and proxy behavior, crafted values may alter the path or query string. Even when the final authentication check rejects the request, the privileged internal request has already occurred. This exceeds least privilege because an unauthenticated external caller can exercise a network capability and credential that should only be available to authenticated cross-post operations. ### At ...[truncated 1201 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Move webhook authentication into the first executable node after the webhook, before all HTTP, Google Sheets, email, Slack, or platform operations. 2. Prefer a secret in an HTTP header such as `Authorization` or `X-Crosspost-Secret` rather than in the request body. 3. Compare secrets using a constant-time comparison where supported. 4. Reject the request immediately if the production secret is missing or still set to a placeholder. 5. Restrict `slug` to an expected pattern, such as `^[a-z0-9]+(?:-[a-z0-9]+)*$`. 6. Restrict `lang` to an explicit allowlist such as `en`, `de`, or other configured languages. 7. Use structured query parameters and URL encoding rather than raw string interpolation. 8. Apply webhook rate limiting, request-size limits, and access logging. 9. Give the blog-admin credential read-only access limited to the exact post endpoint. ]]>
