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

[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

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 23 (12 by maintainers)

Most upvoted comments

@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…

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.

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.

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.

@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.

We’ll address this as part of #5928.

@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.

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.

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