Firebase-ios-sdk: SafariServices on iOS 11 imports the AdSupport framework, causing device advertisement identifier reporting.

Created on 14 Aug 2018  路  20Comments  路  Source: firebase/firebase-ios-sdk

[REQUIRED] Step 2: Describe your environment

N/A

  • Xcode version: 9
  • Firebase SDK version: 5.6.0
  • Firebase Component: Core
  • Component version: -

[REQUIRED] Step 3: Describe the problem

Firebase automatically reports the device's Advertisement Identifier when the AdSupport framework has been manually added to the project. On iOS 11, the system framework SafariServices dynamically loads the AdSupport framework, causing Firebase to report device advertisement identifier without the explicit adding of the AdSupport framework to the project.

SafariServices loads the framework via dlopen() after runtime, so you will not find a load_command to AdSupport.framework within the SafariServices.framework mach header.

Here is the relevant subroutine of disassembled code taken from SafariServices, showing the dynamic linking of the AdSupport framework.
safarisupport code

This is an interesting problem and i'd love to start a discussion around this / possible solutions to address this.

Thanks!
Ethan

analytics

Most helpful comment

@franzdevel That's pretty much what I was going at above, but it seems, with all due respect dear Firebase team, Firebase simply does not care. I find it disturbing that an included library basically "turns on" a feature of _another_ library (in this case one of the frameworks coming with iOS itself nonetheless) unbeknownst to the developer using the library.
Admittedly you could say "audit the stuff you include", but who would seriously expect that behavior and go look for it?

Precisely the reason why this probably hasn't blown up yet, license-wise, is because most developers don't notice it. So a lot of folks will have shipped apps and selecting that "I don't use the IDFA" in good faith, while their product is technically violating the TOS.

Just trying some kind of "magic" to deduct what has been linked when the linked thing touches a clear aspect of human intent is, in my book, very bad design. No logic can detect whether I intentionally included a library myself or whether that was a side-effect of something else.

That has surely costed a lot of trust, at least for me, Firebase...

All 20 comments

@EthanArbuckle Thanks for the issue. Interesting question!

With the current implementation, we just check if ASIdentifierManager is linked in and don't have a mechanism to tell why. I can't think of way to detect at runtime if the linkage would have only come from SafariServices.

Closing for stale-ness. Will reopen with more discussion.

I have the same issue with PassKit here which seems to do the same as SafariServices.

@paulb777 Did you consider adding a boolean to explicitly authorize IDFA collection because it sounds like implicit acknowledgment (AdSupport linking) is not applicable because of Apple frameworks behaviors such as PassKit and SafariServices ?

cc: @htcgh ^

I would also be interested in a solution to this (for example an explicit configuration setting). We decided explicitly not to use track additional data from our users, but we need SafariServices.

To be honest I find the philosophy to just "take what you can get" a little disappointing, imo this should have been an explicit setting from the start... I have to answer Apple's IDFA related question during release and it's concerning that by just using Firebase I might unknowingly lie. I'm not sure about the repercussions or whether this would block our next, imminent release. I found this ticket through sheer luck, btw, perhaps you should at least update the documentation until this is solved. Currently it just implies you're fine unless you link against the AddSupport framework, it's quite easy to miss the fact that that might happen indirectly.

Should it be considered an Apple issue that SafariServices implicitly links AdSupport?

I don't think that's a viable, or rather _productive_ solution. I highly doubt Apple would change anything in their implementation, there's after all probably a reason they open that library, i.e. SafariServices uses some functionality it provides (I have never worked with AdSupport so far, so I have no idea what that could be). It's a little weird that it includes the dependency in a hidden manner with dlopen, but not that uncommon for a private framework either. Maybe they do this precisely to avoid sneaking in IDFA compliance questions without people noticing...

If Apple did _anything_ at all when this is reported, my bet's an update to the IDFA usage questionnaire that we're required to fill out when submitting a binary for review (see here). Firebase basically uses it to gain demographic data and unless I use ads in my app (or advertise _for_ my app) I'm stuck. I cannot properly answer any of the options and would probably need to contact the support (if I interpret the rules strictly).

Apple would _maybe_ also improve their automated checks to figure out if the framework is used in such an indirect manner. The fact that you don't have hundreds of complaining issues here in the repo probably means that during review the usage of the framework isn't detected and people honestly answer "no" when asked if they use the IDFA (they just follow the Firebase documentation and think "nah, I don't link against AdSupport"). Then the binary checks the review likely includes don't indicate otherwise and the app gets accepted, even though deep down, Firebase then _does_ use the IDFA for the demographics and whatnot. I have no idea whether that would be okay according to Apple or a violation of the guidelines that just hasn't been found out yet (and I am aware that by writing this down here I "leave a paper trail"...).

