The postgres-mcp PostgreSQL MCP server implements comprehensive security measures to protect your databases across stdio, HTTP, and SSE transports.
Identifier Sanitization (src/utils/identifiers.ts)
- β Comprehensive coverage β all table, column, schema, and index names validated and quoted across every tool group (admin, backup, core, jsonb, monitoring, partitioning, performance, postgis, schema, stats, text, vector)
- β PostgreSQL identifier rules enforced β start with letter/underscore, contain only alphanumerics, underscores, or $ signs
- β 63-character limit enforced (PostgreSQL maximum)
- β
Invalid identifiers throw
InvalidIdentifierError
Key functions:
sanitizeIdentifier(name)β Validates and double-quotes an identifiersanitizeTableName(table, schema?)β Handles schema-qualified table referencessanitizeColumnRef(column, table?)β Handles column references with optional table qualifiersanitizeIdentifiers(names[])β Batch sanitization for column lists
Parameterized Queries
- β
All user-provided values use parameterized queries via
pglibrary - β Identifier sanitization complements parameterized values β defense in depth
Every tool returns structured error responses β never raw exceptions or internal details:
{
"success": false,
"error": "Descriptive message with context",
"code": "MODULE_ERROR_CODE",
"category": "VALIDATION_ERROR",
"suggestion": "Actionable remediation hint",
"recoverable": true
}Error codes are module-prefixed (e.g., PG_CONNECTION_FAILED, SCHEMA_NOT_FOUND). Internal stack traces are logged server-side but never exposed to clients.
- β Zod schemas β all tool inputs validated at tool boundaries before database operations
- β Parameterized queries used throughout β never string interpolation
- β Identifier sanitization β table, column, schema, and index names validated against injection
Code Mode executes user-provided JavaScript in a Node.js vm context or a true V8 isolate via worker_threads (recommended). The standard vm module provides script isolation, not security isolation β it is not designed to resist a determined attacker with direct access. However, isolating workloads into worker threads with strict resource limits, combined with the following defense-in-depth mitigations, significantly reduces risk within the intended trusted AI agent threat model:
- β
Blocked globals β
require,process,global,globalThis,module,exports,setTimeout,setInterval,setImmediate,Proxyset toundefined - β
Blocked patterns β 17 static regex rules reject code containing
require(),import(),eval(),Function(),__proto__,constructor.constructor,Reflect.*,Symbol.*,new Proxy(), and filesystem/network/child_process references - β Execution timeout β 30s hard limit (configurable)
- β Input limits β 50KB code input, 10MB result output
- β Rate limiting β 60 executions per minute per client
- β Audit logging β every execution logged with UUID, client ID, metrics, and code preview (truncated to 200 chars)
- β
Admin scope β Code Mode requires
adminscope when OAuth is enabled
β οΈ Threat Model: Code Mode is designed for use by trusted AI agents, not for executing arbitrary untrusted code from end users. Thevmmodule does not provide a true security boundary β a sufficiently determined attacker with direct access could potentially escape the sandbox (e.g., via fragmentedconstructorchain access on exposed built-in Error types). Static pattern blocking catches the known literal forms (constructor.constructor) but not dynamically constructed variants.For untrusted input deployments: Use process-level sandboxing such as running the container with
--cap-drop=ALL, or replacevmwithisolated-vmfor V8 isolate-level separation.
When running in HTTP mode (--transport http), the following security measures apply:
- β
DNS Rebinding Protection β
validateHostHeader()strictly validatesHostheaders - β X-Content-Type-Options: nosniff β prevents MIME sniffing
- β X-Frame-Options: DENY β prevents clickjacking
- β Content-Security-Policy: default-src 'none'; frame-ancestors 'none' β prevents XSS and framing
- β Cache-Control: no-store, no-cache, must-revalidate β prevents caching of sensitive data
- β Referrer-Policy: no-referrer β prevents referrer leakage
- β Permissions-Policy: camera=(), microphone=(), geolocation=() β restricts browser APIs
- β Strict-Transport-Security header for HTTPS deployments
- β
Enable via
enableHSTS: trueconfiguration
- β
Origin whitelist with
Vary: Originheader for caching - β
Optional credentials support (
corsAllowCredentials) - β
MCP-specific headers allowed (
X-Session-ID,mcp-session-id)
- β Built-in Rate Limiting β 100 requests/minute per IP
- β
Health Endpoint Bypass β
/healthbypasses limits to ensure reliable load balancer checks - β
Returns 429 Too Many Requests with proper
Retry-Afterheaders when limits are exceeded - β
Slowloris DoS Protection β configurable read timeouts via
MCP_REQUEST_TIMEOUTandMCP_HEADERS_TIMEOUT
Reverse Proxy Note: Rate limiting uses
req.socket.remoteAddress. Behind a reverse proxy (e.g., nginx, Cloudflare Tunnel), all requests may share the same source IP. Ensure your proxy forwards distinct client IPs, or apply rate limiting at the proxy layer instead.
- β
Configurable body limit via
maxBodySize(default: 1 MB) β prevents memory exhaustion DoS
Full OAuth 2.1 for production multi-tenant deployments:
- β
RFC 9728 Protected Resource Metadata (
/.well-known/oauth-protected-resource) - β RFC 8414 Authorization Server Discovery with caching
- β JWT validation with JWKS support (TTL: 1 hour, configurable)
- β
PostgreSQL-specific scopes:
read,write,admin,full,db:{name},schema:{name},table:{schema}:{table} - β
Per-tool scope enforcement via
AsyncLocalStoragecontext threading
β οΈ HTTP without OAuth: When OAuth is not configured, all scope checks are bypassed. If you expose the HTTP transport without enabling OAuth, any client has full unrestricted access. Always enable OAuth for production HTTP deployments.
- β
Dedicated user:
appuser(UID 1001) with minimal privileges - β
Restricted group:
appgroup(GID 1001) - β
Restricted data directory:
700permissions
- β
Minimal base image:
node:24-alpine - β Multi-stage build: Build dependencies not in production image
- β
Production pruning:
npm prune --omit=devafter build - β
Health check: Built-in
HEALTHCHECKinstruction (transport-aware for HTTP/SSE/stdio) - β Process isolation from host system
The Dockerfile patches npm-bundled transitive dependencies for Docker Scout compliance:
- β
diff@8.0.3β GHSA-73rr-hh4g-fpgx - β
@isaacs/brace-expansion@5.0.1β CVE-2026-25547 - β
tar@7.5.11β CVE-2026-23950, CVE-2026-24842 - β
minimatch@10.2.4β CVE-2026-27904, CVE-2026-27903
# Secure volume mounting
docker run -v ./data:/app/data:rw,noexec,nosuid,nodev neverinfamous/postgres-mcp:latest# Apply resource limits
docker run --memory=1g --cpus=1 neverinfamous/postgres-mcp:latest- β Full JSONL Audit Trails β comprehensive logging array capturing mutations, Code Mode executions, and system events
- β Session Token Estimates β robust burn-rate tracking appended to log entries
- β Pre-Mutation Snapshots β interceptor captures table states before destructive administration operations
- β
Sensitive fields automatically redacted in logs:
password,secret,token,apikey,issuer,audience,jwksUri,credentials, etc. - β Recursive sanitization for nested objects
- β Control character sanitization (ASCII 0x00-0x1F except tab/newline, 0x7F, C1 characters)
- β Prevents log forging and escape sequence attacks
- β CodeQL analysis β automated static analysis on push/PR
- β npm audit β dependency vulnerability checking (audit-level: moderate)
- β Dependabot β automated dependency update PRs (weekly for npm and GitHub Actions)
- β Secrets scanning β dedicated workflow for leaked credential detection
- β E2E transport parity β Playwright suite validates HTTP/SSE security behavior
- Never commit database credentials to version control β use environment variables
- Use OAuth 2.1 authentication for HTTP transport in production β never expose HTTP transport without OAuth
- Restrict database user permissions to minimum required
- Enable SSL for database connections in production (
--sslorssl=truein connection string) - Enable HSTS when running over HTTPS (
--enableHSTS) - Configure CORS origins explicitly β avoid wildcards
- Use resource limits β apply Docker
--memoryand--cpuslimits - Apply rate limiting at the proxy layer when deploying behind a reverse proxy
- For cloud-managed databases with IAM authentication (e.g., AWS RDS), set
POSTGRES_POOL_MIN=2to reduce connection establishment latency - Consider SHA-pinning critical GitHub Actions in CI workflows for supply-chain defense-in-depth
- Parameterized queries only β never interpolate user input into SQL strings
- Zod validation β all tool inputs validated via schemas at tool boundaries
- No secrets in code β use environment variables (
.envfiles are gitignored) - Typed error classes β descriptive messages with context; don't expose internals
- Regular updates β keep Node.js and npm dependencies updated
- Security scanning β regularly scan Docker images for vulnerabilities
- Parameterized SQL queries throughout
- Identifier sanitization (table, column, schema, index names)
- Input validation via Zod schemas
- Code Mode sandbox isolation (vm or worker_threads V8 isolate)
- Code Mode execution timeout (30s hard limit)
- Code Mode rate limiting (60 executions/min)
- Code Mode audit logging
- HTTP body size limit (configurable, default 1 MB)
- Configurable CORS with origin whitelist
- Rate limiting (100 req/min per IP)
- Slowloris DoS timeouts (
MCP_REQUEST_TIMEOUT,MCP_HEADERS_TIMEOUT) - DNS rebinding protection via Host header validation
- Security headers (CSP, X-Content-Type-Options, X-Frame-Options, Cache-Control, Referrer-Policy, Permissions-Policy)
- HSTS (opt-in)
- OAuth 2.1 with JWT/JWKS validation (RFC 9728, RFC 8414)
- PostgreSQL-specific scope enforcement (
read,write,admin,full,db:*,schema:*,table:*:*:*) - Per-tool scope enforcement via
AsyncLocalStorage - Credential redaction in logs
- Log injection prevention
- Non-root Docker user
- Multi-stage Docker build with production pruning
- Transitive dependency CVE patching in Dockerfile
- CI/CD security pipeline (CodeQL, npm audit, secrets scanning)
- Structured error responses (no internal details leaked)
- Comprehensive security documentation
| Version | Supported |
|---|---|
| 2.x.x | β |
| 1.x.x | β |
| < 1.0 | β |
If you discover a security vulnerability:
- Do not open a public GitHub issue
- Email security concerns to: admin@adamic.tech
- Include detailed reproduction steps and potential impact
- Allow reasonable time for a fix before public disclosure
- Initial Response: Within 48 hours
- Status Update: Within 7 days
- Fix Timeline: Depends on severity
We appreciate responsible disclosure and will acknowledge your contribution in our release notes (unless you prefer to remain anonymous).
- Container updates: Rebuild Docker images when base images are updated
- Dependency updates: Keep npm packages updated via
npm auditand Dependabot - Database maintenance: Run
ANALYZEandVACUUMregularly for optimal performance - Security patches: Apply host system security updates
The postgres-mcp PostgreSQL MCP server is designed with security-first principles to protect your databases while maintaining excellent performance and full PostgreSQL capability.