docs/THREAT_MODEL.md
This document describes the STRIDE threat model for n8n-mcp. It is intended for contributors and operators who want to understand the security assumptions the project makes, the trust boundaries it relies on, and the mitigations already in place.
For the disclosure policy, see SECURITY.md. For deployment hardening, see SECURITY_HARDENING.md. For incident handling, see .github/INCIDENT_RESPONSE.md.
n8n-mcp is a Model Context Protocol server that gives AI assistants structured access to n8n node documentation and, optionally, management access to an n8n instance via its REST API. This threat model covers:
ghcr.io/czlonkowski/n8n-mcp container image.It does not cover:
SECURITY.md, "the security boundary is n8n itself, not n8n-mcp" — capabilities reachable through the n8n REST API are not re-evaluated here.The project has three security objectives, in priority order:
N8N_API_KEY, HTTP AUTH_TOKEN, and any per-tenant credentials must stay inside the trust boundary they were submitted to.AUTH_TOKEN) secure by default.| Actor | Description |
|---|---|
| Local developer | Runs the server in stdio mode inside Claude Desktop / Cursor / Codex / VS Code. Shares a process trust boundary with the server. |
| Self-hosted operator | Runs the HTTP server (directly or via Docker) for their own use. Owns the AUTH_TOKEN and N8N_API_KEY. |
| Multi-tenant tenant | Connects to a shared HTTP deployment and supplies their own n8n credentials per request via headers. |
| AI client | The LLM-driven MCP client that speaks to the server. Treated as a confused-deputy actor — it may act on untrusted workflow text. |
| Downstream n8n instance | Receives REST API calls from the server when management tools are used. |
| n8n.io templates API | Outbound-only source of public workflow templates. |
| External attacker | Unauthenticated network attacker; targets exposed HTTP deployments. |
| Malicious contributor | Submits crafted PRs or takes over a maintainer account (supply-chain). |
AUTH_TOKEN. Session state is in-memory.ENABLE_MULTI_TENANT=true. Each request carries the tenant's n8n URL and API key in headers (x-n8n-url, x-n8n-key, x-instance-id, x-session-id).ghcr.io/czlonkowski/n8n-mcp, runs as a non-root user with the randomized UID/GID created at build time.N8N_API_KEY from the environment.AUTH_TOKEN gate is the sole authenticator; everything behind it is in the server's trust zone.x-instance-id/x-session-id headers scope session state and the n8n credentials used by that request's tool calls.api.n8n.io leave the server's process and are trusted to the extent their TLS endpoints are.flowchart LR
subgraph ClientTrust["Client Trust Zone"]
LLM[AI Client]
end
subgraph ServerTrust["n8n-mcp Trust Zone"]
STDIO[stdio transport]
HTTP[HTTP transport
+ AUTH_TOKEN gate
+ rate limit]
CORE[MCP core
tools registry]
DB[(nodes.db
templates.db
read-only at runtime)]
SESS[(In-memory
session state)]
end
subgraph External["External"]
N8N[n8n REST API]
TPL[api.n8n.io
templates]
NPM[npm registry]
GHCR[ghcr.io image]
end
LLM -- stdio JSON-RPC --> STDIO
LLM -- "HTTPS + Bearer AUTH_TOKEN
(optional x-n8n-* headers)" --> HTTP
STDIO --> CORE
HTTP --> CORE
CORE --> DB
CORE --> SESS
CORE -- "N8N_API_KEY or per-tenant key" --> N8N
CORE -- "HTTPS fetch (build-time / refresh)" --> TPL
NPM -. "supply chain" .-> ServerTrust
GHCR -. "supply chain" .-> ServerTrust
| Asset | Sensitivity | Notes |
|---|---|---|
AUTH_TOKEN | High | Sole gatekeeper for HTTP mode. Generated per deployment (see SECURITY_HARDENING.md). |
N8N_API_KEY (and per-tenant equivalents) | High | Grants full workflow and credential read/write on the target n8n. |
| In-memory session state | Medium | Holds per-session context including, in multi-tenant mode, the tenant's n8n credentials for the lifetime of the session. |
data/nodes.db | Low | Public n8n node documentation. No user data. |
data/templates.db | Low | Public n8n.io workflow templates. |
npm package n8n-mcp | High | Downstream users execute it directly; integrity matters. |
Docker image ghcr.io/czlonkowski/n8n-mcp | High | Same — users pull and run it. |
Each subsection pairs a category of threat with the concrete mitigation that currently addresses it in this codebase. File references are to the current repository layout.
AUTH_TOKEN used to impersonate a legitimate HTTP client. Mitigation: constant-time comparison via crypto.timingSafeEqual in AuthManager.timingSafeCompare (src/utils/auth.ts), IP-based auth-failure rate limit in src/http-server-single-session.ts, deployment guidance in SECURITY_HARDENING.md recommending openssl rand -base64 32 and quarterly rotation.src/http-server-single-session.ts transports, servers, sessionMetadata, sessionContexts) and per-request credential binding — no per-tenant key is persisted beyond the session TTL.api.n8n.io templates. Mitigation: templates are stored as inert data and validated as JSON; they are never eval'd, require'd, or executed inside n8n-mcp. Any execution happens later on the user's own n8n instance, which is out of scope per SECURITY.md.nodes.db or templates.db at rest. Mitigation: both databases are treated as read-only at runtime (rebuilt via npm run rebuild/npm run fetch:templates). Docker images bake the database at build time.SECURITY_HARDENING.md warns against using plaintext http:// targets outside of local development.src/utils/logger.ts; HTTP access logs include the session identifier and the tool name so operators can attribute actions.N8N_API_KEY or AUTH_TOKEN via logs or error messages. Mitigation: errors returned to clients are sanitized before they leave the server; tokens are never interpolated into log output. Operators are reminded in SECURITY_HARDENING.md to scope log sinks accordingly.src/services/credential-scanner.ts inspects workflows for hardcoded secrets as part of n8n_audit_instance and surfaces them as findings rather than silently echoing them.express-rate-limit limiter applied at the auth endpoint in src/http-server-single-session.ts, with RateLimit-* headers enabled.N8N_MCP_MAX_SESSIONS cap and a periodic cleanup of idle sessions after SESSION_TIMEOUT_MINUTES.Object.create(null) to avoid prototype-pollution pitfalls; each session resolves its n8n credentials from its own headers at request time rather than sharing a global N8N_API_KEY.src/config/n8n-api.ts).Dockerfile explicitly drops privileges before CMD.SECURITY.md — n8n-mcp grants no capability the caller does not already have on the n8n REST API.These threats target the project itself rather than any one deployment.
main, and required review for all PRs.npm audit in CI.| Asset | Primary control |
|---|---|
AUTH_TOKEN | Constant-time comparison, auth-endpoint rate limit, deployment guide mandates rotation. |
N8N_API_KEY (and per-tenant equivalents) | Never persisted to disk; scoped to the request/session; sanitized out of logs and error responses. |
| Multi-tenant isolation | Per-session state, null-prototype maps, header-derived credentials, session TTL and cap. |
| Supply chain (npm + ghcr) | Locked dependencies, CI-gated releases, signed tags, non-root container user. |
This threat model is re-reviewed when any of the following happens:
SECURITY.md — disclosure policy and in-scope/out-of-scope definition.docs/SECURITY_HARDENING.md — deployment hardening knobs..github/INCIDENT_RESPONSE.md — incident handling process.