All in all this is a great example for why it's dangerous to reflect about linked libraries during runtime and infer usage intent from that. No offense, but the way you try to automatically enable the feature is sub-optimal, in my humble opinion. You basically prevent people from using the AdSupport framework (or, due to the indirect inclusion, even SafariServices) orthogonally to Firebase Analytics. I can't use the IDFA, for whatever reason, and then not have Firebase collect demographics. That may sound implausible if you muse "Why not, you get it for free?", but with GDPR and recent developments regarding privacy and users becoming (perhaps?) more aware who collects what from them a "I only collect what I need" approach might be better.

This would require a more fine-grained configuration, i.e. giving people a configuration boolean to either use IDFA or not, _regardless of whether it's included in any way_. Obviously setting that to true and then _not_ having AdSupport included somehow should result in a warning or something (and the docs need to explain this as well). The default case can even stay as now, with the setting to true and the SDK trying to figure out whether it has an IDFA or not on its own. Maybe it's even an enum, along auto, enabled, and disabled.

This has direct consequences for my app, btw, I need to bother our legal team (since IANAL...) and stall a release. Maybe we're overly careful, but we usually try out best to not just follow the GDPR to "the letter", but also "in spirit". At least for now we decided against collecting demographic information and limit the analytics to a minimum. (Sidenote: Obviously this is true until my PO decides they want more... 馃槂 ).

Sorry for this turning into a long-winded novel, but I think the issue is important and wanted to be thorough.

Thanks for the detailed explanation. Opened internal b/135517221 for continued investigation.

@GeroHerkenrath: Thanks for providing very detailed explanation. I'd like to provide some explanation from our point of view here: GDPR is a big issue and we do try our best to comply with GDPR and developers can disable/deactivate Analytics if they don't want to collect data. IDFA can also be disabled by users if they don't want to be tracked by going to Settings to turn on Limit Ad Tracking. Due to our architecture, we have to ensure the consistency of our data and it's not possible for us to tell at run-time whether AdSupport framework is added by developers or pulled in dynamically.
Another way to disable personalized ad tracking by developers is setting GOOGLE_ANALYTICS_DEFAULT_ALLOW_AD_PERSONALIZATION_SIGNALS in the Info.plist (https://firebase.google.cn/docs/analytics/configure-data-collection). You can also enable and disable it at run-time (see https://firebase.google.cn/docs/analytics/configure-data-collection#re-enable_peronalized_advertising_features). This is a new feature we have just released with the version 6.0.0. Hopefully these abilities cover the problems you have raised.

@baolocdo The document you point to only state this prevents ad personalization based off the analytics, not the non-collection of the IDFA. While Firebase can't tell the difference between AdSupport being linked or SafariServices and other libraries dynamically pulling it in, there doesn't seem to be a good technical reason why there isn't an equivalent GOOGLE_ANALYTICS_IDFA_COLLECTION_ENABLED to the existing GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED. Additionally, the current documentation linked doesn't mention to users that not having AdSupport linked is not enough to ensure you don't use the IDFV due to the dynamic linking @GeroHerkenrath mentioned.

Similar to @GeroHerkenrath, I have the same concern about collection of the IDFA by Firebase & comparable analytic libraries like Mixpanel offer a flag to prevent IDFA collection (MIXPANEL_NO_IFA).

@baolocdo Thank you for the reply. Let me first say I appreciate that you take this seriously and respond so timely. I also want to stress that I like Firebase, it has helped us a lot.

Like @abotkin-cpi said, I am not sure whether the configuration settings you linked solve our issue, but that also means I am not a 100 % sure they're _needed_ in our case. Let me illustrate by hypothetically going through the relevant steps in Appstore Connect when submitting my app for review.

It starts with the question whether I use the IDFA in my app. Originally I would have answered "No", because I don't (explicitly) link to the AdSupport framework. That would have been the end of it.

Now let's say I'd answer "Yes". Next come four options to select to assure Apple I do everything according to their TOS. The first three are about what I use the IDFA for.

  1. "Serve advertisements in the app". No, I don't. In fact, we don't even have an ads account that we'd need for this. Easy.
  2. "Attribute this app installation to a previously served advertisement". Er, also no? Again, we don't have an ads account, so we won't even run ads _for_ our app at this point. Unless some generous benefactor buys advertisements for us that won't happen.
  3. "Attribute an action taken within this app to a previously served advertisement". Nope, we don't do that either.

Last, the form requires me to assure that we respect the Limit Ad tracking setting. Obviously I trust that Firebase does this and would check "Yes" here.

Overall, this sure as hell looks weird, doesn't it? In the end, the _only_ thing that the IDFA is used for is _the IDFA itself_.

Or you could say it's used to "convey to Firebase & the Ad Sense framework that a user _uses our app_". I assume that within the big picture that then contributes to the demographical data we get to see in Firebase, am I right? I mean, the Firebase SDK doesn't collect any of that from our users directly (unless we set user properties), or am I completely mistaken here? It's an amalgamation of _all_ relevant data Firebase has, including other apps, platforms etc.?

So while that surely isn't personally identifiable information coming from our app, it _is_ a form of "taking part" in some kind of profiling (for a lack of a better word, I mean profiling groups, not individuals). That's not covered by Apple's above explained form and I don't _think_ it would be a problem, but it is _technically_ also not _not_ using the IDFA. As said earlier I am not a lawyer (and for sure I wish I didn't have to deal with any of this privacy stuff at all 馃槂), so I have no idea whether this is important _at all_ in this context.

What is a little odd overall is that the demographics data we get shown in Firebase as a result of this makes us look like a company that collects such data. I don't want to come across as ungrateful (in fact I am sure that in the future some of our business analysts will be very thankful for this), but providing a clear cut option of "no thanks, we won't give you the IDFA and in turn we won't get that data" is a good thing to have. I mean, after all you sure _want_ to provide that option, apparently, the proposed way (not linking AdSupport) just doesn't fully work...

I apologize again for being so long-winded... 馃檨 I somehow can't help it...

Thanks for the feedback. I understand your concerns and I have brought up this thread to our Product Managers. They will consider the appropriate solutions for this issue.

Any updates on this? The inability to specify a GOOGLE_ANALYTICS_IDFA_COLLECTION_ENABLED similar to the existing GOOGLE_ANALYTICS_IDFV_COLLECTION_ENABLED is causing conversations in our workplace about removing Firebase entirely.

Hey guys - the problem here is that there is currently not a way to determine /how/ the AdSupport framework is brought into the runtime.

Would an "easy" solution be to dump the load commands of the application's executables (both main executable and frameworks) and look for presence of AdSupport.framework? If there is a load command for it, it means the developer has explicitly added it to the project.

If there is no load command but the ASIdentifierManager class is still present, it could be assumed that it was silently linked by an apple framework. This does not cover the cases of developers dynamically linking AdSupport themselves, but I think that is a corner case?

@EthanArbuckle From our perspective, the easiest solution would be to just allow the developer to specify an Info.plist flag to disable IDFA usage no matter AdSupport framework linkage, just like how IDFV is handled today.

The pressure we're getting comes from a legal compliance angle where we need to not be allowing IDFA collection & the current situation of trying to do smart determination of how it was linked at runtime and failing makes Legal folks nervous as they'd prefer an easy declaration we make to frameworks we integrate so that failure to comply with that directive is a vendor issue. Mixpanel accomplishes this via the preprocessor definition MIXPANEL_NO_IFA that just wraps the IFA method like so:

- (NSString *)IFA
{
    NSString *ifa = nil;
#if !defined(MIXPANEL_NO_IFA)
    Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager");
    if (ASIdentifierManagerClass) {
        SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
        id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector);
        SEL advertisingTrackingEnabledSelector = NSSelectorFromString(@"isAdvertisingTrackingEnabled");
        BOOL isTrackingEnabled = ((BOOL (*)(id, SEL))[sharedManager methodForSelector:advertisingTrackingEnabledSelector])(sharedManager, advertisingTrackingEnabledSelector);
        if (isTrackingEnabled) {
            SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier");
            NSUUID *uuid = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector);
            ifa = [uuid UUIDString];
        }
    }
#endif
    return ifa;
}

