<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>secrets on Daniel Fedick</title>
    <link>https://blog.fedick.net/tags/secrets/</link>
    <description>Recent content in secrets on Daniel Fedick</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 21 Oct 2026 00:00:00 -0400</lastBuildDate>
    <atom:link href="https://blog.fedick.net/tags/secrets/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>PHP2Shell, the FBI Warning, and the Password in Your Config File</title>
      <link>https://blog.fedick.net/posts/php2shell-vault-dynamic-db-creds/</link>
      <pubDate>Wed, 21 Oct 2026 00:00:00 -0400</pubDate>
      <guid>https://blog.fedick.net/posts/php2shell-vault-dynamic-db-creds/</guid>
      <description>Two critical PHP flaws can let one unauthenticated request run code on a web server. Patch first. Then look at what that server could read, how Vault dynamic database credentials limit the damage, how to vet software before it ships, and why long-lived secrets matter for harvest now, decrypt later.</description>
      <content:encoded><![CDATA[<h2 id="the-php2shell-vulnerabilities">The PHP2Shell vulnerabilities</h2>
<p>On October 21, the FBI Cyber Division <a href="https://x.com/FBICyberDiv/status/2112921648252719420">posted on X</a> that two critical vulnerabilities have been found in PHP, &ldquo;a widely used programming language that powers websites and web applications.&rdquo; The FBI says the flaws, CVE-2026-109618 and CVE-2026-109619, &ldquo;could allow malicious cyber actors to remotely execute code in PHP core.&rdquo;</p>
<p>Researchers call the pair <strong>PHP2Shell</strong>. The PHP project gave advance warning on October 20, and public reports that day said the bugs could lead to remote code execution. Fixed releases came out on October 21 at about 10:00 AM ET (14:00 UTC): <strong>8.5.12, 8.4.27, 8.3.36 and 8.2.35</strong>. PHP rates both CVEs 9.2 on CVSS v4.0.</p>
<p>Any PHP install that serves web requests is affected, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The PHP team says mod_php is the riskiest setup, with a demonstrated exploit where one unauthenticated request starts the attack before any application code runs. Code runs with the rights of the web worker account. Proof-of-concept code is circulating, and Cloudflare said it was already seeing early attacks before the patches shipped. PHP 8.1 and older no longer get updates from the PHP project, though the team published minimal source backports for 7.4, 8.0 and 8.1.</p>
<figure>
    <img loading="lazy" src="/images/php2shell-what-happened.svg"
         alt="PHP2Shell attack path: an attacker sends one unauthenticated HTTP request, a PHP core flaw (CVE-2026-109618 or CVE-2026-109619, CVSS v4.0 9.2) lets it run code, the attacker gets remote code execution on the web server, then reads config files and secrets and reaches the database. Fix: update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, released October 21 at about 10:00 AM ET."/> 
</figure>

<p>Sources: <a href="https://www.bleepingcomputer.com/news/security/php-ships-emergency-patches-for-critical-php2shell-flaws/">BleepingComputer</a>, <a href="https://cert.europa.eu/publications/security-advisories/2026-018">CERT-EU advisory 2026-018</a>, and the <a href="https://www.php.net/archive/2026.php#2026-10-20-1">PHP project announcement</a>.</p>
<p>Patch now. The PHP team says there are no complete mitigations and updating is the only safe choice.</p>
<h2 id="credential-exposure">Credential exposure</h2>
<p>Patching closes the hole, but it doesn&rsquo;t tell you what already walked out. If someone ran code on that server, the first thing to ask is what they could read.</p>
<p>On a lot of systems the answer is the database password sitting in <code>config.php</code> or <code>.env</code>. It&rsquo;s often shared by several apps, it usually has more access than any of them need, and it hasn&rsquo;t changed in years.</p>
<p>It hasn&rsquo;t changed because changing it is a pain. You update the password in the database, then in every app and server that uses it, then restart all of them at once. If you miss one, something breaks. So rotation keeps getting pushed off, and a stolen copy keeps working long after the patch is in.</p>
<h2 id="dynamic-database-credentials">Dynamic database credentials</h2>
<p>With HashiCorp Vault&rsquo;s <a href="https://developer.hashicorp.com/vault/docs/secrets/databases">database secrets engine</a>, each app gets its own database login that expires:</p>
<ol>
<li>When the app starts, it proves who it is to Vault, using something like its Kubernetes service account, cloud identity or AppRole.</li>
<li>Vault creates a new database user just for that app, with only the permissions it needs.</li>
<li>The credential comes with a <strong>lease</strong>, like a key card that stops working at checkout.</li>
<li>While the app runs, it renews the lease, up to a hard maximum.</li>
<li>When the lease runs out or someone revokes it, Vault deletes that database user.</li>
</ol>
<p>The app itself needs very little: a way to log in to Vault and a way to read the credential it&rsquo;s given. Most teams don&rsquo;t write that code. <a href="https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent">Vault Agent</a>, running next to the app or as a sidecar, logs in, fetches the credential, writes it to a file, and keeps the lease renewed. The app reads a file the way it used to read <code>config.php</code>.</p>
<p>Rotation also turns into a restart, because a restarted app gets a brand-new username and password. Nobody edits a config file and nothing needs a coordinated change.</p>
<p>You can cut off a credential two ways:</p>
<ul>
<li><strong>By time.</strong> Every credential has a TTL. When it ends, the credential is gone.</li>
<li><strong>By command.</strong> One <code>vault lease revoke</code> kills a single credential, and <code>vault lease revoke -prefix</code> kills every credential issued from a role. Vault <a href="https://developer.hashicorp.com/vault/docs/concepts/lease">tracks every lease</a>, so it knows what to remove.</li>
</ul>
<figure>
    <img loading="lazy" src="/images/php2shell-flow.svg"
         alt="Before: a PHP app reads a password that never expires from config.php and uses it on every database request. After: the app logs in to HashiCorp Vault, gets a policy-scoped token, and requests credentials. Vault creates a PostgreSQL role with an expiry, then returns a unique username and password with a one-hour lease. The app connects with these, renews the lease while it runs, and Vault drops the role when the lease expires or is revoked."/> <figcaption>
            <p>A static password lasts forever. A Vault dynamic credential lasts only as long as its lease.</p>
        </figcaption>
</figure>

<p>Every app instance gets its own username, so database logs show which app did what.</p>
<h2 id="vault-configuration">Vault configuration</h2>
<p>Below is a minimal PostgreSQL example. The syntax follows HashiCorp&rsquo;s <a href="https://developer.hashicorp.com/vault/docs/secrets/databases/postgresql">PostgreSQL database secrets engine docs</a>. Names and passwords are placeholders.</p>
<figure>
    <img loading="lazy" src="/images/php2shell-db-secrets-engine.svg"
         alt="The PHP app logs in to Vault with AppRole, Kubernetes or a cloud identity and gets a token scoped by policy. It reads database/creds/app-readonly. Vault uses its own admin connection, database/config/app-postgres, whose password was rotated with rotate-root so only Vault knows it, and runs the role&#39;s creation statements: CREATE ROLE with a password and VALID UNTIL expiry, then GRANT SELECT. Vault returns a unique username, password and lease with a default TTL of 1h. The app connects to PostgreSQL with those credentials. Renewing the lease extends it up to the 24h max TTL. When the TTL ends or someone runs vault lease revoke, Vault drops the database user."/> 
</figure>

<p><strong>1. Turn on the database secrets engine.</strong></p>

<figure class="code" data-lang="bash">
<figcaption><span class="lang">bash</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vault secrets <span class="nb">enable</span> database</span></span></code></pre></div>
</figure><p><strong>2. Tell Vault how to reach the database.</strong> Vault uses this account to create and remove users. Give it the minimum rights it needs to do that.</p>

<figure class="code" data-lang="bash">
<figcaption><span class="lang">bash</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vault write database/config/app-postgres <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">plugin_name</span><span class="o">=</span><span class="s2">&#34;postgresql-database-plugin&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">allowed_roles</span><span class="o">=</span><span class="s2">&#34;app-readonly&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">connection_url</span><span class="o">=</span><span class="s2">&#34;postgresql://{{username}}:{{password}}@db.internal:5432/appdb&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">username</span><span class="o">=</span><span class="s2">&#34;vault-admin&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">password</span><span class="o">=</span><span class="s2">&#34;change-me-then-rotate&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">password_authentication</span><span class="o">=</span><span class="s2">&#34;scram-sha-256&#34;</span></span></span></code></pre></div>
</figure><p>The <code>password_authentication</code> option needs Vault 1.14 or later.</p>
<p><strong>3. Define a role.</strong> This is the recipe Vault follows every time it creates a user: what SQL to run and how long the credential lives.</p>

<figure class="code" data-lang="bash">
<figcaption><span class="lang">bash</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vault write database/roles/app-readonly <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">db_name</span><span class="o">=</span><span class="s2">&#34;app-postgres&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">creation_statements</span><span class="o">=</span><span class="s2">&#34;CREATE ROLE \&#34;{{name}}\&#34; WITH LOGIN PASSWORD &#39;{{password}}&#39; VALID UNTIL &#39;{{expiration}}&#39;; \
</span></span></span><span class="line"><span class="cl"><span class="s2">        GRANT SELECT ON ALL TABLES IN SCHEMA public TO \&#34;{{name}}\&#34;;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">default_ttl</span><span class="o">=</span><span class="s2">&#34;1h&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">max_ttl</span><span class="o">=</span><span class="s2">&#34;24h&#34;</span></span></span></code></pre></div>
</figure><p><code>default_ttl</code> is how long a credential lasts before it must be renewed. <code>max_ttl</code> is the hard ceiling. After that, the app has to ask for a new one.</p>
<p><strong>4. The app asks for credentials.</strong></p>

<figure class="code" data-lang="bash">
<figcaption><span class="lang">bash</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vault <span class="nb">read</span> database/creds/app-readonly</span></span></code></pre></div>
</figure>
<figure class="code" data-lang="text">
<figcaption><span class="lang">text</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Key                Value
</span></span><span class="line"><span class="cl">---                -----
</span></span><span class="line"><span class="cl">lease_id           database/creds/app-readonly/2f6a614c-...
</span></span><span class="line"><span class="cl">lease_duration     1h
</span></span><span class="line"><span class="cl">lease_renewable    true
</span></span><span class="line"><span class="cl">password           SsnoaA-8Tv4t34f41baD
</span></span><span class="line"><span class="cl">username           v-token-app-read-...</span></span></code></pre></div>
</figure><p>In practice, the app doesn&rsquo;t run this by hand. <a href="https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent">Vault Agent</a> or a Vault client library fetches and renews the credential so the app code only reads a file or an environment.</p>
<p><strong>5. Rotate the root credential.</strong> The password you typed in step 2 is now known to you and your shell history. Have Vault rotate it so only Vault knows it:</p>

<figure class="code" data-lang="bash">
<figcaption><span class="lang">bash</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vault write -f database/rotate-root/app-postgres</span></span></code></pre></div>
</figure><p>After this, nobody (including you) knows the password Vault uses to manage the database. See the <a href="https://developer.hashicorp.com/vault/api-docs/secret/databases#rotate-root-credentials">API docs for rotate-root</a>.</p>
<p><strong>6. Revoke by command.</strong> If you think a server was compromised, cut off one lease, or every credential issued from a role:</p>

<figure class="code" data-lang="bash">
<figcaption><span class="lang">bash</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># One credential</span>
</span></span><span class="line"><span class="cl">vault lease revoke database/creds/app-readonly/2f6a614c-...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Every credential issued from this role</span>
</span></span><span class="line"><span class="cl">vault lease revoke -prefix database/creds/app-readonly</span></span></code></pre></div>
</figure><p>Vault drops those database users. Whatever the attacker copied stops working. (<a href="https://developer.hashicorp.com/vault/docs/commands/lease/revoke">lease revoke docs</a>)</p>
<h3 id="static-roles-for-legacy-apps">Static roles for legacy apps</h3>
<p>Some legacy apps or databases expect a fixed username. For those, Vault has <a href="https://developer.hashicorp.com/vault/docs/secrets/databases#static-roles"><strong>static roles</strong></a>: the username stays the same, but Vault owns the password and rotates it on a schedule.</p>

<figure class="code" data-lang="bash">
<figcaption><span class="lang">bash</span><button class="copy" type="button">Copy</button></figcaption><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vault write database/static-roles/legacy-app <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">db_name</span><span class="o">=</span><span class="s2">&#34;app-postgres&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">username</span><span class="o">=</span><span class="s2">&#34;legacy_app&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">    <span class="nv">rotation_period</span><span class="o">=</span><span class="s2">&#34;24h&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vault <span class="nb">read</span> database/static-creds/legacy-app</span></span></code></pre></div>
</figure><p>The <code>legacy_app</code> user must already exist in the database, and Vault changes its password as soon as you create the static role.</p>
<p>It isn&rsquo;t as strong as a unique user per app instance, but it beats a password nobody has changed in years.</p>
<h2 id="securing-the-software-supply-chain">Securing the software supply chain</h2>
<p>Short-lived credentials limit the damage after a break-in. You still want to stop vulnerable software from shipping in the first place.</p>
<p><strong>Scan at rest.</strong> Scan code repositories, build artifacts and stored images, both when they&rsquo;re created and again later. A clean scan in March says nothing about a CVE published in October.</p>
<p><strong>Scan container images.</strong> Most PHP today ships inside a container image, and an image is a box that someone else may have packed. Image scanning opens the box and lists what&rsquo;s inside: the operating system packages, the PHP runtime and its version, and the application&rsquo;s own dependencies, such as Composer packages. The scanner writes that list as an SBOM (software bill of materials) built from the image itself. It also finds old PHP runtimes baked into base images that nobody has rebuilt in a year. When a new CVE drops, rescan every image already in your registry. Don&rsquo;t wait for the next build, because the vulnerable images are the ones already running.</p>
<p><strong>Enforce policy at the gate.</strong> Scanning only helps if something acts on the results. Put a policy check in CI and at deploy time, for example a Kubernetes admission controller, that blocks builds and images that weren&rsquo;t scanned, don&rsquo;t have an SBOM, or contain a known critical CVE. Policy-as-code tools such as Open Policy Agent or HashiCorp Sentinel can express rules like &ldquo;no image with a critical CVE runs in production.&rdquo;</p>
<p><a href="https://www.ibm.com/docs/en/concert-platform?topic=overview-protect">IBM Concert Protect</a> is built for this as well. It pulls image, code and runtime scan results into one place, ranks the CVEs by risk, and can flag vulnerable packages in GitHub pull requests as soon as new commits land, before they ship.</p>
<p>There are many options for the scanning itself. Commercial platforms include <a href="https://www.wiz.io/">Wiz</a>, <a href="https://www.paloaltonetworks.com/prisma/cloud">Palo Alto Networks Prisma Cloud</a>, <a href="https://www.tenable.com/products/vulnerability-management">Tenable</a> and <a href="https://www.qualys.com/apps/vulnerability-management-detection-response/">Qualys</a>. On the open-source side, <a href="https://github.com/anchore/syft">Syft</a> generates SBOMs, and <a href="https://github.com/anchore/grype">Grype</a> and <a href="https://trivy.dev/">Trivy</a> scan images and filesystems for known vulnerabilities. If your developers already use OpenAI&rsquo;s Codex, <a href="https://help.openai.com/en/articles/20001107-codex-security">Codex Security</a> (a research preview on some ChatGPT plans) connects to GitHub repos, scans the code or individual commits, tries to confirm each finding in an isolated environment, and suggests patches for a person to review.</p>
<h2 id="how-to-use-ai-to-find-vulnerabilities">How to use AI to find vulnerabilities</h2>
<p>AI models found these bugs. In its <a href="https://openai.com/index/coordinated-disclosure-of-critical-vulnerabilities/">Patch the Planet post</a>, OpenAI says the security firm Calif found one of the flaws using an OpenAI model, and Wiz independently found and reported both. OpenAI also warns that the same models can help attackers build exploits quickly, and it expects findings like this to &ldquo;increase sharply in the coming months.&rdquo; Your own code and dependencies are worth running through the same kind of tools before someone else does.</p>
<p>A few places where AI helps a defending team:</p>
<ul>
<li><strong>Review pull requests.</strong> Have an AI reviewer read each PR for injection, unsafe deserialization, missing auth checks and similar bugs, and have a person decide what to fix. Codex Security, covered above, scans repos or individual commits, tries to confirm each finding in an isolated environment, and suggests patches for review.</li>
<li><strong>Triage scanner and SBOM output.</strong> Scanners produce long lists. Ask a model to group findings, point out which vulnerable functions your code actually calls, and draft the upgrade or rebuild steps.</li>
<li><strong>Write fuzz harnesses.</strong> Models are good at writing the boring glue code a fuzzer needs. Point them at your parsers and request handlers first, since that&rsquo;s where untrusted input lands.</li>
<li><strong>Look for hardcoded secrets.</strong> Ask the model to flag passwords, tokens and connection strings in config files, <code>.env</code> files and old commits, then move what it finds into Vault and rotate it.</li>
</ul>
<p>For PHP2Shell specifically, OpenAI published an open-source <a href="https://github.com/openai/php2shell-checker">PHP version checker</a>. You can run it with Node against a URL or a server&rsquo;s local PHP, or install it as a Codex skill. It only reads the version a server reports, so a hidden version comes back as unknown, not safe.</p>
<p>If your team protects essential services and doesn&rsquo;t have the budget, OpenAI is offering subsidized access to <a href="https://openai.com/daybreak/">Daybreak</a>, its program that gives vetted defenders AI models for security work.</p>
<p>Faster discovery also shortens the time between &ldquo;patch released&rdquo; and &ldquo;exploit in the wild,&rdquo; which is one more reason to keep an inventory you can search and credentials that expire on their own.</p>
<h2 id="software-inventory-and-sboms">Software inventory and SBOMs</h2>
<p>For a bug like PHP2Shell, the first question is &ldquo;where do we run PHP, and which version?&rdquo; If answering takes a week of emails, attackers have that week too.</p>
<p>PHP hides in more places than people expect: the main web app, an old WordPress marketing site, an internal wiki, a vendor appliance, a forgotten VM in a dev account. CERT-EU points out that many commercial products embed PHP even when they don&rsquo;t expose it directly, and suggests the <a href="https://github.com/NCSC-NL/php2shell">NCSC-NL php2shell list</a> to find them.</p>
<p>If you keep a live inventory of what&rsquo;s deployed (SBOMs for every image and build, tied to where each one runs), that question becomes a search. You can find the affected systems in minutes, patch or rebuild them in hours, and spend the rest of the day on the ones that can&rsquo;t be patched yet. Isolate those, and make sure they don&rsquo;t hold a long-lived key.</p>
<p><a href="https://www.ibm.com/docs/en/concert-platform?topic=overview-protect">IBM Concert Protect</a> is built for this. It takes in SBOMs and the results from scanners you already run, such as Prisma Cloud, Aqua or Sysdig, maps each component to the applications and environments that use it, and ranks the CVEs by risk. It can then open the remediation tickets in Jira, GitHub or ServiceNow, so a finding like PHP2Shell turns into a list of owners and systems instead of a hunt.</p>
<figure>
    <img loading="lazy" src="/images/php2shell-software-visibility.svg"
         alt="Sources: container images, source code, VMs and runtimes, and vendor appliances. Scanners produce SBOMs and vulnerability scans. A central inventory such as IBM Concert Protect maps each component to the applications and environments that use it and ranks CVEs by risk. Outputs: a search such as where do we run PHP 8.x, remediation tickets to owners in Jira, GitHub or ServiceNow, and a policy-as-code gate with Sentinel or OPA in CI and at admission that blocks vulnerable versions from being redeployed."/> 
</figure>

<h2 id="harvest-now-decrypt-later">Harvest now, decrypt later</h2>
<p>Attackers can record encrypted traffic and stolen encrypted data today, even though they can&rsquo;t read it yet. They store it and wait. Once a large enough quantum computer exists, the public-key math protecting much of today&rsquo;s traffic could be broken, and the recordings become readable. NIST <a href="https://www.nist.gov/cybersecurity/what-post-quantum-cryptography">describes this</a> as &ldquo;harvest now, decrypt later,&rdquo; and it&rsquo;s one reason NIST, NSA and <a href="https://www.cisa.gov/quantum">CISA</a> are pushing organizations to plan their post-quantum move now.</p>
<p>Long-lived secrets make harvest now, decrypt later worse. If a recording contains a database password that&rsquo;s still valid five years from now, it&rsquo;s worth decrypting. A credential that expired an hour after it was issued isn&rsquo;t worth anything when the recording is finally cracked. The same goes for a config file stolen this week through PHP2Shell: a static password is worth something for as long as nobody changes it.</p>
<p>Dynamic credentials don&rsquo;t protect the data itself, though. Customer records in that recorded traffic stay sensitive no matter how short the credential&rsquo;s life was. Protecting that data means moving key exchange to post-quantum algorithms such as ML-KEM, which NIST standardized in <a href="https://csrc.nist.gov/pubs/fips/203/final">FIPS 203</a>, often in a hybrid with today&rsquo;s algorithms during the transition. Short-lived credentials shrink how long a stolen secret stays useful, and post-quantum key exchange protects what&rsquo;s inside the traffic, so you need both.</p>
<h2 id="next-steps">Next steps</h2>
<ol>
<li><strong>Find every PHP install.</strong> You can&rsquo;t patch what you don&rsquo;t know you run. Use your inventory and SBOMs (see the inventory section above) to list every system and image with PHP, including the ones embedded in vendor products.</li>
<li><strong>Patch.</strong> Update PHP to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, then rotate any long-lived secret a vulnerable server could read.</li>
<li><strong>Block the old versions.</strong> Add a policy-as-code rule, in Sentinel or OPA, to CI and to deploy or admission checks that rejects images and builds with the vulnerable PHP versions. Otherwise an old image can get redeployed after you&rsquo;ve cleaned up.</li>
<li><strong>Move apps off static credentials.</strong> Give each app a short-lived credential from Vault that expires by time, can be revoked with one command, and gets replaced with a fresh one when the app restarts. A web server compromised after that only gives up passwords that are about to expire.</li>
</ol>
<hr>
<p><em>Views are my own and do not represent my employer.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
