Database Security Hardening

Secure your databases against attacks and data breaches

Database security and protection
By FixTheVuln Team Peer-reviewed security content Sources: CISA, NVD, OWASP
Why Database Security Matters: Databases store your most valuable asset - data. A compromised database can lead to data breaches, regulatory fines, and loss of customer trust. Most breaches involve SQL injection or misconfigured databases.

SQL Injection Prevention

1. Use Parameterized Queries (Prepared Statements)

NEVER concatenate user input directly into SQL queries.

PHP (PDO)

# BAD - Vulnerable to SQL Injection $sql = "SELECT * FROM users WHERE id = " . $_GET['id']; # GOOD - Parameterized Query $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$_GET['id']]); $user = $stmt->fetch();

Python (psycopg2 / mysql-connector)

# BAD - Vulnerable cursor.execute(f"SELECT * FROM users WHERE id = {user_id}") # GOOD - Parameterized cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

Node.js (mysql2)

// BAD - Vulnerable connection.query(`SELECT * FROM users WHERE id = ${userId}`); // GOOD - Parameterized connection.query('SELECT * FROM users WHERE id = ?', [userId]);

Java (JDBC)

// BAD - Vulnerable Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE id = " + userId); // GOOD - PreparedStatement PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?"); pstmt.setInt(1, userId); ResultSet rs = pstmt.executeQuery();

C# (.NET)

// BAD - Vulnerable string sql = "SELECT * FROM users WHERE id = " + userId; // GOOD - Parameterized using (SqlCommand cmd = new SqlCommand("SELECT * FROM users WHERE id = @id", conn)) { cmd.Parameters.AddWithValue("@id", userId); SqlDataReader reader = cmd.ExecuteReader(); }

Key Takeaways

  • Always use parameterized queries — never concatenate user input into SQL
  • Apply least-privilege access: applications should never use the database admin account
  • Enable encryption at rest and in transit (TLS) for all database connections
  • Regularly backup databases and test your restore procedures
  • Audit and log all database access, especially privileged operations

2. Input Validation & Sanitization

# Whitelist validation - only allow expected characters import re def validate_username(username): # Only alphanumeric and underscore, 3-20 chars if re.match(r'^[a-zA-Z0-9_]{3,20}$', username): return True return False def validate_id(id): # Only integers return str(id).isdigit()

MySQL Hardening

3. Secure MySQL Installation

# Run the security script sudo mysql_secure_installation # This will: # - Set root password # - Remove anonymous users # - Disallow remote root login # - Remove test database # - Reload privilege tables

MySQL Configuration (/etc/mysql/mysql.conf.d/mysqld.cnf)

[mysqld] # Bind to localhost only (no remote connections) bind-address = 127.0.0.1 # Disable local file loading (prevents file read attacks) local-infile = 0 # Disable symbolic links symbolic-links = 0 # Enable logging general_log = 1 general_log_file = /var/log/mysql/mysql.log log_error = /var/log/mysql/error.log # Enable slow query logging slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2 # Require secure transport for remote connections require_secure_transport = ON

Create Least-Privilege User

# Never use root for application connections CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'StrongPassword123!'; # Grant only necessary permissions GRANT SELECT, INSERT, UPDATE ON myapp.* TO 'app_user'@'localhost'; # For read-only reporting user CREATE USER 'report_user'@'localhost' IDENTIFIED BY 'AnotherPassword!'; GRANT SELECT ON myapp.* TO 'report_user'@'localhost'; # Apply changes FLUSH PRIVILEGES;

PostgreSQL Hardening

4. Secure PostgreSQL Configuration

pg_hba.conf (Authentication)

# /etc/postgresql/[version]/main/pg_hba.conf # Reject all by default, then whitelist # TYPE DATABASE USER ADDRESS METHOD # Local connections local all postgres peer local all all md5 # IPv4 - only from specific IPs host myapp app_user 127.0.0.1/32 scram-sha-256 host all all 0.0.0.0/0 reject # IPv6 host all all ::1/128 scram-sha-256

postgresql.conf (Security Settings)

# /etc/postgresql/[version]/main/postgresql.conf # Listen only on localhost listen_addresses = 'localhost' # Require password encryption password_encryption = scram-sha-256 # Enable SSL ssl = on ssl_cert_file = '/etc/ssl/certs/server.crt' ssl_key_file = '/etc/ssl/private/server.key' # Logging logging_collector = on log_directory = 'pg_log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_statement = 'all' log_connections = on log_disconnections = on

Create Secure User

-- Create role with limited permissions CREATE ROLE app_user WITH LOGIN PASSWORD 'StrongPassword123!'; -- Grant connect to specific database GRANT CONNECT ON DATABASE myapp TO app_user; -- Grant schema usage GRANT USAGE ON SCHEMA public TO app_user; -- Grant specific table permissions GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO app_user; -- Revoke dangerous permissions REVOKE CREATE ON SCHEMA public FROM PUBLIC;

MongoDB Hardening

5. Secure MongoDB Configuration

Warning: MongoDB has NO authentication by default! Always enable authentication before exposing to any network.

Enable Authentication (/etc/mongod.conf)

