Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SafariServices on iOS 11 imports the AdSupport framework, causing device advertisement identifier reporting. #1686

Closed
EthanArbuckle opened this issue Aug 14, 2018 · 23 comments
Assignees

Comments

@EthanArbuckle
Copy link

[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

@paulb777
Copy link
Member

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

@paulb777

This comment has been minimized.

@davidattias
Copy link

davidattias commented Jan 16, 2019

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 ?

@paulb777
Copy link
Member

cc: @htcgh ^

@GeroHerkenrath
Copy link

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.

@paulb777
Copy link
Member

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

@GeroHerkenrath
Copy link

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.

@paulb777 paulb777 reopened this Jun 18, 2019
@paulb777
Copy link
Member

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

@baolocdo
Copy link

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

@abotkin-cpi
Copy link
Contributor

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

@GeroHerkenrath
Copy link

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

@baolocdo
Copy link

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.

@abotkin-cpi
Copy link
Contributor

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.

@EthanArbuckle
Copy link
Author

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?

@abotkin-cpi
Copy link
Contributor

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

@GeroHerkenrath
Copy link

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

@abotkin-cpi
Copy link
Contributor

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.

@morganchen12
Copy link
Contributor

We'll address this as part of #5928.

@franzdevel
Copy link

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.

@GeroHerkenrath
Copy link

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

@paulb777
Copy link
Member

paulb777 commented May 17, 2021

Should now be addressed with Firebase 7.11.0.

Details in https://firebase.google.com/docs/analytics/configure-data-collection?platform=ios

@rpramirez87
Copy link

Screen Shot 2021-05-17 at 10 26 50 AM

@paulb777 I originally suggested this documentation that Firebase supports removing IFDA collection by replacing Firebase/Analytics with a subspec pod 'Firebase/AnalyticsWithoutAdIdSupport'. However, based on Firebase release notes for 7.11.0. It still seems like AdSupport removal is still a requirement. Please confirm if we can disable IDFA collection without removing AdSupport framework.

@paulb777
Copy link
Member

Confirmed. Analytics does not collect IDFA in an app that uses 'Firebase/AnalyticsWithoutAdIdSupport' even if AdSupport.framework is linked in.

@firebase firebase locked and limited conversation to collaborators Jun 17, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants