en

Building a Database-Driven Tor Hidden Service with Node.js and PostgreSQL

Static file hidden services cover simple use cases, but many hidden services require dynamic content: user authentication, stored content, search functionality, and administrative interfaces. Building a database-driven hidden service introduces additional security considerations beyond a simple file server while enabling the full range of dynamic web application features. This guide covers the complete stack for a Node.js application with PostgreSQL running behind a Tor hidden service, including network isolation, process security, and performance considerations specific to the Tor environment.

Need this done for your project?

We implement, you ship. Async, documented, done in days.

Start a Brief

Application Architecture for Hidden Services

The architecture layers from the Tor network inward: Tor hidden service -> nginx reverse proxy on localhost -> Node.js application on localhost -> PostgreSQL on localhost (or encrypted socket). All components communicate exclusively on localhost interfaces. No traffic ever leaves the server's network stack in cleartext except through the Tor process itself.

This strict localhost isolation is enforced at the application level by binding each service to 127.0.0.1, and at the network level by iptables rules that drop outbound traffic from processes other than tor. Defense in depth: if the application code has a bug that tries to make an external request, the iptables rules catch it before it reaches the network.

PostgreSQL binds to the Unix socket at /var/run/postgresql by default on Debian, which is inherently localhost-only. Use Unix socket connections from Node.js: set the host parameter in the database connection to the socket path. This avoids TCP socket overhead and keeps all database communication within the kernel's IPC mechanisms.

Node.js Security Configuration

Run Node.js as a dedicated non-root user with minimal system privileges. Create a dedicated user: useradd -r -s /bin/false -d /opt/hidden-app -m hidden-app. Run the Node.js process as this user with: su -s /bin/sh hidden-app -c "node /opt/hidden-app/server.js"

Configure Node.js process limits to prevent resource exhaustion attacks. The --max-old-space-size flag limits V8 heap memory: NODE_OPTIONS="--max-old-space-size=512" node server.js limits the process to 512MB of heap. Combine with ulimit settings in the service unit to limit open files and CPU time.

Apply Content Security Policy headers in the application to prevent XSS. For hidden services, the policy can be more restrictive than typical web applications because there is no CDN or external resource loading. A strict policy that blocks all external content is appropriate:

app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy',
    "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'");
  next();
});

PostgreSQL Configuration for Hidden Services

PostgreSQL requires minimal additional configuration for hidden service use. The critical settings are Unix socket binding (default on Debian), access control via pg_hba.conf, and encrypted storage for sensitive tables.

Verify PostgreSQL is not listening on any network interface: netstat -tlnp | grep postgres. The output should show no TCP listeners, only Unix socket entries. If TCP is enabled, edit postgresql.conf: listen_addresses = '' to disable network listening entirely.

Encrypt sensitive database columns at the application layer rather than relying on PostgreSQL encryption alone. Submissions, private messages, and personal data should be encrypted before insertion using application-level encryption with keys stored separately from the database. If the database is dumped by an attacker with server access, encrypted columns remain unreadable without the encryption keys.

Input Validation and Security for Hidden Service Applications

Hidden services attract the same range of attackers as clearnet applications, including automated vulnerability scanners, but also specifically motivated adversaries who want to deanonymize the operator or the users. Input validation must be comprehensive and strict: reject unexpected input formats at the application boundary before they reach the database or business logic.

Prevent SSRF (Server-Side Request Forgery) attacks with particular care. An SSRF vulnerability in a hidden service application is catastrophic: it could allow an attacker to cause the server to make clearnet HTTP requests to an attacker-controlled server, revealing the server's real IP address. Validate all URLs before making any outbound requests, and if outbound requests are not required at all, enforce this with iptables rules.

Rate limiting is essential for hidden services. Without rate limiting, attackers can enumerate users, attempt brute force attacks, or exhaust resources through the same Tor circuits. Apply rate limiting at the nginx layer for simple cases, or at the application layer for more sophisticated per-user limits that persist across sessions.

Why Anubiz Host

100% async — no calls, no meetings
Delivered in days, not weeks
Full documentation included
Production-grade from day one
Security-first approach
Post-delivery support included

Ready to get started?

Skip the research. Tell us what you need, and we'll scope it, implement it, and hand it back — fully documented and production-ready.

Anubiz Chat AI

Online