Blog

Web Security articles

Database Security Best Practices

Database security best practices in layers: network isolation, least-privilege accounts, SQL injection defence, encryption, patching and audit logs.

5 min read Web Security

Your database holds the information attackers want most: customer details, payment records, credentials and business secrets. Yet databases are often secured as an afterthought, with a default admin account, a port open to the internet and an application connecting as a superuser. Good database security is not one setting; it is a series of layers, so that if one fails the next still stands. This article goes through those layers from the outside in.

Layer 1: Keep the database off the public internet

The single most effective step is making sure nobody on the internet can reach the database port at all. MySQL listens on 3306, PostgreSQL on 5432 and SQL Server on 1433 by default, and automated scanners probe these constantly.

  • Place the database in a private network or subnet that only application servers can reach.
  • Use firewall rules or cloud security groups that allow connections from specific application server addresses only.
  • For administrator access, require a VPN or an SSH tunnel through a bastion host instead of opening the port to office IP ranges.
  • Bind the database to a private interface. In MySQL, bind-address controls this; in PostgreSQL, listen_addresses and pg_hba.conf do.

Layer 2: Strong authentication

Remove or disable default and anonymous accounts, and never leave a superuser with a blank or simple password. Every account should have a long, random password stored in a secrets manager or vault, not in source code. Our free password generator creates suitable strong passwords.

Where possible, prefer stronger methods: certificate-based authentication, or identity-based access in cloud platforms, where the application receives short-lived credentials instead of a permanent password. Give each person their own named account rather than sharing one admin login, so actions can be traced to an individual.

Layer 3: Least privilege

The principle of least privilege means each account can do only what it genuinely needs. A web application almost never needs to create users, drop tables or read system files. Create separate accounts for separate jobs:

-- MySQL: application account limited to one database and DML only
CREATE USER 'shop_app'@'10.0.1.%' IDENTIFIED BY 'use-a-long-random-secret';
GRANT SELECT, INSERT, UPDATE, DELETE ON shopdb.* TO 'shop_app'@'10.0.1.%';

-- Read-only account for reporting tools
CREATE USER 'shop_report'@'10.0.2.15' IDENTIFIED BY 'another-long-secret';
GRANT SELECT ON shopdb.* TO 'shop_report'@'10.0.2.15';
-- PostgreSQL equivalent using roles
CREATE ROLE shop_app LOGIN PASSWORD 'use-a-long-random-secret';
GRANT CONNECT ON DATABASE shopdb TO shop_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO shop_app;
REVOKE CREATE ON SCHEMA public FROM PUBLIC;

Schema changes should run through a separate migration account used only during deployments. If the application is compromised, the attacker inherits only the application's limited rights. Review grants periodically; permissions tend to accumulate.

Layer 4: Stop SQL injection at the application

SQL injection happens when user input is pasted directly into a SQL string, letting an attacker change the query's meaning. It remains one of the most common causes of data breaches. Compare:

// Vulnerable: input becomes part of the SQL text
$sql = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'";

// Safe: a prepared statement sends the value separately
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);

With a prepared statement (also called a parameterised query), the database treats the input strictly as data, never as code. Use them everywhere, including admin screens and internal tools. ORMs generally parameterise for you, but raw query helpers inside them can still be misused. The OWASP SQL Injection Prevention Cheat Sheet covers the defences in detail.

Layer 5: Encrypt data in transit and at rest

  • In transit. Require TLS for connections between the application and the database, especially across networks you do not fully control. In MySQL, require_secure_transport = ON enforces this; in PostgreSQL, use hostssl entries in pg_hba.conf.
  • At rest. Encrypt the storage volume or use the database's transparent data encryption, so stolen disks or snapshots are unreadable. Managed cloud databases usually offer this as a simple option.
  • Sensitive columns. For particularly sensitive values, such as identity numbers, consider encrypting them in the application before storing them, with keys held outside the database.
  • Passwords. Never store user passwords, even encrypted. Store a slow, salted hash using an algorithm designed for passwords, such as bcrypt or Argon2.

Layer 6: Patch and harden

Database engines receive security fixes regularly. Track the supported versions of your engine and plan upgrades before a version reaches end of life. Disable features you do not use, such as loading local files from clients (local_infile in MySQL) or unneeded extensions. Keep the operating system patched too; our server management service covers this routine work.

Layer 7: Log, monitor and audit

You cannot respond to what you cannot see. Enable logging of failed logins and privilege changes, and, for sensitive tables, audit who reads and changes data. PostgreSQL offers the pgAudit extension; MySQL has audit plugins in some editions and in MariaDB. Send logs to a separate system so an intruder cannot erase them, and alert on unusual patterns, such as a reporting account suddenly exporting an entire customer table at 3 a.m.

Layer 8: Protect copies and backups

Backups, test databases and analyst exports often contain the same data as production with far weaker protection. Encrypt backups, restrict who can access them, and avoid copying real personal data into development environments. Use masked or synthetic data instead.

A quick database security self-check

  1. Can the database port be reached from the internet?
  2. Does the application connect as a superuser or root?
  3. Are all queries parameterised?
  4. Is TLS enforced and storage encrypted?
  5. When was the engine last patched?
  6. Would you know if someone exported your customer table tonight?

If any answer is uncomfortable, start there. Our database management team can carry out a structured security review.

Key takeaways

  • Database security works in layers: network, authentication, privileges, application, encryption, patching and monitoring.
  • Never expose database ports publicly, and never let applications connect as an administrator.
  • Prepared statements are the core defence against SQL injection.
  • Backups and test copies need the same protection as production.

Need help with this?

Netifi helps businesses around the world with Web Security. Tell us what you are working on.