Database connections are a high-value target. Compromise the database, compromise everything. Most data breaches involve databases.
Connection security is one layer (the others: encryption at rest, access controls, vulnerability management). This page covers the connection layer.
What you're defending against:
Plaintext traffic between application and database. TLS prevents.
Database password in code, config, or environment. Use secrets management.
Attacker gets into app; uses its credentials to query database. Limit what the app credential can do.
Database accessible from internet. Network controls prevent.
Employee with legitimate access does something inappropriate. Auditing detects.
Database in private subnet. No internet access. Only specific application servers can connect.
In AWS:
In Kubernetes:
All connections encrypted. PostgreSQL, MySQL, MongoDB all support TLS.
Modern best practice:
For self-managed: certificates issued by your private CA or Let's Encrypt.
For managed (RDS, Aurora, Cloud SQL): TLS optional but should be required. Set rds.force_ssl=1 (PostgreSQL) or equivalent.
Database authenticates the connector.
Options:
For cloud databases, IAM-based auth eliminates static passwords for application code.
Once authenticated, what can the user do?
Don't share one superuser account across many services.
Log who connected, when, what queries.
Logs to SIEM for analysis. Compliance often requires this.
Establishing connections is expensive. Pools reuse connections across requests.
Without pooling: every HTTP request establishes a new connection. Slow; resource-intensive; database connection limits hit.
Pool maintains N persistent connections. Application borrows; uses; returns. Connection ready for next request.
Too few: requests wait for connection. Too many: database overloaded; resources wasted.
Common: 10-20 connections per app instance. Tune based on load testing.
For Lambda + RDS: RDS Proxy is essentially required (Lambda concurrency hits connection limits).
Each service has its own DB user with its own permissions. Compromise of one service doesn't grant full DB access.
Read-only services connect to replicas with read-only credentials. Limits blast radius even further.
Periodically rotate passwords. Automated:
Cloud secret managers support automatic rotation.
For RDS:
token = rds.generate_db_auth_token(...)
# Use token as password
Token expires after 15 minutes. No long-lived password.
For human DBA access:
Sessions logged for audit.
For high-security: only specific queries allowed. Prepared statements only; reject ad-hoc queries.
Limits damage from compromised application.
For a production database:
Application doesn't parameterize queries; attacker crafts input that becomes SQL.
Defense: prepared statements. Parameterized queries. Always.
Password in code; in environment variable visible to processes; in logs.
Defense: secrets manager; never commit; redact from logs.
App user with too much permission. Attacker escalates within the database.
Defense: least privilege; specific roles per use case.
Attacker bypasses application; connects directly.
Defense: network controls; no direct database access from internet.
Backups stolen; restored elsewhere; data exposed.
Defense: encrypt backups; manage backup access carefully.