It also makes it easier for us to write tests to determine compliance of our dependencies since we can call their IFA method & ensure they're returning nil.

Thank you, @abotkin-cpi, I couldn't have said it better.

Any update on this? The need for a compile time flag to prevent IDFA usage is even more necessary now that iOS 14 will cause a prompt to users when it is accessed.

We'll address this as part of #5928.

Just to add another reason why the option to prevent IDFA usage is really necessary: the Apple Developer Program License Agreement states that IDFA may only be used for the purpose of serving advertisements. In other words, an app that does not include advertisements but uses IDFA via Firebase is technically in breach of the License Agreement. In the past, it has happened that Apple has cracked down on apps because of this, see https://techcrunch.com/2014/02/03/apples-latest-crackdown-apps-pulling-the-advertising-identifier-but-not-showing-ads-are-being-rejected-from-app-store/

We have this problem now as we're developing an app with Xamarin.iOS. The reference to the AdSupport framework is usually stripped from Xamarin.iOS.dll during the build, but having added Firebase, the framework gets included in the final binary and causes Firebase to use IDFA.

@franzdevel That's pretty much what I was going at above, but it seems, with all due respect dear Firebase team, Firebase simply does not care. I find it disturbing that an included library basically "turns on" a feature of _another_ library (in this case one of the frameworks coming with iOS itself nonetheless) unbeknownst to the developer using the library.
Admittedly you could say "audit the stuff you include", but who would seriously expect that behavior and go look for it?

Precisely the reason why this probably hasn't blown up yet, license-wise, is because most developers don't notice it. So a lot of folks will have shipped apps and selecting that "I don't use the IDFA" in good faith, while their product is technically violating the TOS.

Just trying some kind of "magic" to deduct what has been linked when the linked thing touches a clear aspect of human intent is, in my book, very bad design. No logic can detect whether I intentionally included a library myself or whether that was a side-effect of something else.

That has surely costed a lot of trust, at least for me, Firebase...

Was this page helpful?
0 / 5 - 0 ratings