Database Injection Risks in n8n Custom Nodes: Step-by-Ste…

Step by Step Guide to solve database injection risks 
Step by Step Guide to solve database injection risks


If you use n8n’s “Execute Query”, “MongoDB”, or “Redis” nodes, the biggest injection risk comes from passing raw user input directly into query strings. Prevent it by using parameter‑binding (prepared statements), strict type‑validation, and the built‑in “Sanitize” node. Test with the OWASP SQLi/NoSQLi suite before going live.

Who this is for: Developers and DevOps engineers who build production‑grade n8n workflows that interact with relational or document databases. We cover this in detail in the n8n Security & Hardening Guide.


Quick Diagnosis

Symptom Likely Cause Immediate Fix
syntax error near ‘ OR 1=1 in workflow logs Unescaped user input concatenated into an SQL string Use the Parameters field of the Execute Query node (prepared statement)
Unexpected document deletions in MongoDB after a webhook call Query built from raw request body Wrap values in $eq and validate with the Sanitize node
High CPU spikes when a public webhook receives crafted payloads Injection‑driven full‑table scans Enable query‑level timeouts (maxTimeMS) and reject payloads that fail schema validation

Bottom line: Any workflow that builds a database query by string‑concatenating external data has an injection vector. Replace that with bound parameters or explicit sanitization.


1. Where Injection Can Slip Into n8n Workflows?

If you encounter any xss vectors in custom code resolve them before continuing with the setup.

Node Typical Use‑Case Injection Surface
Execute Query (SQL) Pull rows, insert records SELECT * FROM users WHERE email = '{{ $json.email }}'
MongoDB CRUD on collections { find: "users", filter: { email: "{{ $json.email }}" } }
Redis Caching, session storage SET {{ $json.key }} {{ $json.value }}
HTTP Request (DB proxy) Custom API that writes to DB Body payload forwarded directly to DB endpoint

n8n treats node inputs as plain strings unless you explicitly enable parameter binding. The platform does not automatically escape values.


2. SQL Injection – Deep Dive with the Execute Query Node

2.1 Vulnerable Example

The node concatenates the email field directly into the SQL string.

{
  "type": "n8n-nodes-base.executeQuery",
  "parameters": {
    "query": "SELECT * FROM users WHERE email = '{{ $json.email }}';"
  }
}

If email=admin' OR '1'='1 arrives from a webhook, the final query becomes:

SELECT * FROM users WHERE email = 'admin' OR '1'='1';

Result: All rows are returned – classic authentication bypass.

2.2 Safe Pattern – Prepared Statements

Separate the query text from the values; n8n passes the values to the driver’s prepared‑statement API.

{
  "type": "n8n-nodes-base.executeQuery",
  "parameters": {
    "query": "SELECT * FROM users WHERE email = $1;",
    "values": [
      { "name": "email", "value": "={{ $json.email }}" }
    ]
  }
}

Key points

Feature Implementation
Parameter placeholder $1 (PostgreSQL) or ? (MySQL)
Values array Separate values field; driver binds safely
Type safety Cast in node: {{ Number($json.id) }} for integers

Checklist – Securing SQL Nodes

  • Use placeholders ($1, ?) – never concatenate strings.
  • Validate input type (email → regex, numeric IDs → Number()).
  • Set limits – maxRows and timeout to contain heavy queries.
  • Enable query logging (logQueries: true) for audit trails.
  • Run OWASP SQLi test suite against the workflow (see Section 5).

EEFA Note: Some legacy drivers ignore the values field when backticks are present. Verify with the exact driver version used in production. If you encounter any privilege escalation workflow execution resolve them before continuing with the setup.


3. NoSQL Injection – MongoDB & Redis

3.1 MongoDB Injection Vector

Passing a raw JSON filter allows an attacker to inject operators.

{
  "type": "n8n-nodes-base.mongoDb",
  "parameters": {
    "operation": "find",
    "collection": "users",
    "filter": "{{ $json.filter }}"
  }
}

