Step-by-Step Guide
6 steps

How to Secure Database Connections

Database connections are high-value targets for attackers. An unsecured connection can expose credentials in transit, allow SQL injection, or provide unauthorized access to your entire dataset. This guide covers securing every aspect of database connectivity.

Find security issues automatically before attackers do.

Follow These Steps

1

Always use SSL/TLS for database connections

Encrypt data in transit between your application and database.

Code Example
// PostgreSQL with SSL
import pg from 'pg'

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  ssl: {
    rejectUnauthorized: true,  // Verify server certificate
    ca: process.env.DB_CA_CERT // CA certificate if self-signed
  }
})

// MySQL with SSL
import mysql from 'mysql2/promise'

const connection = await mysql.createConnection({
  uri: process.env.DATABASE_URL,
  ssl: { rejectUnauthorized: true }
})
2

Use least-privilege database users

Create separate database users for each service with only the permissions they need.

Code Example
-- Create a restricted application user
CREATE USER app_readonly WITH PASSWORD 'strong-random-password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;

CREATE USER app_readwrite WITH PASSWORD 'another-strong-password';
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_readwrite;

-- Never use the superuser for application connections
3

Store credentials in environment variables

Never hardcode database credentials in source code.

Code Example
# .env.local
DATABASE_URL=postgresql://app_readwrite:password@host:5432/mydb?sslmode=require

# On hosting platform, set via dashboard
# Vercel: Settings > Environment Variables
# Railway: ${{Postgres.DATABASE_URL}}
4

Use parameterized queries exclusively

Never construct SQL with string concatenation.

Code Example
// SAFE - parameterized
const result = await pool.query(
  'SELECT * FROM users WHERE email = $1 AND status = $2',
  [email, 'active']
)

// SAFE - ORM
const user = await db.select().from(users).where(eq(users.email, email))

// UNSAFE - string interpolation
const result = await pool.query(`SELECT * FROM users WHERE email = '${email}'`)
5

Use connection pooling

Connection pools improve performance and limit the number of open connections.

Code Example
const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,          // Maximum connections in pool
  idleTimeoutMillis: 30000,  // Close idle connections after 30s
  connectionTimeoutMillis: 5000  // Timeout if connection takes > 5s
})
6

Enable query logging for audit

Log queries in development and suspicious patterns in production.

Code Example
// Log slow queries in production
pool.on('connect', (client) => {
  const originalQuery = client.query.bind(client)
  client.query = (...args) => {
    const start = Date.now()
    const result = originalQuery(...args)
    result.then(() => {
      const duration = Date.now() - start
      if (duration > 1000) {
        console.warn(`Slow query (${duration}ms):`, args[0])
      }
    })
    return result
  }
})

What You'll Achieve

Database connections use SSL/TLS encryption, least-privilege users, environment variable credentials, parameterized queries, connection pooling, and audit logging.

Common Mistakes to Avoid

Mistake

Using rejectUnauthorized: false in production

Fix

This disables SSL certificate verification. Use the CA certificate from your database provider and set rejectUnauthorized: true.

Mistake

Using the database superuser for the application

Fix

Create a restricted user with only SELECT, INSERT, UPDATE permissions. Never grant DROP, CREATE, or ALTER to the application user.

Frequently Asked Questions

Do managed databases like Supabase or PlanetScale handle security?

Managed databases handle server-side security (patching, encryption at rest) but you are still responsible for connection security (SSL, parameterized queries, access controls).

Is SSL required for database connections?

If your database is on a different server (which it almost always is in production), yes. Without SSL, credentials and data are sent in plaintext over the network.

Ready to Secure Your App?

VAS automatically scans your deployed app for the security issues covered in this guide. Get actionable results in minutes.

Start Security Scan