Exploit Analysis: search_path Hijacking (The Hidden PostgreSQL Attack)
Engineering against architectural vulnerabilities. A professional analysis of search_path hijacking and the static analysis standard for prevention.

Search_path hijacking is an obscure but lethal attack on PostgreSQL apps. Here is the architectural analysis and the automated static analysis standard built to prevent it across the fleet.
Most developers know about SQL injection. Few know about search_path hijacking.
It's just as dangerous.
What is search_path?
PostgreSQL's search_path determines which schema to look in when you reference an unqualified table name.
-- With search_path = public, these are equivalent:
SELECT * FROM users;
SELECT * FROM public.users;
The Attack
If an attacker can control the search_path, they can redirect your queries to malicious tables:
// โ Dynamic search_path from user input
const schema = req.query.tenant; // Attacker controls this
await client.query(`SET search_path TO ${schema}`);
await client.query("SELECT * FROM users"); // Now queries attacker's schema
The attacker:
- Creates a schema with a malicious
userstable - Sets search_path to their schema
- Your query returns their fake data
Why This Matters
| Attack | Impact |
|---|---|
| Data theft | Return fake data, capture input |
| Privilege escalation | Replace security functions |
| Code execution | Malicious triggers, functions |
The Correct Pattern
// โ
Static search_path
await client.query(`SET search_path TO tenant_${tenantId}`);
// โ
Validated against allowlist
const ALLOWED_SCHEMAS = ["tenant_1", "tenant_2", "tenant_3"];
if (!ALLOWED_SCHEMAS.includes(schema)) {
throw new Error("Invalid schema");
}
await client.query(`SET search_path TO ${schema}`);
// โ
Fully qualified table names
await client.query("SELECT * FROM public.users"); // Explicit schema
Let ESLint Catch This
npm install --save-dev eslint-plugin-pg
import pg from "eslint-plugin-pg";
export default [pg.configs.recommended];
Dynamic search_path is caught:
src/tenants.ts
8:15 error ๐ CWE-426 | Dynamic search_path detected
Fix: Use static schema name or validate against allowlist
Multi-Tenant Pattern
// โ
Safe multi-tenant with validated schema
async function queryTenant(tenantId, sql, params) {
// Validate tenant exists
const tenant = await getTenant(tenantId);
if (!tenant) throw new Error("Unknown tenant");
const client = await pool.connect();
try {
// Schema name from trusted source, not user input
await client.query(`SET search_path TO tenant_${tenant.id}`);
return await client.query(sql, params);
} finally {
// Reset search_path
await client.query("SET search_path TO public");
client.release();
}
}
Quick Install
npm install --save-dev eslint-plugin-pg
import pg from "eslint-plugin-pg";
export default [pg.configs.recommended];
Don't let attackers hijack your queries.
๐ฆ npm: eslint-plugin-pg ๐ Rule docs: no-unsafe-search-path
The Interlace ESLint Ecosystem Interlace is a high-fidelity suite of static code analyzers designed to automate security, performance, and reliability for the modern Node.js stack. With over 330 rules across 18 specialized plugins, it provides 100% coverage for OWASP Top 10, LLM Security, and Database Hardening.
Explore the full Documentation
ยฉ 2026 Ofri Peretz. All rights reserved.
Build Securely. I'm Ofri Peretz, a Security Engineering Leader and the architect of the Interlace Ecosystem. I build static analysis standards that automate security and performance for Node.js fleets at scale.