497 comments

[ 3.3 ms ] story [ 400 ms ] thread
The solution is fairly simple: just stop releasing software on that platform. There are millions of customers on more open platforms, so there's really no need to support them anyway.
All of life's problems are simple when suicide is your backup plan.
I am definitely stealing that
Boycott is a valid response no matter how much you try to make false analogies. Pulling app out of app store is by no means equivalent to suicide. At most you change your business.
Right, like Uber, Snapchat, Facebook, Clash of Clans, Pinterest, Whatsapp, Instagram, Twitter, Waze, Shazam, Tinder, Match, YouTube, and basically every other app out there pulling out of the App Store would not be suicide.

"At most", those companies would just have to "change their businesses".

If all those companies did pull out, then it would be the end of Apple.
Absolutely not. Apple was fine long before these companies came into existence, and Apple will long outlast these companies. New companies would come in to fill the spaces these companies will have left in days.
As a user/consumer, I like this. It reduces potentially unpleasant "surprises."

Apple have curbed a lot of obnoxious developer practices (and enforced good ones, like the move to 64-bit not long ago) and they, along with Microsoft, probably the only ones with enough muscle to be able to do that.

tldr - appears to affect rollout.io customers (at least for those who have replied to the thread so far).
I wonder if this is going to hit non native code push solutions like React Native? Or if Apple are going to start cracking down on apps like Facebook, Twitter or Pinterest that do a lot of A/B testing.
This was my immediate question too. Microsoft offers a service called CodePush (https://microsoft.github.io/code-push/) for React Native and Cordova apps that presumedly could get caught by this. I don't have enough mobile dev knowledge to know whether or not it uses the same APIs that were mentioned in Apple's rejection letter, though.
Yep. I use code push in several apps. It's gonna be fun times next time I need to submit an app. I think it might be safe because it doesn't push native code and rollout is all about pushing native changes.
PM on the CodePush team here. The rejection notice seems to explicitly call out the native methods that are a cause of the issue. CodePush cannot inject private frameworks or expose any methods that React Native already exposes.

I would also recommend not using CodePush to completely what an app does.

I accidentally what the app does.
Can you get confirmation that CodePush won't be impacted?
Correct me if I am totally wrong here, but isn't the issue not with introducing new private frameworks or exposing new methods but with changing the behavior of the interpreted code that interacts with already exposed frameworks/methods? The relevant language seems to suggest that you could still be in violation of Apple's TOS if your script(s):

>change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.

I believe the guidelines allow Js functionality like this as long as the functionality does not change. This seems like they are doing this for security reasons so I'd think React Native is different. Not sure though, someone else might know more.
I think in these instances the code is pushed in a build, and then toggled server-side. Enabling features server-side isn't against the ToS, but pushing new app code is.
To date, Apple's Developer Program Guidelines states (in Section 3.3.2):

> Except as set forth in the next paragraph, an Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exceptions to the foregoing are scripts and code downloaded and run by Apple's built-in WebKit framework or JavascriptCore, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.

Personally, I think that Cordova hybrid apps will continue to be okay, but I don't know about something like React Native...

RN runs with JavaScriptCore.
rollout as well...
That actually makes it sound like it's OK to hot-deploy arbitrary new JS code to cordova/ionic apps like bug fixes and new features as long as you don't pull a bait and switch and turn your todo list into a camera or something.
Code push doesn't push Native code. I think Rollout uses swizzling to send native code over the air to your app and then uses JavaScriptCore to inject it into your app at runtime. This always seemed pretty sketchy to me and I could see why Apple would be annoyed by it (it allows you to push changes which can call private objective c apis).

React Native code push does not push any native code, just JavaScript. Out of the box it does not allow you to push code that can call private API calls at runtime.

Correct me if I'm wrong but you can use React Native without any code push features, can't you?
Sure but a lot of React Native people use code push. I assume React Native is not affected as code push is just pushing JavaScript and not using JavaScript core to swizzle native code into your application like rollout does.
CodePush PM here - note that CodePush cannot push any native code to the app.
Can it call arbitrary native code? I think that's our probably going to be an issue if it can (I.e. If it can do loadFramework("baz").getClass("foo").callMethod("bar"))
Hasn't this always been against the App Store terms? I thought the only language you were allowed to download code from the internet and run was Javascript on Apple's VM.
Yes, that's correct. IOS Developer Program License Agreement, section 3.3.2:

"3.3.2 An Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exception to the foregoing is scripts and code downloaded and run by Apple's builtin WebKit framework..."

... or JavascriptCore, which is what React Native uses.
..."provided that such scripts and code do not change the primary purpose of the Application"
(comment deleted)
So you can add features all you want, just make sure you don't change your todo list app to a dating app and all is fine.
Once you've 'worked' with Apple's app review for long enough you'll learn that you can't rely on such an assumption.
The wording of the prohibition has varied over the years. Current wording is:

Except as set forth in the next paragraph, an Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exceptions to the foregoing are scripts and code downloaded and run by Apple's built-in WebKit framework or JavascriptCore, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.

So even if you download JavaScript code and run it on Apple's VM, they reserve the right to reject it if it changes the primary purpose of the application.

>The only exceptions to the foregoing are scripts and code downloaded and run by Apple's built-in WebKit framework or JavascriptCore, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.

It looks like this says "If you use download code that is run on JavaScript core (ie, JavaScript), then you can do this so long as you don't change the purpose of your app when you submitted it to the App Store."

I think that wording with the "exception" is from the OSX developer program. As far as I can remember, the iOS info sheet has always been taxative with no exceptions about executable code. Of course all wordings allow Apple to start rejecting a previous approved app if they feel the code/scripts the app is now downloading are in violation.

The explanation in the rollout.io site about why they are fine is intentionally deceptive. They have the guts to link to a document that says "An Application may not download or install executable code." and then quote more friendly excerpts in the hopes that you won't read the actual doc. I can't imagine why Apple has let this go on for so long.

Rollout.io has been offering a product that leverages this to 'hotpatch' binaries, but it looks like this is now considered not in the spirit of the guidelines. Technicals here: https://rollout.io/blog/under-the-hood-2016-update/

It basically goes:

* add their SDK, which has the ability to swizzle(swap out the implementation for) arbitrary methods in your app

* the swapped in implementations use JavascriptCore to execute javascript you supply, wrapping or replacing the 'real' invocation of the method.

* their SDK checks on startup which methods to replace and downloads the appropriate JS replacements

This is, technically speaking, only using JavascriptCore.

That's actually quite a clever workaround to the current rules, but rather naive of them to think Apple wouldn't fight back at some point.
It's really sad, because that kind of stuff lets you fix bugs / mitigate outages in the wild without having to wait on apple's schedule.
This is true. The problem is bad actors can use this to bypass Apple's review. As an iOS app publisher I slightly regret this inconvenience. As an iPhone user, I appreciate Apple looking out for my security.
Apple's review isn't that useful in this case as a pre-check, it is possible to avoid it if you want. Apple review does automated code checks & a reviewer manually using your app. With that review process you can deliver executable code after the fact in any way you want and only get caught after the fact if it's even noticeable. You can even get sneaky and add some security exploit to make it look like a mistake.

It's much like the argument 'if you ban guns only criminals will have guns' and it's quite true in this case.

Given that Apple's automated review tools detect many ways in which executable code can be injected into apps, and OP's link is itself about that very thing - what you say is mostly false.
It was easy to detect because they are not to trying to hide it. They just have to check if the library exists.
it's not about apple's process being imperfect. sure, you can fool them if you try hard enough and a bad actor.

it's about damage mitigation, and shutting down a 3rd party "app-hot-fix" service is a good move.

it's harder to fool only apple, than to submit some naive looking thing and still have unmitigated access to changing its code.

(comment deleted)
Yes, but it has not been widely enforced.
I wonder if Apple will apply this rule to everyone, which would be fair, or if they plan on letting big name developers like Facebook or Google continue to violate the rules without consequence.
They should pull the Facebook app for this. But I'll eat my hat if they do.
Their guidelines specifically say that, with prior authorization, the prohibitions do not apply.
unfortunately, just like any other private platform, they don't need to be consistent or follow the rules all the time.

I say, don't go native unless you must (for performance reasons etc). push the web forward instead!

Are there any sites that document Facebook or Google using hot code push on iOS?
I'm not sure if they still do this, but Facebook used to ship both code paths in the app binary for new launches, and give Apple instructions on how to test both code paths (e.g. sign in with this special user/pass combo).

So they weren't changing app functionality after App Review approval; it's just that for users some of that functionality was gated on a boolean that was fetched over HTTPS.

Putting code behind a feature flag seems entirely fair and a good idea for developers of any size.

It's also a thing that can easily affect small developers; if your app requires logging into some existing paid account (enterprise software, a bank's app, etc.), the available features depend on what features the account has paid for. So as part of the review, you send Apple credentials for a test account that has all the features enabled. (Without a test account, they couldn't log in at all.)

There's a cynical part of me that thinks this is because Apple is going to announce a similar feature at WWDC
I think that's likely. Why is it a cynical part of you? Either Apple is proactive and aggressive about keeping their platform free of hackishness as a matter of routine, or their store ends up a pile of malware and crashy junk.
That doesn't seem likely. They 'just' reduced app review down to ~24 hours.
Seems like people have been aware of concerns about violating the TOS with these hot patch frameworks.

From April 2016

>>Rollout is aware of the concerns within the community that patching apps outside of the App Store could be a violation of Apple’s review guidelines and practices. Rollout notes both on their FAQ site and in a longer blog post that their process is in compliance.

https://www.fireeye.com/blog/threat-research/2016/04/rollout...

A ton of games do this and it is incredibly annoying. I don't want to download an update, then have to download an update. I only wish the same restriction applied to my Android device.
Google definitely forbids self-updating apps on Google Play. But I'm not sure how well this is enforced.
Horribly. I get a few games from the Japanese market, and almost each one requires in immediate internal download and update.

Although those updates never trigger the Android update service, so I'm not sure if they are just downloading more resources of if they are able to request new permissions(I would like to assume not.)

Games updating DLC is nothing new and is not what this is about.

Google did recently change the Android permission model; previously, apps had to request all their permissions at install time and it was all-or-nothing (and frankly, hardly anyone bothered to look them over.)

Now, certain permissions have to be requested when they're needed (at least for recent versions of the SDK) and the user can choose to allow or deny. But an app can't grant itself new permissions without going through the official update process.

See I do trust that - but only to an extent.

I always wonder when seeing one update, if there is a 0 day that can bypass that. On a technical level I know I run the same risk with my PC, but at the same time, it's more difficult for me to examine processes and startups in my android.

Realistically, if they've written their own native code that parses their updates then almost certainly. If they're using an established library then maybe not (likewise if they're using a decent language, but unfortunately no-one does that). I'm reminded of the example at the bottom of http://www.gamasutra.com/view/feature/194772/dirty_game_deve... where the game had a buffer overflow in displaying its own EULA.
The internal download and update is allowed when it consists of media resources and such which is not native executed code.
Most likely most games are updating only game related data and graphics files. Very few games actually use internal scripting that would be needed to do code updates
The only app I've got that appears to actually update itself without going through the AppStore is the HSBC mobile banking app. I'd be interested in hearing the discussions going on between Apple and HSBC at the moment.
Judging by how sluggish and annoying the HSBC app is, I think it is a web app framed in a thin launcher from the app store.

I.e. it downloads a bunch of javascript/html/css and that executes within a UIWebView/WKWebView. Using caching and localStorage, you can construct such an app to not need to download everything on each launch.

The reason that's allowed is because everything executes within a sandboxed browser environment. No native code is downloaded.

Is it possible to do that and use the fingerprint sensor for login in a secure way? I thought the same as you until they enabled Touch login.
There's no reason why they can't have the native portion do the authentication and just have it return a signed token to the JS portion which forwards it to the server.
> The reason that's allowed is because everything executes within a sandboxed browser environment. No native code is downloaded.

That's the same thing Rollout does. In fact, iOS apps can't download and run native code. The OS won't let you mark pages as executable unless they're appropriately signed, and only Apple has those keys.

(comment deleted)
If they're using JSC, then they can definitely download JS and execute native methods. They could subclass any UIKit object and make it conform to JSExport. Done, now they're "running native code."
Sure, and an app displaying a web page using WebView can provide hooks that allow doing that sort of thing too. Neither one is downloading native code.
Only UIWebView, which is going away soon.

But surely you can see the difference between executing limited actions inside a web view, and making available any native method to a web view.

WKWebView allows the app to execute arbitrary JS within the loaded page, and intercept URL loads and other actions made by JS code. That's all you need to build a bridge.

I don't see any fundamental difference here. Both (UI|WK)WebView and JSC allow bridging. Neither one grants full access to JS code automatically, the programmer has to put some effort into it. And even if there is some important difference, neither one is native code which is what I was disputing above.

I was thinking of the HSBC app the whole time while reading this. In my opinion Apple should reject that piece of shit and force HSBC to write a native app that works properly.
Supercell's games and a bunch of F2P collect-ish games also do that, when you open the game they have an update process. I'm reasonably sure that only updates static assets though, stuff like description files and graphic assets. It's actually pretty useful as it lowers the payload of the core engine and lets them do much smaller updates compared to having to bundle it all, that's especially important with things like Unity Frameworks which are not delta-updatable in the store (hence Hearthstone's 2GB downloads every time they add a cardback or nerf a pair of cards)
I bet it's some code too.

Worked on a F2P mobile game, we bundled a tiny Lua engine, and them pushed various promotion screens as a bundle of resources (images) and lua code (screen layout, its preconditions and what's gonna happen after you click - game provided a small API that Lua called).

Hey, unrelated, came across a comment of yours from ~5 years ago: https://news.ycombinator.com/item?id=2949645

Would you mind elaborating on why Erlang programs tend to have FSMs? LYSE has a chapter on how to use gen_fsm, but I've really been unable to find a great answer as to WHY you would want to use it.

Neither Google nor Apple nor any other game platform is going to stop games from downloading new content. That's just how games work these days.

The reason this type of "hot code push" is more attractive on iOS is because the app review process is much longer, so publishers look for ways to skirt it. Looks like Apple is just starting to enforce it more.

From https://rollout.io/how-it-works/ :

Does Rollout comply to Apple’s Guidelines?

    Yes. As per Apple’s official guidelines, Rollout.io does NOT alter binaries. ... With over 50 million devices already running our SDK, it is safe to say that Rollout complies with with Apple’s development and App Store guidelines.

Ouch. Just like the company's future is in danger.
The first time I saw Rollout I was shocked it wasn't already banned by the App Store. No matter what they say, I can't imagine how they could do what they claim to do without flagrantly violating the guidelines.
I'm guessing there are two things Apple is worried about. The first is using hot code push to change the purpose of the app after release, e.g., switching a business app into a video game. The second is using hot code push to violate app store review guidelines, like the use of private APIs.

You can do hot "code" push techniques that allow the first but not the second, by letting apps update HTML and JS that calls back into pre-existing native code. That's what Cordova / PhoneGap does. I'd guess that Apple will just ban the app and the developer if they catch it.

It appears that Rollout started using some API that would enable it to do the second, and Apple is preemptively making sure that it doesn't happen. The wording of the rejection is based on passing computed parameters to introspection routines.

> The second is using hot code push to violate app store review guidelines, like the use of private APIs.

I've never understood this part. Why doesn't iOS simply prevent apps from calling private APIs?

Presumably because many private APIs are used behind the scenes by public APIs and the security model must allow applications to run them.
Because some of these APIs are useful in an enterprise app setting that aren't distributed via the App Store. Like Disney applications on their turnstile devices at Disney World.
You aren't supposed to call private APIs in your code, but your app is definitely making private API calls all the time since the libraries provided by the platform are running in-process.
Preventing it in a technical way is far from easy: If your app calls public API X, which as part of its implementation calls private API Y, your compiler only needs a declaration of Y to output the function call / ObjC message send. Nothing in the language prevents it, and the code is executed natively unlike Java.
They control the software and the hardware though: seems possible to allow a specific region of memory (aka their public API) to call a specific region of memory (private API) and segfault for all the rest that does that?
Many private APIs are methods on objects which are part of public APIs, so there's no "region" of memory which cleanly corresponds to private APIs.
But they can theoretically create that as they own the entire chain; dev env, tools, OS, software, hardware (CPU included). I know it is not currently the case, sure, but they can do it was my point.
They "own" a compiler, but not all of them.

Say they implement the scheme you mention in clang and the LLVM linker, so the function bodies of their public APIs end up placed in that privileged region of memory, and those of their private APIs end up in the restricted region.

Nothing prevents gcc from producing object files that tell the linker "this user function is part of Apple's public APIs". And nothing prevents people from using a different linker anyway, one that would put private API body functions out of the restricted region of memory.

The only real way to achieve that would be to move all their frameworks to the kernel, which would be all sorts of problematic.

> Say they implement the scheme you mention in clang and the LLVM linker, so the function bodies of their public APIs end up placed in that privileged region of memory, and those of their private APIs end up in the restricted region.

Agreed that this design is fundamentally flawed, but that's because the coder is providing the implementations of private code. Providing that is Apple's job.

Put privileged code into a dynamically-linked library that Apple provides. Only code in that block of memory can call private APIs. Pretty straightforward to implement, and requires nothing fancy from the kernel.

Of course this only works if you can prevent the attacker from corrupting memory.

I don't know if iOS does randomization of loading addresses, but if so, that'd be a disadvantage.

And well, in any case they need to maintain compatibility with current apps for who knows how many years.

> I don't know if iOS does randomization of loading addresses, but if so, that'd be a disadvantage.

Such a scheme wouldn't stop ASLR. The loader just needs to tell the verification code where it put the privileged libraries.

> And well, in any case they need to maintain compatibility with current apps for who knows how many years.

Do they? I think Apple could easily order everyone to switch over to a more secure compiler with a one year deadline.

At best all they could do is change literally every single private function call everywhere to inspect the return address and see if that return address is in a system framework or is in the app image. But this would be huge overhead, a real pain in the ass, and also not even reliable, because all you have to do is pick a public function, figure out the address of the `ret` instruction, push your $pc onto the stack, and then call the private function passing the address of that `ret` instruction as the return address. The private function will see that this address is in a system framework, and so will work, and then it will return, passing control to that `ret` instruction which immediately returns back to your real caller function.

So no, there's no way for Apple to technologically make it impossible to call private functions. The only actual solution there would be to completely rewrite the OS such that literally every call into a framework that an app makes actually goes over IPC (so that way apps can't even attempt to invoke private functions since they won't be linked into the app), but that would probably be crazy slow which is why nobody does that.

> I'm guessing there are two things Apple is worried about.

I'm sure it worries about them but there's a much larger, riskier scenario.

Once you start downloading and executing binary code from untrusted sources (i.e., not the App Store) anything can go wrong.

1. An iOS app doesn't care about security, and it hot-loads code from some non-https source and gets man-in-the-middle'd 2. An iOS app hot-loads code in a secure manner, but the server from which the code is served becomes cot mpromised 3. A malicious employee at an iOS app vendor pushes harmful code out via her company's app

Now, I'm not a fan of Apple's policies. I think there should be a "guys, I know what I'm doing" mode where I'm allowed to download code from untrusted sources. Just like Android or MacOS.

However, I sympathize with them here. For nearly a decade people have been downloading code from the App Store with the understanding that it is safe to do so. Even I appreciate this much of the time... I'm an engineer but I'm busy. I can't audit every app I download. I wish there were other options, but I find huge value in the fact that I don't have to worry about an App Store app screwing my device. And it's a big reason why I recommend iOS despite its flaws to older family members.

> Now, I'm not a fan of Apple's policies. I think there should be a "guys, I know what I'm doing" mode where I'm allowed to download code from untrusted sources.

This exists today and has for a long time, it just costs you money for this "privilege".

You can drop a .ipa file into iTunes and load/run the app on a phone that's syncing to that copy of iTunes...
Getting the .ipa signed still costs money (or has a week-long timebomb), and you can't run unsigned code.
> Rollout lets you push code-level changes to native iOS apps, without waiting on the App Store.

What did they expect when their entire business model is based on something that's literally the opposite of what the review guideline allows?

Uber is still doing kind of ok.
Uber isn't going up against one company though. It is going up against 1000s of governments.
What does Uber do?
They run an illegal taxi service. There's an app for it too.
It's legal in many cities.
It's become legal (after the fact) in several cities, and Uber has allegedly intentionally attempted to subvert attempts by law enforcement to investigate their operations.
Some cities it was legal to begin with, btw.
Only following the law in "some cities" is not a good thing.
"Illegal" doesn't mean "I have a beef with it".

Uber is legal in the vast majority of cities.

"Mostly legal now" doesn't change the fact that it was illegal in the past and is still illegal in cities that are not in the "vast majority".

Not to mention that they (allegedly) attempted to subvert law enforcement attempts to investigate them, by "greyballing" law enforcement. Whether that is considered obstruction of justice is a secondary question to whether it is incredibly shady.

I know. I'm using "illegal" to mean "against the law".
Half of Uber activity is illegal (UberX in states, it was called Uber Pop in France but it got banned). The other half is perfectly legal and convenient.

However, due to their miserable practices I am glad that almost all (if not all) drivers for Uber also drive for the local taxi-in-an-app service.

Uber may be good at skirting local laws, but as a company it's a ticking bomb.
Uber is not a ticking time bomb if it is able to convince a sizable portion of the regulators of the markets it operates in to continue to operate for the foreseeable future
Which is disingenuous to say because the guidelines forbid using these methods to make "significant changes to the app" - it doesn't mention modifying the binary.
Heh, "We're in so many apps... it MUST be allowed!"
To be honest, I think the fact that they're used by so many big players is the reason that Apple is requiring removal in future updates rather than outright removing for ToS violations.
I would be cashing out now...!
It won't be the first $10m+ company killed by Apple's App Store policy. I hope it will be among the last though.
So you're routing for ${FutureAbusiveSpamCompany}?
Rooting. As in 'root password acquired' ;)
Ha, I'm British and it shows.
Very little sympathy for anyone who didn't see this coming, I'm more just shocked they even got it past Apple in the first place.

Even if I had the idea and the technical skills to pull such a product off I wouldn't even bother trying for fear it wouldn't even get past the first hurdle.

Didn't Firebase just introduce Remote Config which touts this very thing?
AFAIK Remote Config is just a server side key value store with customisable values based on audiences (e.g. random 50% for A/B testing, all people from Country X). This means all behaviour changes will require code to be deployed in the first place.

Also, from Firebase Docs[1] "Don't attempt to circumvent the requirements of your app's target platform using Remote Config."

[1] https://firebase.google.com/docs/remote-config/

From this....

https://www.youtube.com/watch?v=_CXXVFPO6f0

"Firebase Remote Config allows you to change the look-and-feel of your app, gradually roll out features, run A/B tests, and deliver customized content to certain users, all from the cloud without needing to publish a new version of your app."

With hot code pushing, you're deploying newly written code/functionality, post release (opening possibility of up MiTM attacks etc).

With Remote Config you deploy the code, then decide what to show to users post release. This gives Apple a chance to review all the code submitted to the App Store.

In the case of rolling out features an A/B tests, you'd make a release including a feature, but only enable it x% of your users using RC. You can then obviously enable it for everyone if it passes your A/B test or if you're happy its working.

(comment deleted)
I'm curious if this has anything to do with the exploits that are in the recent Wikileaks dump. Perhaps Apple saw something in there that raised alarm and an impetus to close the loophole?
This is just Apple's response to the Wikileaks Vault 7 drop this morning.
Should have been banned long ago, why would they have allowed an app to alter major behaviors post review?
We haven't noticed any issues with AppHub, our service for dynamically updating React Native JavaScript code.

We always felt this day would come, however, so we switched directions.

I've always questioned that 'hot code pushing' whenever I face an app that does it, like how on earth is it allowed to begin with ? they basically can send almost any functionality they want skipping Apple's reviewing all together !
I really kinda see no problem with Apple doing this. Hack the endpoint the app checks for new code, push malicious code. Or fool the app into checking for new code at your server, push malicious code.

I mean I read the headline, thought this sounded eminently sensible, then read the story and saw it was a framework for doing this, and my inner mental model of my security researcher girlfriend leaned forward, started rubbing her hands together, and wanted to start digging for those sweet new vulns.

Oh, Mario Run! What are you gonna do?
> any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior or call SPI

I would have expected dlopen and dlsym are blacklisted - you can trivially use dlsym to access any blacklisted API. Similarly, I thought SPI ("system programming interface" = private API) was all blacklisted. Am I misreading the message?

That said, if this is what they're focused on, it seems like it actively does not impact any apps that hot-push HTML code (e.g., PhoneGap / Cordova).

If the only reports are coming from Rollout.io, my guess is that the latest Rollout SDK uses one of these functions (I'd bet method_exchangeImplementations(), i.e., swizzling) with a dynamic parameter, and that the SDK can be changed to just stop doing that.

(comment deleted)
I wonder what they define as "code." Uber often rolls out "Kittens for a day" style features, some of which are topical and can't have been designed before the previous app update.
The rejection notice is very clear:

> This includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior or call SPI, based on the contents of the downloaded script.

Uber can write a "foo for a day" feature, that downloads some strings and some images from the Uber website, but doesn't actually add any functionality.

Same with any mobile game that has periodic events. You just write code to implement a generic event, write data to describe the event (name, graphics, level data), and do an update when you think of a different kind of event.

A topical "special day" feature could easily be defined as a downloadable content blob with zero executable or interpreted code. They probably have a set of special event templates already coded into the application which can then be themed with a few images, colours and text strings.
You can accomplish some crazy stuff with hot code loading: use private APIs, get around privacy restrictions. In theory, there are a lot of guys reasons for Apple to prohibit this.
You can accomplish all of that without loading new code. Apple's private API checks, for example, are easy to get around if you're motivated.
Does this mean all Cordova apps may be banned?
Not necessarily. From the Apple Developer Program License Agreement:

> 3.3.2 An Application may not download or install executable code. Interpreted code may only be used in an Application if all scripts, code and interpreters are packaged in the Application and not downloaded. The only exception to the foregoing is scripts and code downloaded and run by Apple's builtin WebKit framework, provided that such scripts and code do not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store.

(comment deleted)
What will happen to YouTube? YouTube has been pushing new functionality ahead of updates for me recently. For example, the new "double tap to rewind 10 seconds" feature appeared at random one day (without an any updates)...then eventually the feature is announced in an App Store update.

Or is the app review process more subjective?

They might have achieved that by pushing it in silently over time and utilizing feature flags. Only going live in an app store update once it was rolled out to everyone.
Google will absolutely have more wiggle room with rules than smaller developers.
Hmm, I definitely had to install an update for the YouTube app to receive the double tap to rewind/fast-forward.
The iPhone also has the ability to automatically update apps in the background if you allow it.
"Hi there -- I believe that title isn't quite accurate; Apple specifically is referring to behavior of a library called Rollout which lets people dynamically inject Objective-C/Swift. They are doing hot delivery of native, Objective-C code. It's really not about React Native nor Expo.

Expo (and the React Native library we use) doesn't do any of that. We also make sure we don't expose ways to dynamically execute native code such as the dlopen() function that Apple mentioned in that message. We also haven't received any messages from Apple about Expo, nor have we heard of any Expo developers receiving the same." - Exponent Team

(comment deleted)
Though the security justification here is limited to private APIs & native code pushing, the first few sentences of the rejection definitely seem like the "spirit" of the terms includes any significant functionality pushing at all. Wouldn't be surprised if they ramp up enforcement on that.
The problem with Apple that you and your customers need to be aware of (or concerned about), is that once a number of your customers sidestep Apple policies w.r.t. pushing or changing features via JavaScript, Apple will change their policies to close that loophole. It's only a matter of time before companies take advantage of this path to sidestep app store approvals.
As a client I'm pretty baffled that some developer had specifically bypassed something that I see as a security measure.

If an app is modified on the fly to use an undocumented and maybe "forbidden by apple" method in order to bypass security features or worse spy on me I'm clearly not ok.

Do you really think the apple ecosystem work because clients see AppStore as a evil cage and that the external developers are all angels with good intentions?

It's actually a double-edged security sword.

By not allowing developers to patch their app 'on the fly', directly, without going through a new version in the app store (which is a very lengthy process, almost forever in security terms), Apple effectively protects their iOS users from malicious code (not from the dev, which is probably to be trusted if the app is already installed, but from MITM attacks and the likes). However at the same time they deny very hasty security patches, which may compromise the device and all associated data and accounts entirely.

So there's no best world but undoubtingly many aspects to consider. Anecdotally I find that it's often better to trust the developers of an app to maintain their own thing; the OS only being a facilitator, provided the user has control (UAC on Windows, Permissions on mobile, etc.) [note: obviously you trust the OS vendor to patch said OS, it's just a particular kind of app]

edit: wording

I believe carefully crafted update should mostly mitigate that risk.

Also current policy don't prevent app developers to implement a "kill switch" that will prompt user to update (or wait for update) at splash screen and abort loading the malfunctioning version.

(comment deleted)
(comment deleted)
This appears to affect apps using JSPatch which is not pushing Objective-C but JavaScript.

Source: https://github.com/bang590/JSPatch/issues/746 (in Chinese)

The key part of JSPatch is that it exposes arbitrary, uncontrolled access to native APIs. You could use it to call private APIs even because Objective-C doesn't distinguish between public and private APIs at runtime, so Xcode's compiler checks and Apple's static analysis can't anticipate which APIs are possibly called.

In contrast, React Native doesn't expose uncontrolled access to native APIs. You write regular Objective-C methods that are callable from JavaScript, and within your Objective-C methods you write regular Objective-C that is statically checked by Xcode and Apple.

I'm not sure why people are concerned about React Native being targeted by this new enforcement of the rule. It is not the same thing at all.
it shares some components. like javascriptcore.

difference is the js - native bridge. for react native it is fixed bridge, that can only change with a change in the binary.

for rollout they can execute any native code with an update of the JS.

The main reason for React Native to not be impacted is that Facebook + Instagram + Airbnb + Soundcloud + ... are using it and Apple cannot justify to their userbase to not accept those favorite apps for a technical reason.
No, it's not. It's because React Native is a totally different technical solution.
"You write regular Objective-C methods that are callable from JavaScript," - the objC function that I am calling from JS can be capable of calling a private API received by it in an argument - that breaks the entire claim of reactNative not supporting calls to private API. React Native must also go down.
See the difference between a regular human who in theory can take a knife and violently rob or kill someone, and human-like robot that can be easily programmed to do the same. Who has to be isolated?

It is all about intents. If app allows that, it is banned for that behavior, not for having React in his kitchen. But if you have armed drone there, then police has a question.

If you deliberately write code that calls private APIs that are exposed to JS in your React Native app, I expect only your specific app to receive this warning. The same would be true in an app that doesn't use React Native at all, as we've seen with apps using Rollout.

The warning is not about React Native, it's about exposing uncontrolled access to native APIs including private ones and React Native doesn't do that.

Hi, I work on Expo (YC S16) and also am a core contributor to React Native.

Apple's message reads to me that they're concerned about libraries like Rollout and JSPatch, which expose uncontrolled and direct access to native APIs (including private APIs) or enable dynamic loading of native code. Rollout and JSPatch are the only two libraries I've heard to be correlated with the warning.

React Native is different from those libraries because it doesn't expose uncontrolled access to native APIs at runtime. Instead, the developer writes native modules that define some functions the app can call from JavaScript, like setting a timer or playing a sound. This is the same strategy that "hybrid" apps that use a UIWebView/WKWebView have been using for many years. From a technical perspective, React Native is basically a hybrid app except that it calls into more UI APIs.

Technically it is possible for a WebView app or a React Native app also to contain code that exposes uncontrolled access to native APIs. This could happen unintentionally; someone using React Native might also use Rollout. But this isn't something specific to or systemic about React Native nor WebViews anyway.

One nice thing about Expo, which uses React Native, is that we don't expose uncontrolled or dynamic access to native APIs and take care of this issue for you if your project is written only in JS. We do a lot of React Native work and are really involved in the community and haven't heard of anyone using Expo or React Native alone having this issue.

James, do you know if they're going to go agains the Exponent app per-se?
Oh, no, I don't have reason to believe so.
How can you ship an app with access to private APIs? There is a private API usage scanning before you can submit for review.
The scanner isn't foolproof. You could fool it if you obfuscate your calls to performSelector well enough, for example

if jsonResponseFromYourBackend contains:"runThis" then performSelector:json["runThis"]

and make sure you don't send a runThis param while the app is in review.

Unfortunately for Apple's app review process, Apple's own objective-C language and runtime has very strong dynamic reflection capabilities.

Apple could potentially close any loopholes here by scanning new apps for their API usage, checking for any 'bad' calls, and then writing the remaining discovered calls into a permissions file that is delivered with the app in the store.

At runtime, any API calls made by the app are checked against this file; if a new API call is found, then it must have escaped Apple's code scanning logic. The API call can be rejected and logged for Apple to improve their scanner.

This is a great idea actually. Actually, isn't google already doing this via SELinux? You give the app a manifest of calls it's allowed to make, and if the call isn't in the manifest the call gets rejected?
SELinux is not that strong. It works on kernel syscall boundaries and some parameters thereof, and those aren't particularly fine grained. Service access is governed by a separate Google API, for example.

Moreover, any random app cannot enhance SELinux policy of the system.

There are many legitimate uses of calling methods and functions using reflection. Expecting to hit all of them in a short review process is comically optimistic for anything but the simplistic of apps.

Your suggestion of enforcing this also makes no sense from performance or privacy standpoint.

Fair warning that I'm not familiar with Swift

Obvious (to me) idea: have the private API access stored as data sent from the server at runtime, rather than code in the reviewed app. Basically the equivalent of eval()-ing a string for front-end javascript code.

I do wonder why, if they're _really_ fine with that, why they're not fine with browsers with different rendering engines on iOS.

Since they're not, I wouldn't have _too much_ faith in other things not being rejected.

Simple: JIT compilers are banned and so that excludes any modern browser's JavaScript implementation from iOS. But anyone using Apple's JavaScriptCore has nothing to fear.
The rules explicitly forbid any HTML renderer or JS interpreter aside from WebKit, JIT or no JIT. I believe all the popular third party browsers today still use the non-JIT UIWebView rather than WKWebView because the former gives you more control over the request cycle
Chrome uses WKWebView on iOS, so it's basically safari with a different UI (so does firefox on iOS)
Yes, I know. I was saying the OP of this thread was wrong because it's not UIWebView, it's WKWebView with a JIT :).
WKWebView has JIT. In executes the JS in a different process than the hosted app; there the JIT lives, this special process is whitelisted to do JIT magic.
I understand that, the original OP was stating that none of the "browser skins" had JIT because they all used UIWebView, which isn't the case with the link I posted :p.
Apple has been fine with WebViews in apps since the beginning of the App Store, including WebViews that make calls to native code, like in the Quip app. WKWebView even has APIs for native and web code to communicate. It's OK for a WebView to call out to your native code that saves data to disk, registers for push notifications, plays a sound, and so on.

React Native is very much like a WebView except it calls out to one more native API (UIKit) for views and animations instead of using HTML.

What neither WebViews nor React Native do is expose the ability to dynamically call any native method at runtime. You write regular Objective-C methods that are statically analyzed by Xcode and Apple and it's no easier to call unauthorized, private APIs.

With Expo all of the native modules are safe to use and don't expose arbitrary access to native APIs. Apps made with Expo are set up to be good citizens in the React Native and Apple ecosystems.

Well, let's be clear here. I use React Native, and it would take me about 30m to write a bridged React Native method that could execute ObjC code dynamically, including accessing all the private APIs you could want.
Yeah. Objective-C is definitely really flexible. From my reading of the warning, the important thing is not to execute Objective-C dynamically or access private APIs regardless of whether you're using React Native, Cordova, a WebView, or even a bare iOS app.
Webkit is the only acceptable browser engine on iOS. Firefox and Chrome both use webkit on iOS.
This needs a little elaboration. Cached Javascript in any hybrid app is a security hole because that can be exposed through a jailbreak. Depending on how much of your business logic you've pushed into the JS layer to enable that "80% code sharing" that makes managers go all tingly you may be exposing all kinds of things - cached access tokens, API keys and whatnot - to anyone who wants to install your app and mine its secrets.
You as an end user jailbreaking your own phone is not a "security hole".

I'm not aware of any non-tethered jailbreak for iOS 10

I think he's talking about application wide (not user specific) "secrets" in the javascript layer.
Just using one to potential open many more to other apps.
Yes it is, jailbreaking bypasses critical security features of your phone. Granted, it's required to run certain kinds of software but there are better ways to run your own code on your phone (like getting your own developer certificate) that preserve the security model.
Huh? Are you saying that the security hole is that a user could see stuff in the memory of their own phone?
Or someone that steals your phone, or picks it up when you lose it somewhere. Yes, there's the lock screen and passcode but...

http://www.wikihow.com/Bypass-iPhone-Passcode

At that point your phone is fucked, anyhow. If you've lost physical control of the device and an attacker has broken the lock, you're compromised in much bigger ways.
How is this any different from native code? Afaik, you can access the native compiled source code of an app on a jailbroken phone. Sure it's more of a pain to parse through but security through obscurity isn't security.
You aren't "parsing" through compiled (and linked) code; you're decompiling it, which is a much trickier thing to do and get right. Having your app logic in Javascript in the sandbox cache is just serving it up on a plate.
Definitely, running "strings" on a binary is about as easy as finding the JS for a hybrid app (WebViews or React Native). Depending on your experience it could be easier to extract API keys from an IPA than from JS.

In either case the root issue is about sending secrets like an unscoped API key to the client. "Client secret" is an oxymoron in this context regardless of the programming language.

You'd have to know a few things first, like (1) the IPA is a ZIP file, (2) the ZIP file is actually of a directory and (3) you can dump the actual code in the JS files (if they're in the bundle directory) much easier than you can look for strings from the binary that might look like an API key.

The API key is actually the least of the hazards, since you can hide that in the keychain. Having source code for your business logic shipping in your app is not good; having it be hackable business logic (by changing the JS in place) is very not good.

Shipping source code with business logic (assuming the definition of source code includes obfuscated JS) is how the entire web works today! With WASM the code that is shipped will be even further away from the original source code and really not so different from downloading ARM from the App Store or Java/ART bytecode from the Play Store.
Not the entire web. eBay and Amazon don't put all their algorithms in the browser; PayPal doesn't either. They hide their company jewels behind their API where they're a little more secure. What you see in the browser is the presentation layer code.

Hybrid apps could achieve that same kind of relative business logic security, but at the cost of pushing more and more of the actual business logic behind an API and not in the JS in the app. At that point, the benefits of code sharing (such as they are) get fewer and fewer since it's really pretty easy to write API code in Objective C, Swift, Java or Kotlin.

I'm having trouble seeing this as a valid argument when applied to cached JavaScript within a mobile browser. The same security practices apply.
They do, and it's for this reason that the really important stuff isn't in the web page at all - it's behind the company's API. The "business logic" that you can safely push out to a browser or hybrid app is somewhat limited, which means there is a hard upper limit on the real code sharing savings you can get with hybrid apps versus full native.
Rather than a speculating on what really boils down to semantics once ToS is involved maybe someone could actually try submitting an app and reporting back on whether it triggers the same failure?
I haven't heard any reports of Expo developers or React Native developers (who aren't using Rollout or JSPatch) getting this warning.
This has always been a requirement for Apple apps, hasn't it?