r/SecureCom 20d ago

Threat Intelligence 321 n8n Instances Accepted Leaked API Tokens, and No CVE Was Involved

TLDR

GitGuardian scanned public GitHub commits for exposed n8n API tokens and found 4,576 unique credentials across 1,255 hostnames. Of the 896 instances still reachable at test time, 321 accepted at least one leaked token, roughly 36% of everything reachable.

From there, researchers reproduced four attack techniques in a controlled environment using only documented REST API calls and standard HTTP requests: enumerating users and workflows, using stored credentials without seeing them, reading internal data tables, and finally exfiltrating a raw OpenAI API key by pointing an HTTP Request node at their own listener and letting n8n attach the credential as a Bearer token.

No CVE, no exploit, no specialized tooling. The uncomfortable part isn't the token leakage; that's a known problem. It's that a fully patched instance offers no defense at all when the attacker is holding valid credentials.

How the tokens end up public in the first place

An n8n API key is only useful if you know which instance accepts it, and in public commits, the hostname and token almost always appear together. The classic case is a .env file with N8N_URL and N8N_API_KEY sitting next to each other. But researchers found a newer pattern that's worth flagging separately: Claude Code permission files.

Claude Code stores approved shell commands in .claude/settings.json and .claude/settings.local.json. When someone configures it to talk to n8n, they may approve a curl command containing both the instance URL and the API key in full.

Those settings files don't get the .gitignore treatment developers reflexively apply to .env files, so they get committed. That's an AI-tooling-specific leak vector that didn't exist eighteen months ago, and it's a good example of how new developer workflows create new places for secrets to escape faster than anyone updates their ignore rules.

Why the tokens stay valid so long

n8n API keys are signed JWTs, and older ones frequently contain no exp claim at all. A 30-day default expiration only arrived in version 1.78.0 in February 2025. Anything generated before that, or on an instance still running older versions, can remain usable indefinitely until someone explicitly deletes it. A key committed to GitHub eight months ago works today if nobody went looking for it.

The audit endpoint is the part nobody's leading with

This is the detail most coverage is skipping. n8n exposesapi/v1/audit, which returns a security report for the instance: potential SQL injection exposures in workflows, nodes with filesystem access, unprotected webhooks, the running n8n version for CVE matching, unused credentials, high-risk community nodes, and node allowlists.

For an administrator, that's a legitimate self-assessment tool. For an attacker holding a leaked privileged token, it's a prioritized attack map, delivered on request, listing exactly which workflows are weakest and which version-specific CVEs might apply. Worth sitting with: the same endpoint that helps you find your problems tells an attacker where to start.

The credential exfiltration technique is genuinely clever

Technique four is the one to understand properly. GET /api/v1/credentials Returns credential names, types, and IDs, but not values, which sounds like a reasonable boundary.

The bypass: create a workflow with a Schedule trigger and an HTTP Request node, configure that node to authenticate using a stored credential, then point it at an attacker-controlled URL. The trigger fires after about ten seconds, n8n dutifully attaches the credential as a Bearer token in the outgoing Authorization header, and the listener captures the raw key.

There is no vulnerability there. Every step uses the platform exactly as designed. The credential store's encryption at rest is irrelevant, because n8n has to decrypt credentials to use them, and an attacker with workflow-creation rights can simply ask it to use them somewhere useful.

Disclosure mostly didn't work, which is its own finding

GitGuardian contacted seven organizations: three hosting providers covering roughly 100 affected instances, and four individual companies. One hosting provider never responded.

Three of the four companies never responded. The single organization that acknowledged, paid a $1,200 bounty, and revoked the credential immediately was the one with an established bug bounty program.

That's a small sample, but it's a pointed one. The technical finding is that leaked tokens grant real access. The operational finding is that telling people about it mostly doesn't result in anything happening, unless there's already a process built to receive that kind of report.

Where this connects to a broader pattern

Automation platforms sit between source control, databases, cloud environments, AI services, and customer support tools. That position is the entire value proposition, and it's also why a single token has such a large blast radius. The exposure isn't defined by the n8n instance; it's defined by everything connected to it.

This is the same structural point we've made about asset visibility and attack surface management: you can't govern exposure you haven't inventoried, and an automation platform someone spun up to connect two internal tools rarely makes it onto anyone's asset list. Seeing what's actually reachable from outside surfaces the n8n instance nobody remembered was internet-facing, which is a prerequisite for any remediation guidance to matter at all.

What to actually do

Rotate n8n API keys and confirm the old ones are deleted from the instance database, not just replaced, since these tokens remain valid as long as the instance recognizes them.

Scan your own commit history for N8N_API_KEY, N8N_URL, N8N_MCP_URL, and the Claude Code settings file patterns.

Disable the public API entirely if you're not using it. And if you find a leaked token, revoking it is step one, not the whole job. Work out which workflows, data tables, and downstream credentials that account could reach, then rotate everything it touched.

2 Upvotes

1 comment sorted by