The db-mcp SQLite MCP server implements comprehensive security measures to protect your databases across stdio, HTTP, and SSE transports.
- β
SQLCipher Support β The native backend (
better-sqlite3-multiple-ciphers) optionally supports full database encryption. - β
Dynamic Loading β Enabled by providing a
DB_ENCRYPTION_KEYvia environment variable or the--encryption-keyCLI flag. - β
System Database Encryption β When an encryption key is provided, the
SystemDb(which contains audit logs and system metrics) is automatically encrypted using the same key to ensure sensitive queries do not leak through the audit trail. β οΈ Native-Only β Encryption is exclusively available on the native backend. It cannot be used with WASM (sql.js).
Identifier Sanitization (src/utils/identifiers.ts)
- β Comprehensive coverage β all table, column, and index names validated and quoted across every tool group (admin, core, json, stats, geo, introspection, migration, text, vector)
- β SQLite identifier rules enforced β start with letter/underscore, contain only alphanumerics and underscores
- β Length limits enforced for compatibility and safety
- β
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
better-sqlite3/sql.jsbindings - β Identifier sanitization complements parameterized values β defense in depth
- β
Extension paths: The server relies on native extension loading for advanced capabilities (e.g., via
SPATIALITE_PATH). Exercise caution when deploying, as these environment variables resolve to native library files on the host. - β
Ensure extension paths map only to trusted directories containing verified libraries to prevent arbitrary code execution via malicious
.so/.dll/.dylibfiles.
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., SQLITE_CONNECTION_FAILED, TABLE_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
- β
WHERE clause validation β Core and stats tools enforce strict structured arrays (
WhereCondition[]), completely eliminating SQL injection vectors present in legacy string-based WHERE properties. For raw query tools, a blocklist rejects dangerous patterns includingUNION SELECT, stacked queries, comment injection, subqueries ((SELECT ...),ATTACH DATABASE,load_extension,PRAGMA, fileio functions, FTS tokenizer abuse, hex string injection,GLOBleading wildcards, andRANDOMBLOB/ZEROBLOBmemory allocation DoS. Input is Unicode NFC-normalized with full-width Latin character (U+FF01βU+FF5E) to ASCII mapping before pattern matching to prevent homoglyph-based blocklist bypasses (CWE-20) - β
JSON path validation β all JSON path parameters (e.g.,
$.key[0].subkey) are validated against a strict regex allowlist (^\$(\.\w+|\[\d+\]|\[#\]|\[\*\])*$) before SQL interpolation, preventing injection via malicious path values. Seesrc/utils/validate-json-path.ts - β
Aggregate function validation β SQL aggregate functions (
COUNT,SUM,AVG,MIN,MAX,GROUP_CONCAT,TOTAL) are validated against a strict whitelist with column name sanitization, preventing arbitrary SQL execution viaaggregateFunctionparameters - β
Path Traversal Prevention β database exports, backups, dumps, and CSV imports enforce strict path boundaries preventing arbitrary file reads/writes (e.g.
sqlite_dump,sqlite_backup,sqlite_create_csv_virtual_table). All filesystem operations are strictly sandboxed using symlink-aware realpath resolution (assertSafeIoPath). To prevent ambient filesystem authority, configuringALLOWED_IO_ROOTSis a hard requirement when starting the server with HTTP transport. For stdio/local transports, if unconfigured, operations fallback to strictly matching the directory of the primary database. Note: In-memory databases (:memory:) bypass path validation by design. - β
DDL Validation β Migration tools (
sqlite_migration_apply,sqlite_migration_rollback) usevalidateMigrationSqlto strictly prevent unauthorized DDL commands such asATTACH,DETACH,PRAGMA, andLOAD_EXTENSION. - β
JWT claims sanitization β prototype-polluting keys (
__proto__,constructor,prototype) are filtered from OAuth token payloads before spreading into claims objects
Code Mode executes user-provided JavaScript inside a process-level isolated-vm V8 isolate, providing strict memory separation and secure C++ execution boundaries. The previous insecure node:vm and worker_threads architectures have been entirely replaced to mitigate prototype pollution and execution escapes.
- β
Strict Memory Separation β
isolated-vmenforces true, native V8 isolates. The executing code has absolutely no memory access to the host Node.js environment,worker_threads, or sharedvmnamespaces. - β
Blocked globals β
require,process,global,globalThis,module,exports,setTimeout,setInterval,setImmediate,Proxyare strictly undefined. - β
Blocked patterns β 29 static regex rules reject code containing
require(),import(),eval(),Function(),__proto__,constructor.constructor,Reflect.*,Symbol.*,new Proxy(),fetch(),WebSocket,Object.getPrototypeOf,Object.defineProperty, and filesystem/network/child_process references. Code comments (/* ... */and//) are stripped, and\u/\xescapes are explicitly blocked prior to validation to prevent pattern evasion. - β
RPC boundary enforcement β Host-side API capabilities (e.g.
sqlite.*tools) are explicitly bridged into the isolate via explicit C++Referenceinstances. The isolate cannot bridge out to host functions arbitrarily. - β Readonly Proxy traps β group API objects are wrapped in Proxy traps that throw structured errors when stripped (readonly) methods are called, halting execution instead of silently returning undefined.
- β Execution timeout β 30s hard limit (configurable), enforced strictly by the isolate engine.
- β Input limits β 50KB code input, 10MB result output.
- β Rate limiting β 10 executions per minute per client for Code Mode (internal map capped at 10,000 active clients to prevent memory exhaustion DoS).
- β
Sandbox pooling β Isolate instances are managed via a strict LRU pool (
maxInstances: 5) to prevent memory exhaustion and host starvation during concurrency bursts. - β Audit logging β every execution logged with UUID, client ID, metrics, and code preview (truncated to 200 chars, credential patterns redacted).
- β
Forensic traceability β each isolate script execution uses a unique
randomUUID()filename for distinguishable stack traces. Stack traces are strictly stripped from worker error responses in production (NODE_ENV=production) to prevent internal path and dependency leakage. - β
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. Whileisolated-vmprovides robust security against context escapes, this server still runs the isolation within the main Node.js process space.For untrusted input deployments: Use infrastructure-level sandboxing:
- Run the container with
--cap-drop=ALL --security-opt=no-new-privilegesto limit post-compromise capabilities.- Apply Docker resource limits (
--memory,--cpus) and read-only filesystem (--read-only) where possible.
When running in HTTP mode (--transport http), the following security measures apply:
- β
DNS Rebinding Protection β
validateHostHeader()strictly validatesHostheaders - β X-Powered-By header suppression β prevents framework version fingerprinting
- β 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
- β
Deny-all by default β
corsOriginsdefaults to[](no origins allowed). Must be explicitly configured for cross-origin access. - β
Origin whitelist with
Vary: Originheader for caching - β
Wildcard subdomain matching β supports patterns like
*.example.com - β Optional credentials support β set automatically for explicit (non-wildcard) origins
- β
MCP-specific headers allowed (
mcp-session-id,mcp-protocol-version)
- β Built-in Rate Limiting β 100 requests/minute per IP
- β
Health Endpoint Bypass β
/healthbypasses limits to ensure reliable load balancer checks. Unauthenticated requests receive only minimal data ({ status, timestamp }) to prevent information disclosure. - β
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: WhentrustedProxyIpsis enabled, rate limiting uses the rightmostX-Forwarded-ForIPs (up to the trusted proxy count). Only configuretrustedProxyIpswhen deploying behind a trusted reverse proxy (e.g., nginx, Cloudflare Tunnel) that securely appends to theX-Forwarded-Forheader. Without a trusted proxy, clients can spoof this header to bypass rate limits. WhentrustedProxyIpsis unconfigured (the default),req.socket.remoteAddressis used directly and behind a proxy all requests share the same source IP β apply rate limiting at the proxy layer instead.
β οΈ Multi-Instance Deployments: The defaultexpress-rate-limitin-memory store is per-process. In multi-instance deployments behind a load balancer, each instance maintains independent counters, effectively multiplying the rate limit by the number of instances. For production clusters, configure a shared rate limit store by providing a Redis store instance (e.g.,rate-limit-redis).
- β
UUID session IDs β cryptographically random session identifiers via
crypto.randomUUID() - β
Session ownership binding β each session is bound to the authenticated subject (
req.auth.sub) at creation. Every subsequent POST, GET (SSE stream, including legacy SSE connections), and DELETE request verifies that the requester's identity matches the session owner, preventing cross-client session hijack (CWE-284, CWE-639) - β
Graceful degradation β when auth is disabled (stdio transport, local dev), session ownership is not enforced (owner is
undefined)
- β Idle timeout β Sessions inactive for 30 minutes are automatically expired and cleaned up via a 1-minute sweep interval.
- β Absolute TTL β Sessions have a hard 24-hour maximum lifetime regardless of activity, forcing periodic re-authentication.
- β In-flight protection β Sessions with active requests are skipped by the sweep timer to prevent mid-request disconnection.
β οΈ In-Memory Sessions: Session state (including ownership binding and timeout tracking) is stored in-memory. Server restarts clear all sessions, forcing clients to re-establish. In multi-instance deployments, sessions are not shared across instances β use sticky sessions at the load balancer or implement a shared session store for production clusters.
- β
ALLOWED_IO_ROOTSRequirement β To prevent ambient filesystem authority in remote deployments, the HTTP transport will refuse to start unlessALLOWED_IO_ROOTS(via env var or CLI flag) is explicitly configured. This ensures administrators explicitly define exactly which directory paths the server is authorized to interact with.
- β
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), enforcing strict HTTPS for all JWKS and discovery URLs in production.
- β
SQLite-specific scopes:
read,write,admin,full - β
Per-tool scope enforcement via
AsyncLocalStoragecontext threading - β
Resource and Prompt authorization β Scope enforcement middleware covers
resources/readandprompts/get, requiringadminscope for audit resources andreadscope for others - β
Fail-closed scope default β unknown or unmapped tools default to
adminscope, preventing accidental privilege escalation when new tools are added - β
Dynamic scope set derivation β
ADMIN_TOOLS,READ_ONLY_TOOLS, andWRITE_TOOLSare derived at module load fromTOOL_GROUPS Γ TOOL_GROUP_SCOPES, preventing drift between tool registration and scope enforcement - β
Generic error responses β token validation errors return generic
"Token validation failed"messages to clients, preventing leakage of internal auth infrastructure URLs (e.g., JWKS endpoint addresses). Detailed errors are logged server-side only - β
WWW-Authenticate sanitization β
error_descriptionattributes inWWW-Authenticateheaders are sanitized (quotes stripped, truncated to 200 chars) to prevent header injection (CWE-113) and information disclosure (CWE-209)
β οΈ 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@9.0.0β GHSA-73rr-hh4g-fpgx - β
@isaacs/brace-expansion@5.0.1β CVE-2026-25547 - β
tar@7.5.15β CVE-2026-23950, CVE-2026-24842, CVE-2026-26960 - β
minimatch@10.2.5β CVE-2026-26996 - β
brace-expansion@5.0.6β CVE-2026-45149, CVE-2026-33750
Additional package.json overrides mirror these patches for npm audit compliance. The dockerfile-patch-drift.yml CI workflow runs weekly to detect when patches become stale (bundled versions catch up), covering both Dockerfile patches and package.json overrides.
# Secure volume mounting
docker run -v ./data:/app/data:rw,noexec,nosuid,nodev writenotenow/db-mcp:latest# Apply resource limits
docker run --memory=1g --cpus=1 writenotenow/db-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
- β SSE payload redaction β legacy SSE transport logs only session ID at debug level, never serialized message content (prevents bypassing logger redaction via raw JSON payloads)
- β
Code preview redaction β Code Mode audit log applies credential pattern matching (
sk-,Bearer,token=,password=,secret=,apikey=,api_key=) to code previews before logging, preventing embedded secrets from leaking to server logs
- β 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
- β pnpm audit β dependency vulnerability checking (audit-level: moderate)
- β Secrets scanning β dedicated workflow on push and PR (defense-in-depth for direct pushes to main)
- β
Lockfile integrity β SHA-256 hash and
git diff --exit-codeverification beforenpm cito detect post-checkout tampering - β Patch drift detection β weekly CI workflow validates Dockerfile patches and package.json overrides against upstream versions
- β
Dependabot Action Updates β proactive weekly
.github/dependabot.ymlpolicy monitors and updates GitHub Action versions. - β
Credential Isolation β CI workflows strictly pass credentials (
DEST_CREDS,NPM_TOKEN) via environment variables rather than command-line arguments to prevent leakage into intermediate shell evaluations or process listings. - β E2E transport parity β Playwright suite validates HTTP/SSE security behavior
- β Artifact Exposure Limits β sensitive workflow artifacts are explicitly purged after 24 hours
- 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
- Restrict filesystem access to only the required database directory
- 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 WAL mode performance, consider enabling
PRAGMA journal_mode=WAL;in your initialization script. - 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)
-
WHERE clause validation with subquery detection
-
WHERE clause Unicode NFC normalization + full-widthβASCII mapping (homoglyph bypass prevention)
-
Input validation via Zod schemas
-
Strict DDL validation with boundary regexes
-
Global AST pre-parsing rejection for mutating PRAGMAs (with multi-line comment stripping evasion protection)
-
Strict escaping for DDL identifiers (foreign keys, check constraints)
-
JWT claims sanitization (prototype pollution prevention)
-
Code Mode sandbox isolation (worker_threads V8 isolate + vm.createContext)
-
Code Mode V8 codeGeneration restrictions (eval/Function disabled at engine level)
-
Code Mode frozen built-in prototypes (constructor chain escape prevention)
-
Code Mode blocked patterns (29 static regex rules)
-
Code Mode Proxy constructor nullified in sandbox context
-
Code Mode RPC allowlist validation (host-side method authorization)
-
Code Mode readonly Proxy traps (structured errors for stripped methods)
-
Code Mode execution timeout (30s hard limit)
-
Code Mode RPC bridge quota enforcement (100 calls/execution)
-
Code Mode Regex input truncation (10,000 chars) for ReDoS mitigation
-
Code Mode rate limiting (60 executions/min)
-
LRU eviction algorithm for rate-limit map to prevent starvation DoS
-
Code Mode streaming egress boundary (abort serialization mid-flight on oversized results)
-
Code Mode
maxYoungGenerationSizeMbresource limit (caps V8 nursery allocation bursts) -
Code Mode audit logging
-
HTTP body size limit (configurable, default 1 MB)
-
CORS deny-all by default (explicit origin configuration required)
-
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)
-
SQLite-specific scope enforcement (
read,write,admin,full,db:*,table:*) -
Fail-closed scope default (
admin) for unknown tools -
Per-tool scope enforcement via
AsyncLocalStorageand protocol-layertools/listfiltering -
Credential redaction in logs
-
Log injection prevention
-
Non-root Docker user
-
Multi-stage Docker build with production pruning
-
Transitive dependency CVE patching (Dockerfile + package.json overrides)
-
Patch drift detection (weekly CI workflow)
-
Lockfile integrity verification (SHA-256 + git diff in CI)
-
CI/CD security pipeline (CodeQL, npm audit, secrets scanning on push+PR)
-
Artifact retention limited to 1 day for sensitive workflow outputs
-
Structured error responses (no internal details leaked)
-
Session ownership binding (session ID β auth subject verification)
-
SSE payload redaction (no raw message content in logs)
-
Code preview credential redaction in audit logs
-
Recursive structural JSON credential redaction (deep-clone) in audit logs to prevent serialization corruption
-
WWW-Authenticate header sanitization (quote stripping, truncation)
-
Generic token validation error responses (no internal URL leakage)
-
Code Mode vm sandbox gated to non-production environments
-
Per-execution UUID filenames in vm.Script for forensic traceability
-
Implicit prototype traversal prevention (Object.getPrototypeOf) in worker sandbox
-
Code Mode buffer uninitialized memory read prevention
-
Strict HTTPS JWKS enforcement in production (via ALLOW_HTTP_JWKS)
-
CI/CD pipeline Trivy SHA-pinning
-
SQL string literal credential redaction in audit logs
-
DSN/URI regex credential scrubbing in error formatters
-
Code Mode worker sandbox Function constructor nullification
-
Strict PRAGMA blocklist extended for DoS prevention (
locking_mode,mmap_size) -
Strict path boundary blocking for in-memory virtual paths (
:memory:) -
Scope enforcement without implicit admin fallback
-
Strict JWT clockTolerance defaults (30 seconds)
-
CI/CD concurrent execution block safeguards
-
Unauthenticated HTTP transport implicitly fails closed without
--no-auth-enforcementflag -
Session ID format and length validation (UUIDv4) for stateful transport
-
Session idle timeout enforcement (30-minute default, 1-minute sweep)
-
Session absolute TTL enforcement (24-hour hard cap)
-
In-flight request protection during session expiry sweep
-
WebAssembly and SharedArrayBuffer blocked in Code Mode sandbox
-
File I/O functions (
WRITEFILE,READFILE) blocked in restore tool -
Obfuscated adapter ID mapping in built-in tools
-
Low entropy startup warning for single-tenant tokens
-
Comprehensive security documentation
-
DDL Validation blocklist for
ATTACH/DETACH/LOAD_EXTENSION(CWE-89, CWE-22) -
Code Mode recursive credential redaction for deeply nested array objects (CWE-200)
-
Tool annotations with exact allowlist enforcement for
openWorldHint=true(filesystem-touching tools only)
- β Full data ownership: SQLite databases stay on your infrastructure
- β No telemetry: No data sent to external telemetry endpoints
β οΈ External Services: If configured for HTTP transport with OAuth, communicates with authorization server discovery (JWKS) endpoints.
- β No sensitive data harvesting: Doesn't harvest private keys or credentials
- β Schema and Data Secrecy: The server operates strictly on the databases you configure. It does not phone home schemas, queries, or query results.
GDPR-friendly:
- No telemetry collection
- No external data transmission of database records
- Controlled local or containerized storage
- User controls all data
- Easy database export/backup via built-in tools
No PII collection:
- No implicit collection of user identities
- No tracking
- No analytics
Recommended (highest security):
# Use SHA-256 digest for immutable reference
docker pull writenotenow/db-mcp@sha256:abc123...
# Find SHA digests at:
# https://hub.docker.com/r/writenotenow/db-mcp/tagsBenefits:
- Immutable image reference
- Protection against tag hijacking
- Reproducible deployments
- Supply chain verification
Tag-based (convenience):
docker pull writenotenow/db-mcp:latest| Version | Supported |
|---|---|
| 4.x.x | β |
| 3.x.x | β |
| 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 pnpm packages updated via
pnpm auditand manual dependency upgrades - Database maintenance: Run
ANALYZEandVACUUMregularly for optimal performance - Security patches: Apply host system security updates
The db-mcp SQLite MCP server is designed with security-first principles to protect your databases while maintaining excellent performance and full SQLite capability.