Key Takeaways
- Default PostgreSQL and MySQL installations are not production-ready. Authentication, network binding, and logging all require explicit configuration.
- The principle of least privilege is critical — application accounts should never have superuser or DDL permissions.
- Encryption in transit (TLS) and at rest (Transparent Data Encryption or filesystem encryption) are both required for compliance with SOC 2 and PCI DSS.
- Database audit logging is essential for breach investigation and compliance. Log all DDL changes, privilege grants, and authentication failures.
- Automated configuration scanning catches misconfigurations before they reach production. Integrate checks into your CI/CD pipeline.
Why Database Security Matters
Databases store the most valuable data in any organization — customer records, financial transactions, intellectual property, and credentials. A compromised database often represents the worst-case scenario in a breach, leading to regulatory penalties, customer notification requirements, and lasting reputational damage. Despite this, database security is frequently treated as an afterthought, with default configurations deployed to production and application accounts granted far more privileges than necessary.
PostgreSQL and MySQL are the two most widely deployed open-source relational databases. Their flexibility and extensive feature sets also mean they have large configuration surfaces. Security teams must understand the default behaviors of each database engine and apply hardening configurations appropriate for their threat model.
Authentication Hardening
Authentication is the first line of defense for database access. Both PostgreSQL and MySQL support multiple authentication methods, and the default configurations are often too permissive for production use.
PostgreSQL Authentication (pg_hba.conf)
PostgreSQL's authentication is controlled by the pg_hba.conf file, which defines which users can connect from which addresses using which authentication methods. The most critical settings include:
# pg_hba.conf — Production hardening
# TYPE DATABASE USER ADDRESS METHOD
# Reject all connections by default
# Allow only specific application users from known CIDR ranges
# Always use scram-sha-256 (not md5 or trust)
local all postgres reject
host appdb appuser 10.0.1.0/24 scram-sha-256
host all all 0.0.0.0/0 rejectNever use 'trust' authentication in production
trust authentication method allows connections without a password. It is enabled by default for local connections in many PostgreSQL distributions. This must be replaced with scram-sha-256for all environments beyond local development.MySQL Authentication
MySQL uses a plugin-based authentication system. The default plugin in MySQL 8.0+ is caching_sha2_password, which is significantly more secure than the legacy mysql_native_password. Ensure all user accounts use the modern authentication plugin. Disable remote root access entirely and create dedicated application accounts with specific privilege grants.
-- Disable remote root access
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
-- Create a least-privilege application account
CREATE USER 'appuser'@'10.0.1.%'
IDENTIFIED WITH caching_sha2_password BY 'strong_password_here';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'10.0.1.%';
-- Flush privileges
FLUSH PRIVILEGES;Access Control and Privileges
The principle of least privilege is the most important access control concept for databases. Application accounts should have only the permissions they need to perform their function — typically SELECT, INSERT, UPDATE, and DELETE on specific tables. They should never have DDL permissions (CREATE, ALTER, DROP), superuser access, or the ability to grant privileges to other accounts.
Create separate accounts for different application components. Your read-only reporting service should not share credentials with your read-write transaction service. Migration scripts should use a dedicated account with DDL permissions that is not available to the running application. Administrative tasks should require separate authentication, ideally with multi-factor verification.
Encryption: In Transit and At Rest
Encryption in transit ensures that data flowing between your application and the database cannot be intercepted on the network. Both PostgreSQL and MySQL support TLS connections, but neither enforces TLS by default. You must configure the server to require TLS and the client to verify the server certificate.
# PostgreSQL: Require TLS in postgresql.conf
ssl = on
ssl_cert_file = '/etc/ssl/certs/server.crt'
ssl_key_file = '/etc/ssl/private/server.key'
ssl_min_protocol_version = 'TLSv1.3'
# Force all connections to use SSL in pg_hba.conf
hostssl all all 0.0.0.0/0 scram-sha-256Encryption at rest protects data on disk from unauthorized access. PostgreSQL does not provide native transparent data encryption (TDE), so you must use filesystem-level encryption (LUKS, dm-crypt) or cloud provider encryption (AWS EBS encryption, GCP disk encryption). MySQL Enterprise Edition includes TDE, and MariaDB offers it as well. For open-source MySQL, filesystem encryption is the primary option.
Network Security
Databases should never be directly accessible from the internet. This is the single most impactful security control you can implement. Place databases in private subnets with no public IP addresses. Use security groups or firewall rules to restrict inbound connections to only the application servers that need database access.
- Bind to private interfaces only — Configure
listen_addressesin PostgreSQL andbind-addressin MySQL to private IP addresses, not0.0.0.0. - Network segmentation — Place databases in a dedicated network segment (VPC subnet) with strict ingress rules.
- Connection pooling — Use PgBouncer or ProxySQL as an intermediary, reducing the number of direct database connections and adding an additional authentication layer.
- Disable unnecessary protocols — Disable local socket connections if not needed. Disable X Protocol in MySQL if not using the MySQL Shell.
Audit Logging
Database audit logs are essential for security incident investigation, compliance requirements, and detecting unauthorized access patterns. At minimum, log the following events:
- All authentication attempts (successful and failed)
- DDL statements (CREATE, ALTER, DROP)
- Privilege changes (GRANT, REVOKE)
- Access to sensitive tables (customer data, credentials, financial records)
- Configuration changes
pgAudit for PostgreSQL
Common Misconfigurations
These are the most frequently discovered database misconfigurations in production environments:
- Default ports — Running on port 5432 (PostgreSQL) or 3306 (MySQL) makes targeted scanning trivial. While changing ports is not a security control, it reduces automated scanning noise.
- Empty root passwords — MySQL historically allowed passwordless root access. Always set strong passwords for all administrative accounts.
- PUBLIC schema permissions — PostgreSQL's PUBLIC schema grants CREATE permission to all users by default. Revoke this immediately.
- Unrestricted listen addresses — Both databases default to listening only on localhost, but cloud deployments and containerized environments often override this to
0.0.0.0without compensating network controls. - Disabled SSL — TLS is available but not enforced by default in either database engine.
- Excessive log verbosity or insufficient logging — Either extreme is dangerous. Verbose logs can leak query parameters containing sensitive data. Minimal logs make incident investigation impossible.
Backup Security
Database backups are a high-value target because they contain a complete copy of your data, often without the access controls that protect the running database. Encrypt all backups at rest. Store them in a separate account or environment from the production database. Test restoration procedures regularly. Implement retention policies that automatically delete old backups. Restrict access to backup storage to dedicated service accounts with minimal permissions.
Full Security Checklist
Use this checklist for hardening PostgreSQL and MySQL in production:
- Replace trust/passwordless authentication with scram-sha-256 or caching_sha2_password
- Disable remote root/superuser access
- Create least-privilege application accounts per service
- Enforce TLS 1.3 for all connections
- Enable encryption at rest (filesystem or TDE)
- Bind to private interfaces only — never 0.0.0.0 without network controls
- Place databases in private subnets with strict security groups
- Enable audit logging for authentication, DDL, and privilege changes
- Revoke PUBLIC schema CREATE permission (PostgreSQL)
- Encrypt and isolate all backups
- Apply security patches within 30 days of release
- Run automated configuration scans against CIS benchmarks
Scan your databases with ShieldGraph
Scan Your Applications for These Vulnerabilities
ShieldGraph continuously scans your web applications, APIs, and databases to detect these vulnerabilities before attackers do. Start your free scan today.
Start Free ScanRelated Articles
OWASP Top 10 (2026): The Complete Guide to Web Application Security
14 min read
Industry ReportsSOC 2 vs PCI DSS: Which Compliance Framework Does Your Business Need?
11 min read
Vulnerability ResearchZero-Day Vulnerabilities: Detection, Response, and Prevention Strategies
12 min read
