T09 · Insecure Skill Coding Practices
Error
- Location
- workflows/03-broadcast-sender.json:23
- Finding
- Broadcast Authentication Bypass Through a Public Fallback Secret<![CDATA[ ## Vulnerability Details **File Location**: `workflows/03-broadcast-sender.json:23` **Vulnerability Type**: Authentication fail-open caused by a known fallback credential **Risk Level**: High ### Vulnerable Code ```js const body = $input.first().json.body || $input.first().json; const subject = (body.subject || '').trim(); const content = (body.content || '').trim(); const secret = (body._secret || '').trim(); if (!secret || secret !== ($env.NEWSLETTER_SECRET || 'YOUR_NEWSLETTER_SECRET')) { return [{ json: { error: 'Unauthorized', valid: false } }]; } if (!subject || !content) { return [{ json: { error: 'Subject and content are required', valid: false } }]; } return [{ json: { subject, content, valid: true, sent_at: new Date().toISOString() } }]; ``` ### Technical Analysis The broadcast webhook authenticates requests by comparing a request-body value against `NEWSLETTER_SECRET`. If that environment variable is missing or empty, authentication silently falls back to the literal string `YOUR_NEWSLETTER_SECRET`. That fallback value is present in the publicly distributed workflow and is therefore not secret. A deployment that omits the environment variable will accept the documented placeholder as a valid credential. This is a fail-open configuration defect on a high-impact bulk-email operation. The secret is also supplied in the request body rather than through n8n credential handling or an authorization header. Request bodies may be retained in workflow execution histories, reverse-proxy logs, debugging systems, and monitoring products. ### Attack Path 1. An administrator imports and activates the workflow without configuring `NEWSLETTER_SECRET`. 2. The public endpoint `/webhook/newsletter/broadcast` becomes reachable. 3. An attacker sends a POST request containing: ```json { "_secret": "YOUR_NEWSLETTER_SECRET", "subject": "Attacker-controlled subject", "content": "<p>Attacker-controlled HTML</p>" ...[truncated 733 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Fail closed when `NEWSLETTER_SECRET` is unavailable: ```js const configuredSecret = $env.NEWSLETTER_SECRET; if (!configuredSecret) { throw new Error('NEWSLETTER_SECRET is not configured'); } ``` - Remove the public placeholder as an authentication fallback. - Use n8n-supported webhook authentication or place the endpoint behind an authenticated API gateway. - Supply credentials through an authorization header rather than the request body. - Compare secret values using a timing-safe comparison where the runtime supports it. - Use a randomly generated, high-entropy secret and document a rotation procedure. - Add request rate limits, replay protection, audit logging, and alerts for bulk sends. - Require explicit administrative approval or a preview/confirmation step before dispatching a broadcast. - Disable saving successful and failed execution payloads where possible so secrets and message contents are not retained unnecessarily. ]]>
