Security

    Over-the-air updates in React Native: security and store rules

    A release pipeline diagram showing a signed JavaScript bundle travelling from an update server to phones that verify the signature before loading it.

    Over-the-air updates let a React Native or Expo app ship a new JavaScript bundle to users without going through app store review. For a small team that is the difference between fixing a bug in an hour and waiting a day or more, and it is why most React Native apps use some form of it. It also means the code your users run can change after review, delivered by infrastructure you control, and that raises three security questions: can anyone else push code to your users, does the update stay inside what the stores allow, and what happens when an update breaks.

    Short answer

    Over-the-air updates are allowed for interpreted JavaScript as long as they do not change the app's primary purpose or add features that would need review, and they become a security risk when the update channel is not authenticated end to end. Use a maintained update service, turn on code signing so the app verifies every update with a public key it ships with, keep the signing key out of CI logs and shared machines, restrict who can publish to production channels, and keep a fast rollback. Microsoft's CodePush service has been retired, so apps still pointing at it need a replacement rather than a patch.

    How over-the-air updates work

    A React Native app has two layers: native code compiled into the binary that the store reviews, and a JavaScript bundle that the native layer loads and runs. An update service hosts newer bundles. At launch or in the background, the app asks the service whether a newer bundle exists for its runtime version, downloads it, and loads it on the next start.

    EAS Update is the most widely used service for Expo and React Native apps today. Microsoft's CodePush was the other common choice for years; its hosting was part of Visual Studio App Center, which Microsoft retired after March 31, 2025, and the react-native-code-push repository was archived in May 2025. Self-hosted servers that implement the same protocols exist too.

    The security model in all of them comes down to one thing: the app runs whatever bundle the update channel delivers.

    What the store rules allow

    Apple's position is set out in two places. App Review Guideline 2.5.2 says apps may not download, install or execute code which introduces or changes features or functionality of the app. Apple's developer agreement then makes an exception for interpreted code, such as JavaScript run by the platform's engine, provided it does not change the primary purpose of the app or add features inconsistent with what was reviewed.

    In practice that means bug fixes, copy changes, small UI adjustments and tuning are routinely shipped over the air, while a new feature set, a new business model or anything a reviewer would want to see should go through review. Google Play's policies take a similar view of apps that change their behaviour after installation in ways that would have affected review. Using updates to slip a feature past review is a policy risk to the whole developer account, not just to one release.

    The threat model in plain terms

    RiskHow it happensImpactControl
    Unauthorised publishLeaked token or compromised CI pushes a malicious bundleArbitrary JavaScript on every user's deviceCode signing, scoped tokens, protected channels
    Tampered deliveryBundle altered between server and deviceSame as aboveCode signing verified on device
    Policy breachUpdate adds reviewable featuresStore removal, account actionRelease rules for what ships over the air
    Broken updateBad bundle crashes on launchUsers locked out until fixedStaged rollout, automatic rollback
    Runtime mismatchBundle expects native code the binary lacksCrashes or silent failuresRuntime versioning

    The first two rows are the security core. Every user's app trusts the update channel completely, so the update channel is effectively a production deploy pipeline to every device you have.

    Code signing is the control that matters

    Transport encryption protects the download from casual interception, and it does not prove who produced the bundle. If an attacker obtains your publishing credentials, or compromises the hosting, a TLS-protected download faithfully delivers their code.

    End-to-end code signing closes that gap. The app ships with a public key inside the reviewed binary. Every update is signed with the matching private key before publishing, and the app refuses to load any bundle whose signature does not verify. Expo documents this as end-to-end code signing for EAS Update, including how key rotation works. With signing on, a compromised hosting account or intercepted download cannot push code that runs, because the attacker does not hold the private key.

    This maps directly to CWE-494, download of code without integrity check, which is the weakness an unsigned update channel exhibits.

    Protecting the signing key

    Signing moves the trust to the private key, so treat it like a release signing key:

    • Keep it out of the repository and out of CI logs.
    • Store it in a secrets manager or hardware-backed store, and limit who can use it.
    • Sign in a dedicated release job rather than on developer laptops.
    • Plan rotation in advance, since apps in the field only trust keys their binary knows about.

    Access control on publishing

    Even with signing, the people and systems that can publish to production deserve the same care as production server access. Use separate channels or branches for development, preview and production. Restrict production publishing to a release job, with tokens scoped to that purpose. Require review on the commits that feed a production update, as you would for a server deploy. Keep an audit trail of who published what and when.

    A common weak point is a long-lived personal access token used in CI, created by a developer who has since left. Rotate tokens, prefer short-lived credentials where the service supports them, and review access when people change roles.

    Rollback and staged rollout

    An update that crashes on launch is a security problem as much as a reliability one, because users cannot reach the app to receive the fix if the fix is also delivered by that app. Two practices limit the damage. Roll updates out to a small percentage first and watch crash rates before widening. Use the update service's rollback so the app reverts to the last good bundle, or to the embedded bundle, when a new one fails to start.

    Runtime versioning belongs here too. An update built for native modules the installed binary does not contain will fail in confusing ways. Tie every update to a runtime version that matches the binary, and ship native changes only through the stores.

    Migrating off CodePush

    Apps still configured for CodePush are pointing at a retired service. The practical path is to move to a maintained update service or a self-hosted server that is actively maintained, then ship a store release with the new client and keys before relying on it. Plan for a period where some users still run the old binary and receive no updates at all; a minimum version check that prompts those users to update from the store closes that gap. Use the migration as the moment to turn on code signing, since adding it later means another store release.

    Checking your setup

    Review the update configuration in the built app, not just the source: which update server it contacts, whether code signing is configured, which channel the production build follows, and whether a debug or development channel is accidentally enabled in a release build. An automated pass over the artifact, such as PTKD.com, flags build-level issues like debuggable flags, verbose logging and embedded secrets, which often travel together with update misconfiguration; the publishing permissions and key handling are an account and pipeline review you do directly. The broader OWASP MASVS code quality requirements set the expectation that apps only run code from trusted, verified sources.

    Questions to answer for your app

    1. Which service delivers updates, and is it maintained?
    2. Is code signing on, and where is the private key?
    3. Who and what can publish to the production channel?
    4. What stops an update that crashes on launch?
    5. What is your rule for what may ship over the air and what must go through review?

    What an incident looks like

    If a publishing credential leaks, the first hour matters. Revoke the token, check the update history for any release you did not make, and if one exists, publish a known-good bundle immediately so devices replace the bad one on their next check. With code signing on, an attacker holding only a publishing token cannot get unsigned code to run, which turns a potential mass compromise into a credential rotation. Without signing, every device that fetched the malicious update ran it, and the only clean recovery may be a store release.

    Write the steps down before you need them: who can revoke tokens, how to identify the last good update, how to publish it, and how to tell users if anything ran. Treat it the same way you would a compromised server deploy key.

    Release rules that keep updates inside the guidelines

    A short written policy prevents most store problems. A typical version allows over-the-air updates for bug fixes, text and translation changes, styling, and configuration within existing features, and requires a store release for any new screen or feature a reviewer has not seen, any change to payments, subscriptions or data collection, any new permission, and any change to native code. Review each over-the-air release against that list before publishing, and record the decision with the release.

    Teams that follow a rule like this rarely have trouble at review, because the reviewed binary always reflects what the app actually does.

    Monitoring updates after release

    Treat each over-the-air release like a deploy that needs watching. Track adoption so you know how many devices run each update, compare crash and error rates between the new update and the previous one, and set an alert threshold that triggers rollback automatically or pages someone. Keep the release notes for every update, even small ones, because when a support ticket arrives a week later the first question is which bundle the user was running.

    It is also worth checking that devices actually verify signatures in practice. Publish a deliberately unsigned test update to a development channel on a test device configured for that channel, and confirm the app refuses it. A misconfigured client that silently accepts unsigned updates looks identical to a correct one until someone tests it.

    Updates and privacy declarations

    An over-the-air update can change what data the app collects without touching native code, for example by sending a new analytics event or a new field to the backend. Store privacy declarations describe the app's actual behaviour, so a JavaScript-only change that adds collection still needs the App Store privacy details and the Google Play data safety form updated. Include that question in the release rules, next to the question of whether the change needs review.

    Data safety and privacy details are a frequent cause of review questions, and keeping them accurate is far easier when every release, store or over-the-air, passes through the same short check.

    What to take away

    Over-the-air updates are allowed for interpreted JavaScript that does not change what the app is, and they turn the update channel into a direct path to every user's device. Authenticate that path end to end with code signing, protect the signing key like a release key, restrict publishing to a reviewed release job, and keep staged rollout and rollback ready. If the app still depends on CodePush, replace it with a maintained service and turn on signing as part of the move. Keep feature changes that a reviewer would want to see in store releases.

    • #react native
    • #expo
    • #eas update
    • #codepush
    • #code signing
    • #app review

    Frequently asked questions

    Are over-the-air updates allowed on the App Store?
    For interpreted code such as a React Native JavaScript bundle, yes, provided the update does not change the app's primary purpose or add features that should have gone through review. Apple's guideline 2.5.2 forbids downloading code that changes functionality, with an exception in the developer agreement for interpreted code within those limits. Bug fixes and small changes are routine.
    What is the main security risk of over-the-air updates?
    That someone other than you can deliver code to your users. A leaked publishing token, a compromised CI system or a tampered download could push a malicious bundle that every installed app runs. End-to-end code signing, where the app verifies each update against a public key in the reviewed binary, prevents unsigned code from running.
    Is CodePush still available?
    Microsoft retired Visual Studio App Center, which hosted CodePush, after March 31, 2025, and archived the react-native-code-push repository in May 2025. Apps still configured for it need to move to a maintained update service or a maintained self-hosted server, ship a store release with the new client, and preferably enable code signing at the same time.
    Does HTTPS make over-the-air updates safe?
    It protects the download in transit, which is necessary but not sufficient. HTTPS does not prove who produced the bundle, so a compromised publishing account or hosting server would deliver malicious code over a perfectly valid connection. Code signing verified on the device is what ties each update to your private key.
    When should a change not ship over the air?
    When it changes native code, adds a feature a reviewer would want to see, alters payments or data collection, or changes what the app is for. Those belong in a store release. Over-the-air updates suit bug fixes, copy, small UI changes and configuration within the reviewed feature set.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free