A payload like { "$gt": "" } turns the filter into a MongoDB operator, exposing the whole collection.

3.2 Defensive Pattern – Explicit Operators

Build the filter object inside the node UI, using only allowed operators.

{
  "type": "n8n-nodes-base.mongoDb",
  "parameters": {
    "operation": "find",
    "collection": "users",
    "filter": {
      "email": { "$eq": "={{ $json.email }}" }
    }
  }
}

3.3 Redis Injection Mitigation

Redis lacks prepared statements, so treat keys/values as high‑risk strings.

{
  "type": "n8n-nodes-base.redis",
  "parameters": {
    "command": "SET",
    "key": "session:{{ $json.sessionId }}",
    "value": "{{ $json.payload | json }}"
  }
}
Threat Mitigation
Newline or space in key Encode with encodeURIComponent() before interpolation.
Binary payload causing protocol desync Encode payload as Base64: {{ $json.payload \| base64 }} and decode server‑side.
Unlimited TTL Set a default expiration (EX 3600).

EEFA Warning: Because Redis cannot bind parameters, always whitelist allowed characters for keys (^[a-zA-Z0-9:_-]+$).


4. End‑to‑End Hardening Workflow

4.1 Textual Flow Overview

  1. Webhook Trigger receives external data.
  2. Sanitize Node validates and normalizes fields.
  3. Validated output feeds into each database node (SQL, MongoDB, Redis).
  4. Error branch returns a 400 response if validation fails.
  5. Audit logging captures the sanitized payload for forensic review.

4.2 Step‑by‑Step Configuration

Add a “Sanitize” node immediately after any external trigger.

{
  "type": "n8n-nodes-base.sanitize",
  "parameters": {
    "mode": "strict",
    "fields": ["email", "userId", "sessionId"]
  }
}
  • Connect the sanitized output to each DB node.
  • Enable node‑level timeouts (timeout: 5000 ms) to abort long‑running queries.
  • Log sanitized payloads to a separate “Audit” collection for later review.

4.3 Production‑Grade Checklist

Item Recommended Setting
DB driver version Latest stable (e.g., pg@8.11 for PostgreSQL)
Connection pooling max: 20, idleTimeoutMillis: 30000
Secrets storage Use n8n environment variables (DB_PASSWORD)
Monitoring Enable Prometheus exporter for query latency
Rollback plan Keep a “dry‑run” branch with SELECT 1 only

EEFA Insight: After any driver upgrade, verify placeholder syntax ($1 vs ?) and re‑run regression tests on all DB nodes.


5. Testing & Validation

5.1 Automated OWASP Injection Test Suite (Node.js)

#!/bin/bash
npm install -g owasp-sql-injection-test
owasp-sql-injection-test --target http://your-n8n-instance/webhook/123

Pass criteria: All malicious payloads should return 400 Bad Request or a custom error, never a successful DB operation.

5.2 Manual Pen‑Test Checklist

Test Expected Result
Send email=admin'-- via webhook Workflow aborts with validation error.
Send { "$ne": "" } as MongoDB filter Node rejects payload (type mismatch).
Send Redis key with newline (session\nid) Key is rejected by regex whitelist.

5.3 CI Integration (GitHub Actions)

name: Injection Tests

on:
  push:
    branches: [main]

jobs:
  injection-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Start n8n
        run: docker compose up -d n8n
      - name: Run OWASP suite
        run: owasp-sql-injection-test --target http://localhost:5678/webhook/secure

The job fails on any 200 response, ensuring regressions are caught before merge.


Conclusion

Injection attacks in n8n are preventable when you apply three core actions:

  1. Never concatenate raw input into queries – always use bound parameters or explicit operator objects.
  2. Sanitize & validate every external field before it reaches a DB node.
  3. Automate testing (OWASP suite + CI) to catch regressions before they reach production.

Implement the patterns above, and your n8n workflows will stay resilient against both SQL and NoSQL injection threats.

Leave a Comment

Your email address will not be published. Required fields are marked *