Auditing Your LAMP Stack's EOL Exposure in an Afternoon

Most of the LAMP servers we are called into are not insecure because someone made a bad decision. They are insecure because nobody has run the inventory in three years, and support windows closed quietly while the application kept serving traffic. The stack is fine. The version numbers are the problem.

This is the audit we run first on every engagement. It takes an afternoon, it needs no agent installed, and it ends with a dated list you can hand to whoever signs off on the work.

The dates that matter

Write these down before you start, because "is it supported?" is otherwise an argument:

  • PHP 7.4 — security support ended 28 November 2022. PHP 8.0 ended 26 November 2023. PHP 8.1 ended 31 December 2025. PHP 8.2 is in its security-only year through 31 December 2026; 8.3 and 8.4 are the versions with runway.
  • MySQL 5.7 — extended support ended 31 October 2023. No more security fixes from Oracle; 8.0 itself entered its final support phase in 2026, with 8.4 LTS as the successor.
  • CentOS 7 — end of life 30 June 2024, and there is no in-place path to a supported successor. Ubuntu 20.04 LTS left standard support 31 May 2025 (Pro/ESM continues, if you pay for and enable it). Debian 11 is on LTS-only maintenance.

A distro-packaged PHP can muddy this: RHEL and Ubuntu backport security fixes into their own PHP builds for the life of the OS, so php -v showing 7.4 on a supported Ubuntu 20.04 with ESM is a different risk from 7.4 compiled from source in 2019. Find out which you have — that answer changes the urgency, and it is worth ten minutes.

Step 1: what is actually running

Not what the deploy docs say. What the process table says.

php -v && php --ini | head -3
php-fpm -v 2>/dev/null || /usr/sbin/php-fpm8.2 -v
mysql --version; mysql -e "SELECT VERSION(), @@version_comment\G"
httpd -v 2>/dev/null || apache2 -v
cat /etc/os-release; uname -r
openssl version

Two traps here. First, the CLI PHP and the FPM PHP are frequently different builds with different php.ini files; the web path is the one exposed to the internet. Second, uname -r versus the installed kernel package tells you whether the box has been rebooted since its last kernel update — a server patched but not rebooted for 400 days is running the old kernel.

List every PHP version present, not just the default:

dpkg -l | grep -E '^ii\s+php' | awk '{print $2, $3}'    # Debian/Ubuntu
rpm -qa | grep -E '^php' | sort                          # RHEL family

Old FPM pools left running after an upgrade are common, and they are still reachable.

Step 2: unpatched packages

apt-get update && apt-get -s upgrade | grep -i security
# or
dnf updateinfo list security

Then check whether patching is automatic at all:

systemctl status unattended-upgrades
cat /etc/apt/apt.conf.d/20auto-upgrades

If unattended security upgrades are off, turn them on for the security pocket. It is a ten-minute change that removes an entire category of exposure, and it is genuinely low-risk when limited to security updates on a server whose services get restarted on a schedule.

Step 3: application dependencies

The OS is usually not where the CVEs are. The vendor directory is.

composer audit --format=table
composer outdated --direct

composer audit checks your installed versions against the PHP Security Advisories database and is built into Composer 2.4+. Run it against composer.lock from the deployed release, not your laptop's. If the project has no lock file committed, that is finding number one: you do not currently know what is deployed.

Expect noise. Rank findings by whether the vulnerable code path is reachable from a request — a deserialization bug in a library used only by a CLI import script is not the same as an unauthenticated RCE in the framework's HTTP layer. Say so in the report; inflating everything to critical is how remediation lists get ignored.

For front-end assets, npm audit --omit=dev on the build tooling. For WordPress or Magento-adjacent builds, inventory plugins and themes with versions and last-update dates; an abandoned plugin is a permanent finding.

Step 4: TLS and exposure surface

nmap -sT -p- --open <host>           # from outside, with permission
ss -tlnp                             # from inside: what is listening
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null | openssl x509 -noout -dates -issuer

What we look for: MySQL listening on 0.0.0.0 instead of 127.0.0.1, forgotten admin tools (phpMyAdmin, Adminer, /status, /server-info) reachable without auth, TLS 1.0/1.1 still enabled, certificates expiring inside 30 days with no renewal automation, and directory listings from a stray Options +Indexes.

Also check PHP's own posture: expose_php, display_errors, and allow_url_include in the FPM ini, plus whether the application's .env, .git directory, or backup .sql files are served from the document root. That last one costs nothing to test:

curl -sI https://example.com/.env https://example.com/.git/config

Step 5: turn it into an order of work

A list of forty findings is not a plan. We rank on two axes — reachable from the internet without credentials, and cheap to fix — and produce four buckets:

  1. Today: exposed secrets, unauthenticated admin tools, a database bound to a public interface, an expired certificate. Hours of work, no upgrade required.
  2. This month: enable unattended security upgrades, apply pending OS patches with a reboot window, patch the dependency CVEs that resolve with a minor version bump.
  3. This quarter: the version moves — PHP to a supported branch, MySQL 5.7 to 8.x, the OS to a supported release. These are projects with rehearsals and rollback paths, not afternoon work, and they get planned properly.
  4. Accepted: things you have looked at and decided to live with, written down with a date and a name. An accepted risk recorded is fine. An unnoticed one is not.

The honest part: on a decent number of the servers we audit, buckets 1 and 2 are close to empty, and the whole finding is "you are two years from a support cliff, start planning the PHP upgrade for the next quiet quarter." That is a good outcome, and it is worth an afternoon to know it rather than assume it.

If you want the version-move buckets scoped by someone who has done them before, that is our LAMP Health & Security Review — and if the audit says you are fine, we will tell you that too.