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.
I don't care about specific numbers, let's use some from androidauthority.com . ios apps make more revenue then Google Play apps. So what you say? Well you make a LOT more revenue with BOTH gPlay + Appstore then EITHER on their own.
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".
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.
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.
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...
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.
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.
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..."
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.
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.
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.
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.)
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.
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.
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.
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.
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.
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.
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.
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.
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)
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).
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.
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.
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?
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.
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.
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".
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.
"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.
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 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.
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.
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.
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."
"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.
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?
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.
> 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.
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.
> 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.
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.
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.
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.
"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
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?
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]
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.
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.
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.
"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.
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.
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.
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
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.
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.
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.
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.
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?
497 comments
[ 3.5 ms ] story [ 408 ms ] threadhttp://www.androidauthority.com/google-play-store-vs-the-app...
"At most", those companies would just have to "change their businesses".
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.
I would also recommend not using CodePush to completely what an app does.
>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.
> 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...
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.
"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..."
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.
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."
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.
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.
It's much like the argument 'if you ban guns only criminals will have guns' and it's quite true in this case.
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.
I say, don't go native unless you must (for performance reasons etc). push the web forward instead!
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.
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.)
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...
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.)
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.
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.
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.
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.
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.
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.
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).
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.
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.
Does Rollout comply to Apple’s Guidelines?
Ouch. Just like the company's future is in danger.https://news.ycombinator.com/newsguidelines.html
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.
I've never understood this part. Why doesn't iOS simply prevent apps from calling private APIs?
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.
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.
And well, in any case they need to maintain compatibility with current apps for who knows how many years.
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.
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 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.
This exists today and has for a long time, it just costs you money for this "privilege".
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 legal in the vast majority of cities.
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.
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.
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.
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/
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 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.
We always felt this day would come, however, so we switched directions.
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.
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.
> 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.
> 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.
Or is the app review process more subjective?
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
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?
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
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.
Source: https://github.com/bang590/JSPatch/issues/746 (in Chinese)
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.
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.
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.
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.
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.
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.
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.
Moreover, any random app cannot enhance SELinux policy of the system.
Your suggestion of enforcing this also makes no sense from performance or privacy standpoint.
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.
Since they're not, I wouldn't have _too much_ faith in other things not being rejected.
https://blog.chromium.org/2016/01/a-faster-more-stable-chrom...
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.
I'm not aware of any non-tethered jailbreak for iOS 10
http://www.wikihow.com/Bypass-iPhone-Passcode
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.
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.
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.