1. Red
  2. Learn
  3. Website security basics

How to verify a vulnerability is fixed: 5 proofs to ask for

How to verify a patch worked: five pieces of evidence that show a vulnerability is fixed, what each proves, what it misses, and a PHP2Shell worked example.

By Alexander Gusev, run.cloudPublished 24 October 20268 min readHow we check facts
On this page
  1. What does "fixed" actually mean?
  2. Which five pieces of proof should I ask for?
  3. How do I read the package proof?
  4. How do I prove the service restarted?
  5. What does a scan from outside prove?
  6. Am I confirmed? A three-state self-test
  7. What exact question gets a usable answer from a host?
  8. How do I check a fix on a fully managed platform?
  9. Does verification need a retest by a tester?
  10. What should the record say?

To verify a vulnerability is fixed, collect evidence from the running service, not from the dashboard. You need the fixed package or build that names the CVE, a restart or redeploy after it, and a check that the process serving your domain is the one you updated. A version number or a clean outside scan alone proves little.

FactDetailSource
What counts as "fixed"The fixed code is installed, and every process serving requests started after the installPHP notice, checked 24 October 2026
Why the version number misleadsDistributions keep the upstream number and backport the fixDebian security FAQ
Why the restart mattersCanonical says the application using PHP would likely need a restart, or a rebootCanonical, 21 October 2026
Worked example in this pagePHP2Shell, CVE-2026-109618 and CVE-2026-109619, fixed in 8.5.12, 8.4.27, 8.3.36 and 8.2.35PHP notice
Standards viewPatch management includes verifying the installation of patchesNIST SP 800-40 Rev. 4

This page uses PHP2Shell as the running example because it is the live case this month. The method works for any server-side fix, and the other evergreen guides cover the neighbouring questions. For the PHP specifics, see the PHP2Shell fix guide.

What does "fixed" actually mean?

Three things have to be true at once. The fixed code is on the machine, the process that answers your visitors is running that code, and the request path to your domain reaches that process. Most "we patched" messages cover only the first.

That gap is where owners get misled. A host dashboard shows the package state. A changelog shows the vendor shipped a fix. Neither shows what is running right now for your domain.

Five steps from a shipped fix to confirmed: fix released, package installed, workers restarted, running process checked, record kept 1. Fix released vendor advisory 2. Package in changelog names CVE 3. Restarted workers replaced 4. Running check process, not panel 5. Record kept dated, forwardable Vendor trackers and host dashboards usually stop at step 2.
The five steps between "a fix exists" and "confirmed". Most status pages cover only step 2.

Which five pieces of proof should I ask for?

Ask for as many as the situation allows. Each one closes a different gap, and each has a blind spot.

ProofWhat it showsWhat it cannot show
1. Package or build identifier naming the CVEThe fixed code was installedThat anything is running it
2. Install time, then service start time after itThe process started after the fix landedThat this process serves your domain
3. A check of the running processThe live binary is the updated oneThat a second pool or host is also updated
4. A check from outsideThe visible stack hint, headers and exposureThe build, a backport, or a restart
5. A dated recordSomeone can audit it laterAnything beyond what was checked

Proofs 1 to 3 come from the server or the host. Proof 4 is the only one you can collect without them, and it is the weakest. The next sections take them in order.

How do I read the package proof?

Read the package changelog for the CVE id. Do not read the version alone, which is the backport version trap in short. Debian explains that it backports security fixes to the version shipped in the stable release rather than moving to a new upstream version. AlmaLinux tells customers to confirm a patched build with rpm -q php-common and not to rely on php -v, because its fixes are backported.

CloudLinux gives a concrete case. At its 23 October 2026 update it said most packages carry the fix as a backport, so the fixed ea-php84 build is 8.4.26-2 and not 8.4.27. A reader comparing numbers would call that unpatched. The changelog entry naming both CVE ids is what settles it.

The vendor tracker is the next place people stop. At our check on 24 October 2026 the Debian tracker listed bookworm fixed at 8.2.35-1~deb12u1 and trixie fixed at 8.4.27-1~deb13u1. That tells you the package exists. It does not tell you that your server installed it.

How do I prove the service restarted?

A fixed package on disk does nothing for a process that loaded the old code before the update, and restarting PHP-FPM after an update is the step most often skipped. Canonical's notice says the application that uses the PHP interpreter would likely need to be restarted, or the system rebooted. PHP's own notice tells operators applying its settings to restart every worker.

