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
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
Related Resources
Test Your Knowledge
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+ Planner60+ 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 →