Safeguard
Security

PHP Security Issues: The Vulnerabilities That Still Bite in 2025

Most PHP security issues come down to a handful of repeatable mistakes: unsanitized input, weak session handling, and outdated dependencies. Here is what breaks and how to fix it.

Karan Patel
Platform Engineer
5 min read

The PHP security issues that actually cause breaches are rarely exotic — they are the same input-handling, session, and dependency mistakes repeated across countless codebases. PHP powers a huge share of the web, which makes it a large target, but the language itself is not the problem. The problem is patterns that were acceptable in 2008 living on in code that ships today.

This guide walks the categories that show up in real incident reports, with the fixes you can apply this week.

SQL injection is still the number one offender

Concatenating user input into a query is the classic mistake. Consider this shape of code, which you should never ship:

// Vulnerable: user input flows straight into the query
$query = "SELECT * FROM users WHERE email = '" . $_POST['email'] . "'";
$result = mysqli_query($conn, $query);

An attacker who controls email can rewrite the query's logic. The fix is prepared statements with bound parameters, which keep data and code separate:

// Safe: parameterized query with PDO
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?');
$stmt->execute([$_POST['email']]);
$user = $stmt->fetch();

Use PDO or mysqli prepared statements everywhere, with no exceptions for "trusted" input. There is no trusted input.

Cross-site scripting from unescaped output

If you echo user-controlled data into HTML without encoding it, you have a cross-site scripting (XSS) hole. The reflected form looks like this:

// Vulnerable: renders attacker-controlled markup
echo "Welcome, " . $_GET['name'];

Escape on output, contextually. For HTML body context, htmlspecialchars with the right flags is the baseline:

echo 'Welcome, ' . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');

Better still, use a templating engine like Twig that auto-escapes by default, and add a Content-Security-Policy header so an injected script has nowhere to run.

Insecure session and authentication handling

Weak session management undermines even well-written code. The common failures: not regenerating the session ID after login (session fixation), storing session tokens in a way that is readable by JavaScript, and rolling your own password hashing.

Fix the fundamentals:

session_regenerate_id(true);          // after privilege change
// Cookie flags that block theft and CSRF
session_set_cookie_params([
    'httponly' => true,
    'secure'   => true,
    'samesite' => 'Strict',
]);
// Password hashing — let PHP pick the algorithm
$hash = password_hash($password, PASSWORD_DEFAULT);

Never store passwords with md5 or sha1. password_hash and password_verify handle salting and algorithm upgrades for you.

File uploads and path traversal

Upload handlers are a favorite entry point. Validate the file type by content, not the client-supplied extension or MIME type; store uploads outside the web root; and generate your own filenames rather than trusting the user's. Watch for path traversal, where an input like ../../etc/passwd escapes the intended directory. Normalize and validate any path derived from user input with realpath and a prefix check.

Outdated and vulnerable dependencies

A large portion of PHP security issues today live in third-party packages, not in your own code. Composer makes pulling in dependencies trivial, and those dependencies pull in their own. A known vulnerability in a transitive package is just as exploitable as one you wrote.

Audit regularly:

# Composer's built-in advisory check
composer audit

composer audit flags installed packages with known advisories. For continuous coverage across every branch and a full transitive graph, an SCA tool integrated into CI will catch a vulnerable package the moment it enters a lockfile rather than at your next manual audit. This matters because the gap between disclosure and exploitation keeps shrinking.

Configuration and error handling

Ship with display_errors off in production so stack traces never leak paths or query structure. Set expose_php to off to stop advertising your version. Keep the PHP runtime itself current — end-of-life PHP branches stop receiving security fixes, and running one is a standing liability. Track the supported-versions list on php.net and plan upgrades before a branch reaches end of life.

FAQ

What is the most common PHP security issue?

SQL injection and cross-site scripting remain the most frequently exploited, both stemming from unsanitized input. Prepared statements and contextual output encoding eliminate the large majority of cases.

Is PHP less secure than other languages?

No. PHP has a large attack surface because of its ubiquity, but modern PHP with prepared statements, password_hash, and current frameworks is as secure as any comparable stack. Most issues come from legacy patterns, not the language.

How do I check my PHP project for vulnerable dependencies?

Run composer audit for a point-in-time check against the advisory database. For continuous monitoring of direct and transitive dependencies across branches, use a software composition analysis tool wired into your pipeline.

Does keeping PHP updated actually matter for security?

Yes. End-of-life PHP versions stop receiving security patches entirely, so any newly discovered flaw in them stays unfixed. Upgrading to a supported branch is one of the highest-value security actions you can take.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.