KEEP LEARNING
Build the bigger picture.
The Workflow Engineer connects individual n8n concepts to testing, deployment and running a complete workflow.
Tips > Ops & Security
Webhook payloads are untrusted user input.
Treat every webhook payload as untrusted input. Before using it in a database query, API call, or shell command, validate and sanitize it in a Code node, then pass the values to parameterized queries so the database escapes them. This blocks SQL and NoSQL injection, command injection, and SSRF instead of interpolating raw user input into a query string.
Webhook payloads are untrusted user input. Using them directly in database queries, API calls, or shell commands opens your workflows to injection attacks including SQL injection, NoSQL injection, command injection, and server-side request forgery (SSRF).
Real-world example: A webhook receives a contact form submission and inserts it into a PostgreSQL database. An attacker submits '; DROP TABLE contacts; -- as the name field.
Never interpolate raw input into a query string. This is vulnerable to injection:
// BAD: Direct string interpolation into SQL
// Mode: Run Once for Each Item
const name = $input.item.json.name;
const email = $input.item.json.email;
// VULNERABLE -- never do this
const query = `INSERT INTO contacts (name, email) VALUES ('${name}', '${email}')`;Instead, sanitize and validate the input first, then hand the clean values to parameterized queries:
// GOOD: Sanitize and validate input, use parameterized queries
// Mode: Run Once for Each Item
function sanitizeString(input, maxLength = 255) {
if (typeof input !== 'string') return '';
return input
.trim()
.slice(0, maxLength)
.replace(/[<>'";&|`$\\]/g, ''); // Remove dangerous characters
}
function isValidEmail(email) {
return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
}
const name = sanitizeString($input.item.json.name, 100);
const email = $input.item.json.email?.trim().toLowerCase() ?? '';
if (!name || !isValidEmail(email)) {
return [{ json: { error: 'Invalid input', rejected: true } }];
}
// Pass sanitized values to a Postgres node using parameterized queries
return [{
json: {
sanitizedName: name,
sanitizedEmail: email,
receivedAt: new Date().toISOString(),
}
}];Then in the PostgreSQL node, use n8n's built-in parameterized query support:
-- PostgreSQL node query (parameters are auto-escaped by the node)
INSERT INTO contacts (name, email, received_at)
VALUES ($1, $2, $3)Map $1 to {{ $json.sanitizedName }}, $2 to {{ $json.sanitizedEmail }}, and $3 to {{ $json.receivedAt }}.
Warning: SSRF Attacks. If your webhook accepts a URL parameter and your workflow fetches that URL, an attacker can make your server request internal resources (e.g., http://169.254.169.254/latest/meta-data/ on AWS to steal instance credentials). Always validate and allowlist URL domains before fetching.
Related: Use Docker Compose with Health Checks for n8n and PostgreSQL · Use Path Parameters in Webhook URLs for Dynamic Routing
KEEP LEARNING
The Workflow Engineer connects individual n8n concepts to testing, deployment and running a complete workflow.
APPLY IT TO YOUR SYSTEM
Bring the process, the tools involved and an example of where the current workflow gets stuck.