PHP Development

Building Secure PHP Applications: 2022 Best Practices

PHP powers a huge portion of the web, which makes it a top target for attackers. The OWASP Top 10 — the definitive list of critical web application security risks — maps directly onto mistakes PHP developers make regularly. This post covers the essential defences every PHP application should implement in 2022.

SQL Injection: Parameterised Queries Are the Only Defence

Never interpolate user input into SQL strings. Always use PDO prepared statements:

// VULNERABLE — never do this
$result = $db->query("SELECT * FROM users WHERE email = '{$_POST['email']}'");

// SAFE — parameterised query
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();

Cross-Site Scripting (XSS)

Escape all output that includes user-controlled data. htmlspecialchars() with ENT_QUOTES and UTF-8 is the standard:

// Safe output
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');

// In WordPress
echo esc_html($value);
echo esc_attr($value); // for HTML attributes
echo esc_url($url);    // for URLs

CSRF Tokens

Every state-changing form (login, settings, delete) must include a CSRF token. Generate a cryptographically random token, store it in the session, verify it on submission:

// Generate
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

// In form HTML
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">

// Verify
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    http_response_code(403);
    die('CSRF validation failed');
}

Password Hashing

Use password_hash() with PASSWORD_BCRYPT or PASSWORD_ARGON2ID. Never use MD5, SHA1, or unsalted hashes:

// Hash on registration
$hash = password_hash($plaintext, PASSWORD_ARGON2ID);

// Verify on login
if (password_verify($plaintext, $storedHash)) {
    // authenticated
}

Security Headers

header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('Content-Security-Policy: default-src 'self'; script-src 'self'');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');

Input Validation vs Sanitisation

Validate that input meets your expectations (is it an email? is it within the allowed range?). Sanitise when you must store or display data that may contain special characters. Never rely solely on client-side validation — always validate server-side.

Security is not a one-time checklist — it is an ongoing discipline. Run your code through static analysis tools like PHPStan and Psalm, keep dependencies updated with composer audit, and conduct regular security reviews of authentication and data-handling code.

Share this post:
Copied!

Leave a Comment

Your email will not be published.

READY TO BUILD?

Let's Build Something
Amazing Together

Tell us about your project. We'll have a proposal in your inbox within 24 hours.

Free Consultation
NDA Available
Fixed-price Options
Dedicated PM