These checks need shell access and only read state:

  • Compare the package install time with the service start time. systemctl show <unit> -p ActiveEnterTimestamp reports when the unit last entered the active state, per the systemd interface documentation. The start must be later than the install.
  • Ask the system which services still run old code. On Debian and Ubuntu, needrestart checks which daemons need a restart after library upgrades. On RPM systems, dnf needs-restarting with -s lists the affected services.
  • Look for a replaced binary. Per the proc(5) manual, a link to an unlinked executable carries the string (deleted). A PHP process showing that predates the update.
  • If you run PHP-FPM, read the pool's own start time. The FPM status page exposes a pool "start time" and, in full mode, a per-process start time.

For containers there is no restart to prove. The container is replaced, so compare the running container's image digest with the image you built after the base image changed. On managed platforms, the instruction is usually a redeploy, as Heroku's changelog says: redeploy your apps to pick up the fixed versions.

What does a scan from outside prove?

Less than most people hope. An outside check reads what the site volunteers: headers, a version string, known paths, exposed files. It cannot see the package changelog, which runtime serves a given virtual host, or when workers last started.

A competitor's own checker page makes the same admission. Aikido's PHP2Shell checker says a clean result does not prove PHP is patched, and that a version in X-Powered-By is a hint that may not account for vendor backports. A clean result can mean the fix is installed, but also a rejected request, an untested path or a proxy in front.

The same logic applies to a firewall. Cloudflare says customers should still patch their PHP installations, with WAF protection as an extra layer. A blocked probe means the edge did its job (what a WAF does and does not cover). It says nothing about the origin.

Outside checks still earn a place. They catch the cases where nothing needs asking: a visible version already below the fix, an exposed .env, a forgotten service. They are a first filter, not the finish.

Am I confirmed? A three-state self-test

Am I affected? Where you stand right now

An outside view can give you the first two states. Only evidence from the server or the host gives you the third.

  1. Detected A signal from outside puts your version below the fix for its branch. Treat the fix as not done. Patch and restart, or send your host the question below today.
  2. Unknown The version is hidden, could be a backport, or you cannot tell which runtime serves this domain. This is the common case. Ask for the package identifier and the restart time.
  3. Confirmed by owner evidence You hold a changelog entry naming the CVE and a service start or redeploy time after the install. The fix is in. Whether anything got in before it is a separate question.

No scan, ours included, moves you from "unknown" to "confirmed".

What exact question gets a usable answer from a host?

Vague questions get vague replies. "Is it patched?" invites "yes, fully deployed". Name the CVE, the domain and the two pieces of evidence you want.

For [your domain], is the runtime that serves web requests fixed for [CVE ids]? Please give the fixed package, build or backport identifier, the time all workers serving it were restarted or replaced, and whether I need to redeploy or change a setting.

Then read the reply against this table.

Reply you getStateNext step
"Fully deployed, no action needed"UnknownAsk again for the build and the restart time
A build identifier, no restart timeUnknownAsk when the workers were replaced
Build naming the CVE plus a restart time after installConfirmed by owner evidenceKeep the message with its date
"Your PHP is customer-managed"Not the host's jobCheck it yourself with the steps above
No reply within your own deadlineUnknownEscalate, or reduce exposure while you wait

Who is responsible for patching, host or site owner, is covered separately. For the PHP-specific wording to send a provider, see questions for a shared host.

How do I check a fix on a fully managed platform?

You usually cannot see the process, so you verify the platform's output instead. Read the deploy or build log for the resolved runtime. Confirm the running release is newer than the redeploy. Ask support for the deployed build or image digest. Heroku's PHP support instruction is a redeploy, and a rebuild that reuses an unchanged lock file can resolve the same old runtime, so the log matters more than the fact that you deployed.

Does verification need a retest by a tester?

Sometimes. The OWASP Web Security Testing Guide says focused penetration testing can be useful for detecting whether specific vulnerabilities are fixed in the deployed code. That fits flaws in your own application, where the fix is a code change and the proof is that the attack path no longer works.

It fits server-side runtime bugs less well. A failed attempt against a patched-looking server does not prove a fix, because the attempt may have missed the condition. Use a retest as one more piece of evidence, never as the only one. A request-level test also needs the owner's permission, and nobody should run one against a host they do not control.

