Today we published our first ever set of security advisories for NativePHP for Mobile. Seven of them, all at once.
That's a lot. This is the first time NativePHP has issued advisories, and personally, it's my very first time handling anything like this. Going from zero to seven essentially in one go has been, frankly, kinda nuts. But every one of them is now fixed, and this post walks through what they were, what you need to do, and some thoughts on how they reached us.
It's also been a big week for PHP itself, so at the end I'll cover what we've done about the two critical PHP CVEs released on Wednesday.
TL;DR: what you need to do#
-
Update
nativephp/mobileto 4.6.1 (or 3.3.11 if you're on the 3.x line). -
If you use the
nativephp/mobile-fileplugin, update it to 1.0.3. If you're on 4.x, remove it instead: core already providesFile.MoveandFile.Copy, and having both installed can let the plugin's implementation shadow the safe core one. -
Regenerate your native project and rebuild:
Copied!php artisan native:install --force -
Ship the new build to your users. This will require new app store releases. Builds already on people's devices stay vulnerable until they're updated.
nativephp/mobile1.x and 2.x are end-of-life and won't receive patches. If you're still on them, please upgrade to v4.
The advisories#
Most of these share a common thread: the internal routes the web view uses to talk to the native side (/_native/api/*) had no middleware, and a handful of bridge functions trusted whatever they were given. On their own, most require an attacker to already be running script on your app's origin, for example through an XSS bug in your app or a compromised third-party script you may have bundled. That's why the CVSS scores mostly carry "high attack complexity". But defence in depth matters, and these should never have been reachable in the way they were.
GHSA-9884-fw9c-mq85: Web view pages can drive the async task bridge, leading to code injection#
High (8.1) · nativephp/mobile 4.5.0 – 4.6.0 · Fixed in 4.6.1
Script on the app's origin could call the async task bridge functions directly. A task ID that wasn't a UUID could be spliced into PHP code evaluated by the async lane, and AsyncTask.Complete let a caller post arbitrary events into a native screen's runloop. Task IDs are now validated as UUIDs on every platform, completions are only accepted from the async lane itself, and the web view bridge refuses AsyncTask.* calls entirely.
GHSA-83jv-8h7x-287p: Arbitrary object instantiation via the /_native/api/events endpoint#
High (7.7) · nativephp/mobile 3.0.0 – 3.3.10 and 4.0.0 – 4.6.0 · Fixed in 3.3.11 and 4.6.1
The event dispatch endpoint would instantiate any autoloadable class named in the request, with request-supplied constructor arguments. Using a core PHP class like SplFileObject, a page could truncate or create files anywhere writable, including your SQLite database. Events dispatched this way must now implement a new marker interface, Native\Mobile\Events\Contracts\DispatchableFromNative.
Plugin authors: if your plugin dispatches its own event classes through this endpoint, add the marker interface to them.
GHSA-3w4w-cchh-qvrw: File.Move and File.Copy accept unrestricted filesystem paths#
High (7.7) · nativephp/mobile 4.0.0 – 4.6.0 · Fixed in 4.6.1
These bridge functions accepted any absolute path, so a page could overwrite, rename or delete files anywhere in the app sandbox. Paths are now canonicalised and confined to storage_path('app'), with sensitive locations like public/, .env, the database and framework caches explicitly refused. Normal capture-to-storage moves keep working.
GHSA-7927-vv5p-fv4f: Web-view-reachable File.Move / File.Copy take unconfined paths (mobile-file plugin)#
High (7.7) · nativephp/mobile-file < 1.0.3 · Fixed in 1.0.3
The same root cause as above, but in the standalone plugin used by 3.x apps. Combined with the /_assets read-back, this could be used to copy files like .env into a readable location. 1.0.3 applies the same canonicalise-and-confine approach natively on both platforms. As mentioned above, 4.x apps should simply remove this plugin.
GHSA-9g37-88p8-75j2: Async task payloads are deserialized without authentication#
High (7.4) · nativephp/mobile 4.5.0 – 4.6.0 · Fixed in 4.6.1
The async task runner passed spooled task files straight to unserialize(). Exploiting this needs a separate way to write into app-private storage, but it's exactly the kind of sink an attack chain would aim for. Payloads are now encrypted and authenticated with your app key, bound to the specific task ID. Note that apps without an app key can no longer dispatch async tasks. Apps that don't use async tasks were never affected.
GHSA-76px-r497-93p9: Path traversal in the /_assets static asset handler#
Moderate (5.9) · nativephp/mobile 3.0.0 – 3.3.10 and 4.0.0 – 4.6.0 · Fixed in 3.3.11 and 4.6.1
The iOS and Android asset handlers didn't strip .. from the requested path, so a crafted request could read files outside public/, most notably the SQLite database. Read-only, but direct. Asset paths are now canonicalised and confined to public/.
GHSA-27jx-7m9x-fx5c: Android runtime logs Laravel session and XSRF cookies to logcat in release builds#
Moderate (4.1) · nativephp/mobile < 3.3.11 and 4.0.0 – 4.6.0 · Fixed in 3.3.11 and 4.6.1
The generated Android project logged full cookie values, including the Laravel session cookie and XSRF token, on every response, and release builds didn't strip those log calls. Other apps can't read logcat on modern Android, but it does turn up in bug reports and in crash and support tools that ship logs off the device. Cookie values are no longer logged, sensitive headers are redacted, the request inspector has been removed, and minified release builds now strip verbose log calls. On 3.x this also stops classic mode logging full POST bodies.
Thank you to the researchers#
Thank you to aragossa, who reported the logcat issue. It was a clear, well-documented report with a reproducible proof of concept, and it came to us privately. That's exactly how this should work.
Thank you to acd421, who found the other six issues. The research was thorough and it has made NativePHP more secure, and we've credited it accordingly on every advisory.
And thank you to RedEye Security, who passed the findings on to us.
On how we found out#
I want to be upfront about something, because I think it matters for the wider ecosystem.
The six issues from acd421 did not come to us through responsible disclosure. They were published publicly, along with working exploit chains, after RedEye posted their write-up. Despite what was stated in the researcher's disclosure, they did not follow our published guidance on how to report vulnerabilities to us. Our security policy asks for an email to [email protected], and private vulnerability reporting is enabled on our GitHub repositories, so a report could easily have been filed through GitHub's built-in reporting feature too.
The disclosure states that the issues were reported via Tidelift. As of writing, we still haven't received any notice from Tidelift.
We advocate for responsible disclosure. That means giving maintainers a private heads-up and a reasonable window to fix things before details and exploit code go public, so that the people who build on our tools, and their users, aren't left exposed in the meantime. This wasn't that.
I'm genuinely grateful for the research itself. But if you find a security issue in any NativePHP package, please email [email protected] or use the "Report a vulnerability" button on the repository's Security tab. We will respond, we will fix it, and we will credit you, just as we've done here.
This week's PHP CVEs#
Separately from all of the above, on Wednesday the PHP project shipped emergency security releases fixing two critical vulnerabilities, CVE-2026-109618 and CVE-2026-109619, both rated 9.2 under CVSS v4.0.
The first relates to how PHP handles out-of-memory conditions during a request; the second is a use-after-free in PHP's request state handling. The fixed releases that affect NativePHP - Mobile and Desktop - are PHP 8.5.12, 8.4.27, 8.3.36.
NativePHP apps embed their own PHP runtime, so the PHP binaries we distribute are part of your app's attack surface. Here's what we've done:
- Updated every binary. All of the PHP binaries we distribute have been rebuilt on the patched PHP releases.
- Removed the old binaries. The previous, vulnerable builds have been pulled from distribution, so you can't accidentally install one.
- Hardened the binary supply chain. Our binaries now ship with published hashes and signatures, so the tooling can verify that what you download is exactly what we built, and hasn't been tampered with along the way. This verification step is already built in to
nativephp/[email protected],nativephp/[email protected], and will be available innativephp/[email protected]in the coming days.
To pick up the patched runtime, update to the latest release and run php artisan native:install --force, then rebuild and ship your app, the same steps as above. If you follow the TL;DR at the top of this post, you'll get both sets of fixes in one go.
nativephp/php-bin is now deprecated#
For Desktop users, when upgrading to nativephp/[email protected], you may wish to remove the nativephp/php-bin dependency. It is no longer required (composer remove nativephp/php-bin).
This package was becoming increasingly unwieldy and causing a number of issues for users on slower connections. Desktop 2.4 no longer requires it by default, making installations noticeably quicker as you are no longer forced to download binaries that you may not need.
Wrapping up#
Seven advisories in one go is not how I imagined our first time doing this would look. But I'd much rather these were found, fixed and documented than sitting quietly in your apps. Every one of them made NativePHP better.
We take security very seriously, especially as we're distributing an upstream runtime that can is shipped with each application to hundreds of thousands of users.
If you have any questions, or you're not sure whether your app is affected, get in touch at [email protected] and we'll be happy to help.
Please update, rebuild and ship. And if you find something, please report it to us using the procedures we have outlined.
And if you can possibly support our work in doing all of this, we would greatly appreciate it!
Timeline#
Tuesday, Oct 20#
- Evening UTC: we became aware of the PHP announcement for Wednesday's patch.
- We became of the overnight vulnerability leak ahead of the coordinated patch release and began preliminary investigations using the unverified vulnerability details to determine impact to NativePHP.
Wednesday, Oct 21#
- We awaited the official disclosures from PHP.net to assess the true impact.
- Official disclosure arrived around 2pm UTC and we immediately jumped into assessmnet and mitigation. We determined the risk to be
Lowto NativePHP users due to the nature of the attack vectors. - We patched the latest versions of our binaries (4.1.0, 4.2.0-rc) as soon as the patches were available from
php/php-src.
- Official disclosure arrived around 2pm UTC and we immediately jumped into assessmnet and mitigation. We determined the risk to be
- We made public announcements of our findings via our social channels, for example on X:
Thursday, Oct 22#
- We continued to closely monitor the situation to watch for follow-up reports.
- We took time to further harden our binary distribution supply chain (file hashes and manifest signature).
Saturday, Oct 23#
- Evening UTC: We became aware of another set of publicly-disclosed vulnerabilities, directly mentioning NativePHP. Acknowledged.
- We began investigation to verify claims immediately.
Sunday Oct 24#
- Verified vulnerability claims and moved into immediate patching.
- Continued binary patching and supply chain reinforcement for older binary versions still in use (our binary versions 3.1 and 4.0)
- Belatedly became aware of GHSA-27jx-7m9x-fx5c and began patching work for this also.
- Published patches for all known vulnerabilities and created security advisories.