Skip to content
Now building for 2026 — tell us what your team needs. Start a project
Sembosi Digital Sembosi Digital

Writing Secure PHP: 10 Practices We Use in Every Project

PHP 3 min read
Writing Secure PHP: 10 Practices We Use in Every Project

PHP powers a huge share of the web, which also makes it a favourite target. These are the habits that keep the systems we build out of the breach reports.

Most PHP security problems are not exotic. They are the same handful of mistakes, repeated. After building business systems, hospital platforms and APIs, these are the practices we treat as non-negotiable.

1. Always use prepared statements

Never build SQL by concatenating strings. With PDO it looks like this:

$stmt = $pdo->prepare('SELECT * FROM patients WHERE hospital_no = :no');
$stmt->execute(['no' => $hospitalNo]);
$patient = $stmt->fetch();

Prepared statements separate code from data, which removes SQL injection as a class of bug rather than a case-by-case risk.

2. Hash passwords with password_hash

Never write your own hashing, and never use md5 or sha1 for passwords.

$hash = password_hash($password, PASSWORD_DEFAULT);
password_verify($inputPassword, $hash); // boolean

PASSWORD_DEFAULT currently maps to bcrypt and will move to stronger algorithms over time. Existing hashes keep verifying, and you can rehash on login when the cost changes.

3. Escape output, not input

echo htmlspecialchars($userInput, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

Store data as it arrives; escape it where it is rendered. Escaping on input corrupts data, escaping on output prevents XSS.

4. Validate with allow-lists

Check that a value is one of the values you expect, rather than banning the ones you fear.

$allowed = ['reception', 'clinician', 'lab', 'pharmacy'];
if (!in_array($role, $allowed, true)) {
    http_response_code(400);
    exit('Invalid role');
}

5. Configure sessions properly

session_set_cookie_params([
    'httponly' => true,
    'secure' => true,
    'samesite' => 'Lax',
]);
session_start();

Regenerate the session ID after login to prevent session fixation.

6. Protect forms with CSRF tokens

Every state-changing request should carry a token tied to the user's session, verified with a constant-time comparison.

7. Never expose credentials

Database passwords, API keys and tokens belong in environment variables or files outside the web root. A single .env accidentally served as text has leaked more production databases than any clever exploit.

8. Handle uploads like they are hostile

Validate MIME type and extension, store outside the web root or serve through a controller, generate random filenames, and never execute anything from an upload directory.

9. Turn error reporting off in production

Show users a friendly page; log details privately. Stack traces in production are a free map of your file system.

10. Rate-limit authentication

Limit login attempts per account and per IP, add exponential backoff, and log failures. Brute-force protection is not optional for admin panels.

None of this is complicated. It is simply disciplined. The systems we build treat these as defaults, and the maintenance conversations afterwards are much shorter because of it.