OWASP Top 10:2025 for PHP Developers

A01:2025 — Broken Access Control

Users can act outside their permissions. Includes SSRF. Most prevalent risk.

<?php
// Vulnerable
if ($_GET['user_id'] == $currentUserId) { ... }

// Secure: enforce on every request
if (!$auth->hasPermission('view_user', $requestedUserId)) {
    http_response_code(403);
    exit('Access denied');
}

// SSRF mitigation
$allowedHosts = ['api.example.com'];
$host = parse_url($url, PHP_URL_HOST);
if (!in_array($host, $allowedHosts, true)) {
    die('Invalid URL');
}
?>

A02:2025 — Security Misconfiguration

Default configs, exposed debug, unnecessary features.

<?php
// Production bootstrap
ini_set('display_errors', '0');
error_reporting(E_ALL); // but never display
?>

A03:2025 — Software Supply Chain Failures

Compromised dependencies, malicious packages, outdated components.

A04:2025 — Cryptographic Failures

Weak algorithms, improper key management, sensitive data exposure.

<?php
// Secure password storage (never roll your own)
$hash = password_hash($password, PASSWORD_ARGON2ID);

// Verify
if (!password_verify($password, $storedHash)) { ... }

// Never use MD5/SHA1 for passwords
// Use sodium for symmetric encryption (libsodium is bundled)
$key = sodium_crypto_secretbox_keygen();
$encrypted = sodium_crypto_secretbox($data, $nonce, $key);
?>

A05:2025 — Injection

SQLi, command injection, LDAP injection, etc. Still common in PHP.

<?php
// SQL Injection prevention
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $userId]);

// Command injection
$safeDir = escapeshellarg($userInput);
exec("ls $safeDir");

// Use filter_input + whitelist validation
?>

A06:2025 — Insecure Design

Flaws in architecture and threat modeling. Not fixable with code alone.

A07:2025 — Authentication Failures

Weak credential handling, session fixation, credential stuffing.

<?php
session_start([
    'cookie_httponly' => true,
    'cookie_samesite' => 'Strict',
    'cookie_secure' => true,
    'use_strict_mode' => true
]);

// After login
session_regenerate_id(true);

// Rate-limit login attempts (use Redis or DB)
?>

A08:2025 — Software or Data Integrity Failures

Deserialization, unsigned updates, CI/CD tampering (below supply chain).

<?php
// Never use unserialize() on user input
// Prefer JSON + validation or signed JWTs

// Validate signed data
if (!hash_equals($expectedSignature, $providedSignature)) {
    die('Integrity check failed');
}
?>

A09:2025 — Security Logging and Alerting Failures

Insufficient logging of security events, no monitoring.

A10:2025 — Mishandling of Exceptional Conditions

Information disclosure via stack traces, improper error handling.

<?php
// Global handler
set_exception_handler(function (Throwable $e) {
    error_log($e->getMessage()); // never echo to user
    http_response_code(500);
    echo 'Internal server error';
});

// PDO exceptions
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
?>