Database Connection String Security: Encryption, IAM Auth & Secrets Management
Your database connection string is the keys to your kingdom. A leaked connection string means full access to your data — reads, writes, deletes, everything.
Yet developers still hardcode credentials in config files, commit them to Git, and share them over Slack. Let's fix that.
The Problem with Traditional Connection Strings#
A typical connection string looks like this:
postgresql://admin:SuperSecret123@db.example.com:5432/production
Everything an attacker needs is right there: username, password, host, port, and database name. If this string leaks through logs, error messages, or source control, your database is compromised.
Common leak vectors:
- Hardcoded in application config committed to Git
- Logged in plaintext by ORMs or connection pools
- Shared in Slack/email between team members
- Visible in CI/CD pipeline logs
- Stored in
.envfiles without proper.gitignore
Connection Encryption with SSL/TLS#
Why Encryption Matters#
Without SSL/TLS, database traffic travels in plaintext. Anyone on the network path (cloud VPC notwithstanding) can sniff queries and results.
# Unencrypted — vulnerable to sniffing
Client → [SELECT * FROM users WHERE email='...'] → Database
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Anyone on the network can read this
# Encrypted with TLS
Client → [encrypted bytes] → Database
Only client and server can decrypt
Configuring SSL/TLS for PostgreSQL#
# Connection with SSL required
connection:
host: db.example.com
port: 5432
sslmode: verify-full
sslrootcert: /certs/ca.pem
sslcert: /certs/client-cert.pem
sslkey: /certs/client-key.pem
SSL modes ranked by security:
| Mode | Encryption | Verification | Use Case |
|---|---|---|---|
disable | No | No | Never in production |
allow | Maybe | No | Not recommended |
prefer | Yes if available | No | Default, but weak |
require | Yes | No | Minimum for production |
verify-ca | Yes | CA only | Better — verifies certificate authority |
verify-full | Yes | CA + hostname | Best — prevents MITM attacks |
Always use verify-full in production. Anything less is vulnerable to man-in-the-middle attacks.
Configuring SSL/TLS for MySQL#
[client]
ssl-mode = VERIFY_IDENTITY
ssl-ca = /certs/ca.pem
ssl-cert = /certs/client-cert.pem
ssl-key = /certs/client-key.pem
MongoDB TLS Configuration#
const client = new MongoClient(uri, {
tls: true,
tlsCAFile: '/certs/ca.pem',
tlsCertificateKeyFile: '/certs/client.pem',
tlsAllowInvalidCertificates: false, // never set true in production
tlsAllowInvalidHostnames: false // never set true in production
});
IAM Authentication: Eliminate Passwords Entirely#
IAM-based authentication replaces static passwords with cloud identity tokens. No passwords to rotate, no credentials to leak.
AWS RDS IAM Authentication#
import boto3
def get_rds_auth_token(host, port, user, region):
client = boto3.client('rds', region_name=region)
token = client.generate_db_auth_token(
DBHostname=host,
Port=port,
DBUsername=user,
Region=region
)
return token
# Token is valid for 15 minutes — auto-rotated
token = get_rds_auth_token('mydb.abc123.us-east-1.rds.amazonaws.com', 5432, 'app_user', 'us-east-1')
GCP Cloud SQL IAM Authentication#
from google.cloud.sql.connector import Connector
connector = Connector()
def get_connection():
return connector.connect(
"project:region:instance",
"pg8000",
user="service-account@project.iam.gserviceaccount.com",
db="production",
enable_iam_auth=True
)
Azure AD Authentication for Azure SQL#
# Connection string using Azure AD
Server=myserver.database.windows.net;
Database=mydb;
Authentication=Active Directory Default;
Encrypt=True;
IAM auth benefits:
- No static passwords to manage or rotate
- Credentials tied to cloud identity (service account, role)
- Automatic expiration (15-minute tokens for AWS)
- Centralized access control through IAM policies
- Full audit trail in cloud provider logs
Short-Lived Credentials with HashiCorp Vault#
Static credentials are a liability. Vault generates temporary database credentials on demand.
# How Vault dynamic secrets work
App → Vault: "I need database credentials"
Vault → Database: CREATE ROLE temp_user WITH PASSWORD 'random' VALID UNTIL '15min'
Vault → App: {username: "temp_user", password: "random", ttl: "15m"}
# After TTL expires
Vault → Database: DROP ROLE temp_user
Vault Database Secrets Engine Configuration#
# Enable the database secrets engine
vault secrets enable database
# Configure PostgreSQL connection
vault write database/config/production \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db.example.com:5432/production" \
allowed_roles="app-role" \
username="vault_admin" \
password="admin_password"
# Create a role with a creation statement
vault write database/roles/app-role \
db_name=production \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
Application Usage#
import hvac
client = hvac.Client(url='https://vault.example.com:8200')
creds = client.secrets.database.generate_credentials('app-role')
db_config = {
'host': 'db.example.com',
'user': creds['data']['username'], # v-app-role-abc123
'password': creds['data']['password'], # randomly generated
'database': 'production'
}
# Credentials auto-expire after TTL
Secrets Management for Connection Strings#
Never Hardcode — Use a Secrets Manager#
# BAD — hardcoded in environment
DATABASE_URL=postgresql://admin:secret@db.example.com:5432/prod
# GOOD — reference to secret
DATABASE_URL=vault:database/creds/app-role
DATABASE_URL=aws-sm:prod/database/connection
DATABASE_URL=gcp-sm:projects/123/secrets/db-connection/versions/latest
Kubernetes Secrets with External Secrets Operator#
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-credentials
spec:
refreshInterval: 15m
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: db-credentials
data:
- secretKey: username
remoteRef:
key: database/creds/app-role
property: username
- secretKey: password
remoteRef:
key: database/creds/app-role
property: password
Secret Rotation Strategy#
# Rotation timeline
Day 0: Generate new credentials (Credential B)
Day 0: Update application to accept both A and B
Day 1: Switch primary to Credential B
Day 3: Verify no traffic using Credential A
Day 5: Revoke Credential A
# Automated rotation with AWS Secrets Manager
aws secretsmanager rotate-secret \
--secret-id prod/database/credentials \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123:function:rotate-db-creds \
--rotation-rules AutomaticallyAfterDays=30
Audit Logging Database Connections#
Every connection to your database should be logged and monitored.
PostgreSQL Connection Audit Logging#
-- Enable connection logging
ALTER SYSTEM SET log_connections = on;
ALTER SYSTEM SET log_disconnections = on;
ALTER SYSTEM SET log_line_prefix = '%t [%p]: user=%u,db=%d,app=%a,client=%h ';
-- Log all DDL statements
ALTER SYSTEM SET log_statement = 'ddl';
What to Monitor#
# Alert on these patterns
- Connection from unknown IP addresses
- Connections using the root/admin account
- Unusual connection volume spikes
- Failed authentication attempts (brute force)
- Connections outside business hours (if applicable)
- Connections from deprecated credential sets
Centralized Audit with pgAudit#
-- Install pgAudit extension
CREATE EXTENSION pgaudit;
-- Log all reads and writes
ALTER SYSTEM SET pgaudit.log = 'read, write, ddl';
-- Log role-specific activity
ALTER ROLE app_user SET pgaudit.log = 'write, ddl';
Security Checklist#
| Practice | Priority | Effort |
|---|---|---|
Enable SSL/TLS (verify-full) | Critical | Low |
| Move secrets out of code/config | Critical | Medium |
| Use IAM auth where available | High | Medium |
| Implement short-lived credentials | High | High |
| Enable connection audit logging | High | Low |
| Rotate credentials automatically | Medium | Medium |
| Restrict network access (VPC, firewall) | Critical | Low |
| Use separate credentials per service | High | Low |
| Monitor for credential leaks (GitGuardian) | Medium | Low |
Common Mistakes#
- Using
sslmode=requireinstead ofverify-full—requireencrypts but does not verify the server identity - Sharing one credential across all services — if one service is compromised, all services are compromised
- Long-lived credentials without rotation — credentials should expire, not live forever
- Logging connection strings in error messages — sanitize all logs to strip credentials
- Storing secrets in environment variables without encryption — env vars are visible in process listings and crash dumps
Summary#
| Layer | What to Do |
|---|---|
| Transport | SSL/TLS with verify-full, mTLS for internal |
| Authentication | IAM auth, short-lived tokens, no static passwords |
| Secrets | Vault/Secrets Manager, auto-rotation, never hardcode |
| Access control | Per-service credentials, least privilege grants |
| Monitoring | Connection logging, pgAudit, anomaly alerting |
Audit your database security at codelit.io — 413 architecture articles and growing. Generate architecture diagrams, run security checks, and visualize your infrastructure.
Try these templates
Authentication & Authorization System
OAuth2/OIDC-based auth with SSO, MFA, session management, and role-based access control.
8 componentsHeadless CMS Platform
Headless content management with structured content, media pipeline, API-first delivery, and editorial workflows.
8 componentsProject Management Platform
Jira/Linear-like tool with issues, sprints, boards, workflows, and real-time collaboration.
8 componentsContinue learning
Go deeper on security
As an Amazon Associate I earn from qualifying purchases. Codelit may receive a commission at no extra cost to you.
Real-World Cryptography
David Wong · 2021
Which primitive to pick for TLS, JWTs or end-to-end messaging, and what breaks when you pick wrong.
4.7 (100)#14 in Client-Server Networking SystemsInvestments Unlimited
Helen Beal, Bill Bensing, et al. · 2022
A 160-page story of a bank automating audit evidence, showing what regulators actually ask for.
4.4 (163)Kindle editionSolving Identity Management in Modern Applications
Yvonne Wilson, Abhishek Hingnikar · 2022
Every OAuth 2 and OIDC flow drawn end to end, including which to pick and the tokens each returns.
4.0 (21)Kindle editionSeven Databases in Seven Weeks
Luc Perkins, Eric Redmond, Jim Wilson · 2018
Hands-on days with Postgres, Redis, Neo4j, DynamoDB and more, so you can compare data models.
4.5 (69)Kindle edition