What should the record say?

A fix is easier to defend when it leaves a note. Keep one short record per site, and date it.

  • The CVE ids and the vendor advisory link
  • The fixed package or build identifier, from the changelog
  • The install time and the restart or redeploy time
  • The check you ran on the running service, and its result
  • What you could not see, such as other pools or the origin behind a proxy
  • The date, and who ran the checks

That note answers the question clients and managers ask next: how do you know. It also keeps patch state apart from a different question, whether anything entered before the fix. For that, see the step-by-step incident checklist and, for PHP2Shell, whether your site was exploited.

If you want an outside view of what your own site shows, the free scan reads the stack your site serves and matches each version to known vulnerabilities. It is a lookup. It can say a version is affected, not whether your setup is exposed. For the wider list of this month's fixes, the month's incidents and patches in one list links each one.

Questions people ask

How do I know my host really patched PHP, and not just said so?

Ask for the fixed package or build identifier and the time the PHP workers were restarted or replaced, for your domain, in writing. A message that says 'fully deployed' with no build and no restart time is a claim, not evidence. If the host cannot give both, treat the answer as unknown.

php -v shows an old version after the update. Did the patch fail?

Not necessarily. Distributions backport fixes and keep the upstream number, and the command-line PHP can be a different binary from the web one. Read the package changelog for the CVE id and check which binary serves the site.

I updated the package. Why is the site still listed as vulnerable?

A running PHP-FPM or Apache process keeps the code it loaded when it started. Until it is restarted, the old code serves requests, whatever is on disk. Compare the service start time with the package install time.

Does a clean scan result mean the vulnerability is fixed?

No. A scan from outside can show a version hint, and a clean result can also mean the request was rejected, the path was untested or a proxy hid the answer. Only the package, the restart and the running process settle it.

What should I send a client to prove I patched their site?

A short dated note with the CVE ids, the fixed package or build, the restart time, the check you ran against the running service, and what you could not see. It lets them forward the answer instead of repeating your claim.

Does patching also tell me whether I was already hacked?

No. Patching closes the hole and removes nothing that was planted earlier. Whether anything got in before the fix is a separate question that needs logs and a file and account review.

Sources

  1. PHP security notice, 21 October 2026 (php.net)Fixed releases, scope, and the instruction to restart every worker. Checked 24 October 2026.
  2. NIST SP 800-40 Rev. 4: Guide to Enterprise Patch Management PlanningDefines patch management as including verifying the installation of patches.
  3. Debian Security FAQ: backportingWhy a fixed Debian package keeps the old upstream version number.
  4. Canonical: PHP2Shell vulnerability fixes availableFixed package per Ubuntu release, and the restart or reboot instruction.
  5. Debian security tracker: CVE-2026-109618Per-release package status. It records the package, not a restart. Checked 24 October 2026.
  6. AlmaLinux: PHP2Shell patched packages (21 October 2026)Says to verify with rpm -q, not php -v.
  7. CloudLinux: fixed ea-php and alt-php packagesUpdated 23 October 2026. Backported builds keep lower numbers; read the changelog for the CVE ids.
  8. Heroku changelog: PHP runtime builds with the PHP2Shell fixesInstructs customers to redeploy.
  9. dnf needs-restarting (dnf-plugins-core documentation)Lists services that still run pre-update code.
  10. needrestart (Debian manual page)Checks which daemons need a restart after library upgrades.
  11. proc_pid_exe(5), Linux manual pageA replaced executable shows (deleted) on the link.
  12. systemd D-Bus interface: ActiveEnterTimestampWhen a unit last entered the active state.
  13. PHP manual: FPM status pagePool and per-process start time fields.
  14. Aikido PHP2Shell checkerA competitor's own page says a clean result does not prove PHP is patched. Checked 24 October 2026.
  15. OWASP Web Security Testing Guide, introductionTargeted testing can detect whether specific vulnerabilities are fixed in the deployed code.
  16. Cloudflare: PHP vulnerabilities and WAF protectionCustomers should patch even with WAF protection in place. Checked 24 October 2026.

Facts on this page are dated and point at the primary source next to them. If a vendor status changes after the date shown, the vendor's page wins; tell us at support@run.cloud and we will update it.