security_fundamentals:php_database_safety

8. Database Safety — Parameterization, Boundaries, and Least Privilege

A database is not just storage.
It is a boundary
— one of the most sensitive in the entire system.

Security comes from treating the database as a separate trust domain,
not an extension of application logic.

The goal is simple:
even if the application misbehaves,
the database should remain safe.

This page introduces the mental model for protecting data through parameterization, boundaries, and least privilege.


1. Parameterization Is Non‑Negotiable

SQL injection is not a theoretical risk.
It is one of the most common and most damaging vulnerabilities.

The only reliable defense is parameterized queries:

$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$email]);

Parameterization:

  • separates code from data
  • prevents injection
  • enforces type safety
  • avoids string concatenation pitfalls

Escaping is not enough.
Sanitization is not enough.
Only parameterization is safe.


2. Never Build SQL by Concatenating Strings

Avoid patterns like:

$sql = "SELECT * FROM users WHERE email = '$email'";

Or:

$sql = "UPDATE posts SET title = '$title' WHERE id = $id";

Even if you “sanitize” the input, string‑built SQL is fragile and error‑prone.

A secure system never mixes:

  • SQL structure
  • untrusted data

in the same string.


3. Use Named Parameters for Clarity

Named parameters make queries more readable and less error‑prone:

$stmt = $pdo->prepare('SELECT * FROM posts WHERE author_id = :id');
$stmt->execute(['id' => $userId]);

This improves:

  • clarity
  • maintainability
  • correctness

Named parameters are small, but they reduce entire classes of mistakes.


4. Avoid Dynamic SQL Whenever Possible

Dynamic SQL is dangerous because it changes the structure of the query based on input.

Avoid patterns like:

  • dynamic ORDER BY
  • dynamic column names
  • dynamic table names
  • dynamic WHERE clauses built from strings

If you must use dynamic SQL:

  • whitelist values
  • validate structure
  • avoid passing raw input into SQL keywords

Dynamic SQL should be rare and carefully controlled.


5. Use the Least Privilege Principle for Database Users

Your application should not connect to the database as a superuser.

Prefer:

  • one database user per application
  • minimal privileges
  • no ability to create tables
  • no ability to drop tables
  • no ability to modify schema
  • no access to other databases

A compromised application should not become a compromised database.


6. Separate Read and Write Connections

For larger systems:

  • use a read‑only user for SELECT queries
  • use a write‑privileged user for INSERT/UPDATE/DELETE

This reduces:

  • accidental writes
  • injection damage
  • privilege escalation

Read‑only connections are a powerful safety boundary.


7. Validate Data Before It Reaches the Database

The database is not a validator.
It is a boundary.

Validate:

  • types
  • shapes
  • lengths
  • formats
  • domain rules

before the data reaches SQL.

The database should receive only predictable, shaped data.


8. Use Transactions for Consistency and Safety

Transactions protect:

  • integrity
  • consistency
  • partial failures
  • race conditions

Example:

$pdo->beginTransaction();
// operations
$pdo->commit();

A secure system uses transactions not just for correctness, but for safety.


9. Avoid Exposing Raw Database Errors

Database errors can leak:

  • table names
  • column names
  • SQL structure
  • internal logic
  • sensitive data

A secure system:

  • logs detailed errors privately
  • shows generic messages publicly
  • avoids exposing SQL in exceptions

Error messages are a boundary.


10. Treat the Database as a Separate Security Domain

The database is not an extension of PHP.
It is a separate system with its own:

  • permissions
  • boundaries
  • constraints
  • risks

Security comes from respecting that separation.


Summary

Database safety is about boundaries, not cleverness.

A secure system:

  • uses parameterized queries
  • avoids string‑built SQL
  • uses named parameters
  • limits dynamic SQL
  • applies least privilege
  • separates read and write access
  • validates data before SQL
  • uses transactions
  • hides internal errors
  • treats the database as a separate trust domain

The database is where truth lives.
Protect it with intention
.


This page describes secure defaults and common practices in modern PHP. It is not a complete security guide, and it does not replace formal audits or framework‑specific documentation. These principles are consistent enough to be useful, but security always requires context, judgment, and ongoing review.


security_fundamentals/php_database_safety.txt · Last modified: by editor