Hidden iOS app build seems available publicly to non-authorized (non-TF) users...

  • We are testing our Expo app on iOS through TestFlight with a handful of beta testers known to us.
  • The app was built with EAS.
  • Our app is only available in the US (per AppStoreConnect)
  • This Expo project is marked hidden.
  • We’ve uploaded 4 builds to TestFlight and recently expired the oldest two, leaving two in AppStoreConnect.
  • But our Expo project still lists those 4 builds plus a few more builds as well, including a few dev builds.

The strange things:

  • We are seeing metrics that indicate someone outside the US is running it.
  • They are running old builds, jumping back and forth between builds. E.g. they started and incremented A, B, C, D over the last month though A wasn’t even a public build nor in TestFlight!
  • Then they went back and did A, B, A, C and stayed on C for now.
  • But C and D are the only builds still on TestFlight!

Ergo, they’re picking these builds up from EAS/Expo hosting somehow.

How are they doing that and what can we do to stop it?

Thanks in advance!

If you are using EAS Update or expo publish (or expo build) URL for the published js bundle can be extracted by anyone with access to apk/ipa.

If you are building on eas and do not use any update services, then nothing from your app is hosted on expo with the exception of artifact created by the build. Even if someone had access to your ipa file they still would not be able to install it on iOS device, for release builds that ipa is only intended to be uploaded to appstoreconnect and internal distribution builds can only be sideloaded to devices registered in your apple account.

If you are using EAS Update or expo publish (or expo build ) URL for the published js bundle can be extracted by anyone with access to apk/ipa.

Yes, we are using expo publish for OTA updates. But access to the ipa should only be through TestFlight - we haven’t shared it any other way.

It’s as if the list of builds in the project is readable from outside and the ipa files downloadable as well.

expo publish url is very predictable (it’s printed when you run expo publish)

e.g.
https://exp.host/@username/app_name/index.exp?sdkVersion=44.0.0

It looks like from that url one can download the entire JS code pack.

  1. and this seems unaffected by whether the project is marked “Hidden” or not. Is that true?

  2. it doesn’t seem good that the app’s code is available publicly. What are the options if we want to use OTA updates (whether expo publish or EAS Update).

  3. And still, how is it that the JS bundle is useful without the ipa? I looked in our metrics and they’re not running it in an Expo client (its build and version numbers are ours, not Expo client’s).

  1. hidden only changes that apps are not visible when someone opens your profile

  2. this is client-side feature, everyone determined enough could easily access that URL when they have access to ipa or just install it from store on jailbroken device. If we would add some random string to that URL it would not affect security in almost all cases. The only exception is if you are only distributing it to trusted users, but this is a very narrow use case so we decided cleaner url have more benefits.

What are the options if we want to use OTA updates

No OTA update mechanism in any client-side software can be secure(unless it’s secured on the hardware level), you can obscure it but someone determined enough will always will be able to get access to that from ipa/apk. If you just want to have it under some random url so it’s not guessable, both expo publish and EAS Update can be self-hosted.

  1. That depends on how you are getting those metrics, expo publish angle I suggested assumes that metrics are from js. Also, note that published bundle is build in production mode, so you might be detecting incorrectly if code is executed in dev client or Expo Go.
1 Like