# /etc/mongod.conf # Network settings net: port: 27017 bindIp: 127.0.0.1 # Only localhost # Security security: authorization: enabled # Enable TLS/SSL net: tls: mode: requireTLS certificateKeyFile: /etc/ssl/mongodb.pem CAFile: /etc/ssl/ca.pem

Create Admin and Application Users

// Connect to MongoDB mongosh // Switch to admin database use admin // Create admin user db.createUser({ user: "admin", pwd: "StrongAdminPassword123!", roles: ["userAdminAnyDatabase", "dbAdminAnyDatabase", "readWriteAnyDatabase"] }) // Switch to application database use myapp // Create application user with limited permissions db.createUser({ user: "app_user", pwd: "StrongAppPassword123!", roles: [ { role: "readWrite", db: "myapp" } ] }) // Create read-only user db.createUser({ user: "report_user", pwd: "StrongReportPassword123!", roles: [ { role: "read", db: "myapp" } ] })

Microsoft SQL Server Hardening

6. Secure MSSQL Configuration

Disable SA Account

-- Rename SA account ALTER LOGIN sa WITH NAME = [disabled_sa]; -- Disable SA account ALTER LOGIN sa DISABLE; -- Create new admin account CREATE LOGIN [db_admin] WITH PASSWORD = 'StrongPassword123!'; ALTER SERVER ROLE sysadmin ADD MEMBER [db_admin];

Enable Encryption

-- Enable TDE (Transparent Data Encryption) USE master; CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'MasterKeyPassword123!'; CREATE CERTIFICATE TDECert WITH SUBJECT = 'TDE Certificate'; USE myapp; CREATE DATABASE ENCRYPTION KEY WITH ALGORITHM = AES_256 ENCRYPTION BY SERVER CERTIFICATE TDECert; ALTER DATABASE myapp SET ENCRYPTION ON;

Create Least-Privilege User

-- Create login CREATE LOGIN app_user WITH PASSWORD = 'StrongPassword123!'; -- Create database user USE myapp; CREATE USER app_user FOR LOGIN app_user; -- Grant specific permissions only GRANT SELECT, INSERT, UPDATE ON SCHEMA::dbo TO app_user; DENY DELETE ON SCHEMA::dbo TO app_user; -- Deny dangerous permissions DENY EXECUTE ON xp_cmdshell TO app_user; DENY EXECUTE ON sp_OACreate TO app_user;

Database Encryption

7. Encryption at Rest and in Transit

Encryption at Rest

Database Feature How to Enable
MySQL InnoDB Tablespace Encryption innodb_encrypt_tables=ON
PostgreSQL pgcrypto extension CREATE EXTENSION pgcrypto;
MongoDB Encrypted Storage Engine security.enableEncryption: true
MSSQL TDE ALTER DATABASE SET ENCRYPTION ON

Encryption in Transit (SSL/TLS)

# MySQL - Force SSL connections ALTER USER 'app_user'@'%' REQUIRE SSL; # PostgreSQL - Require SSL in pg_hba.conf hostssl myapp app_user 0.0.0.0/0 scram-sha-256 # MongoDB - Connect with TLS mongosh --tls --tlsCertificateKeyFile /path/to/client.pem # Connection strings with SSL # MySQL mysql://user:pass@host/db?ssl-mode=REQUIRED # PostgreSQL postgresql://user:pass@host/db?sslmode=require # MongoDB mongodb://user:pass@host/db?tls=true

Backup Security

8. Secure Database Backups

# Encrypted MySQL backup mysqldump -u root -p myapp | openssl enc -aes-256-cbc -salt -out backup.sql.enc # Decrypt backup openssl enc -aes-256-cbc -d -in backup.sql.enc -out backup.sql # PostgreSQL encrypted backup pg_dump myapp | gpg --symmetric --cipher-algo AES256 -o backup.sql.gpg # Secure backup permissions chmod 600 backup.sql.enc chown root:root backup.sql.enc # Store backups off-site # - AWS S3 with server-side encryption # - Azure Blob with encryption # - Encrypted external drive

Security Checklist

☐ Using parameterized queries (no SQL concatenation)
☐ Input validation on all user inputs
☐ Database runs as non-root user
☐ Application uses least-privilege database account
☐ Remote root/admin login disabled
☐ Database bound to localhost or internal IP only
☐ SSL/TLS enabled for connections
☐ Encryption at rest enabled
☐ Audit logging enabled
☐ Backups encrypted and stored securely
☐ Default/test databases removed
☐ Strong passwords enforced
☐ Regular security updates applied

Important: Test all configuration changes in a staging environment first. Incorrect database security settings can lock you out or break your application.

Related Resources

SQL Injection (OWASP Top 10) API Data Protection Data Encryption

Test Your Knowledge

Security+ Practice Quiz AWS Database Specialty Quiz
← Back to Tools

FixTheVuln Store

Get the CompTIA Security+ Study Planner

Fillable PDF study planners with domain trackers, weekly schedules, and progress tracking. Available in Standard, ADHD-Friendly, Dark Mode, and 4-Format Bundle.

CompTIA Security+ Planner

60+ certifications available — from $5.99

CyberFolio

Choosing your next cert? Track them all in one place.

Build a shareable cybersecurity portfolio that highlights your certifications, projects, and skills — free.

Build Your Portfolio →