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 –
maxRowsandtimeoutto 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
valuesfield 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
- Webhook Trigger receives external data.
- Sanitize Node validates and normalizes fields.
- Validated output feeds into each database node (SQL, MongoDB, Redis).
- Error branch returns a 400 response if validation fails.
- 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: 5000ms) 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 (
$1vs?) 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:
- Never concatenate raw input into queries – always use bound parameters or explicit operator objects.
- Sanitize & validate every external field before it reaches a DB node.
- 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.



