8bitco.de
Loyalty Card? Privacy might be hard!
As most of the folks on the XXI century, I carry my smartphone with me anywhere I go, it’s a blessing and a privacy nightmare that we sponsor with our willingness to share personal data, mostly unaware that we are doing so.
# The problem
The other day, I tried to use the mobile app for a supermarket loyalty card without success. Worse than that, I did not have with me the physical card so I couldn’t even use any available coupons. I payed and checked out with my groceries not giving it much thought.
The next time I went shopping for groceries, the same problem occured. This time, the app wouldn’t even open properly. It had worked seemingly flawless until some days ago. This was not some random error anymore. I tried uninstalling the app and installing it back, only to find it to be worse: I couldn’t even login anymore. Needless to say I checked ou again without any loyalty benefits.
# The origin
I was not happy with my sudden inability to use loyalty discounts on the groceries store closer to home. So I did what the best detectives do when processing the scene of a crime, in this case my phone, I back tracked every changes I had made that could lead to the current situation.
To test for the culprit, I tried to login on the loyalty card app each time I undid some setting or configuration. It wasn’t long before I found the source of the problem: the private DNS service.
As a privacy aware professional, I chose to configure DNS4EU as my private DNS service. It is is a secure, privacy-compliant public DNS resolver service funded by the European Union to boost digital sovereignty, featuring protective options and managed by a consortium.
> if any device connected to the DNS4EU Resolver tries to access a malicious domain (for example to activate a malware or to access a scam website), it is stopped immediately, preventing any damage the threat may have caused.
I had configured the Protective Resolution with Child Protection & Ad blocking. As soon as disabled the private DNS setting, the loyalty card app became functional. I was able to login and, on the next trip to the groceries store, it was fully functional.
# Who to blame?
The private DNS service is blocking domain resolution for critical features on the loyalty card app, including authentication. This is really bothersome given that I shouldn’t have to turn off the private DNS each time I go to the supermarket, right?
It’s also strange that a trustworthy app, from a European company, should fall on the blocking policies of the protective measures by an European Service. Alas, it does and it is maimed by it.
# The crime lab
Only one way to determine if the private DNS service is being too strict, or the loyalty card app is not that trustworthy after all. Follow the DNS requests and find out which ones are being rejected. For that we have to find a way to capture the requests.
I went with PCAPDroid, in their own words it’s a “privacy-friendly open source app which lets you track, analyze and block the connections made by the other apps in your device”. After installing, the setup is pretty straight-forward:
1. Open PCAPDroid, choose to filter by the loyalty card app.
2. Select the option to create output on PCAP file format.
3. Start the tracking.
4. Open the App, wait for it to startup and get beyond the welcome screen.
5. Swap back to PCAPDroid and stop the tracking session. Send the output file to yourself (I used a Signal message to personal notes).
After saving the PCAP file (ex: PCAPdroid_DD_MMM._HH_mm_ss.pcap), I used command line tools on a linux prompt to search for the DNS queries:
tcpdump -r PCAPdroid_DD_MMM._HH_mm_ss.pcap -n udp port 53 2>/dev/null | grep -oP '(?<=\? )\S+\.' | sed 's/\.$//' | sort -u
The output of this was the following list:
cartaocontinente.pte-11439.adzerk.netkafkaproducer-appcc.mc-events.ptmc8b785nhn5bqbnzs5-k4bfk02s8.device.marketingcloudapis.commobile-collector.newrelic.coms.zkcdn.net
Looking at the list, the first one is a dead giveaway of what supermarket chain I’m a customer of. The other 5 domains are definitely something that an adblocker service might deny access to:
* **e-11439.adzerk.net**
Adzerk (now doing business as “Kevel”) is a programmatic ad-serving API company that develops and publishes advertising server space, applications, and tools, providing real-time reporting, ad serving, and network building support.
* **kafkaproducer-appcc.mc-events.pt**
A Portuguese (`.pt`) domain, subdomain naming (`kafkaproducer`) suggests a backend event-ingestion/telemetry endpoint — likely feeding an Apache Kafka pipeline for an events/marketing platform.
* **mc8b785nhn5bqbnzs5-k4bfk02s8.device.marketingcloudapis.com**
This is a Salesforce Marketing Cloud endpoint — the `marketingcloudapis.com` domain is Salesforce’s official API domain for its Marketing Cloud (ExactTarget-derived) platform, and the `device.` subdomain pattern is used for the MobilePush/device-registration SDK embedded in mobile apps to register push tokens and sync marketing data. The long alphanumeric string is a tenant-specific subdomain (a “stack” identifier) Salesforce assigns per customer instance.
* **mobile-collector.newrelic.com**
New Relic is an application performance monitoring (APM) and observability platform. This specific hostname is the ingestion endpoint for New Relic’s Mobile SDK, collecting crash reports, performance metrics, and diagnostic telemetry from apps that have the New Relic mobile agent embedded.
* **s.zkcdn.net**
This resolves to Zeotap, a Customer Data Platform (CDP) headquartered in Berlin that helps organizations unify, manage, and activate customer data across marketing and digital channels, with roots in third-party audience/identity data. `zkcdn` = “Zeotap CDN,” and the `s.` prefix typically indicates a script/tag-serving subdomain, consistent with an SDK dropped into an app or site to sync identifiers and audience segments for ad targeting.
# So who’s being bounced out the door?
So far all I’ve done is finding out what the customer loyalty app is up to. Now it’s time to determine which of the identified domains, if not all, is on DNS4EU block list.
At the time of this writing, the DNS server we can use for the Protective Resolution with Child Protection & Ad blocking, has the IP address 86.54.11.11. Let’s use it with the “dig” command, for instance:
dig @86.54.11.11 marketingcloudapis.com
The output has a section called the “ANSWER SECTION”. Let me present you with a quick summery of the results:
~$ dig @86.54.11.11 marketingcloudapis.com
[...]
;; ANSWER SECTION:
marketingcloudapis.com. 3600 IN A **0.0.0.0**
[...]
~$ dig @86.54.11.11 cartaocontinente.pt
[...]
;; ANSWER SECTION:
cartaocontinente.pt. 89 IN A 31.214.213.44
[...]
~$ dig @86.54.11.11 e-11439.adzerk.net
[...]
;; ANSWER SECTION:
e-11439.adzerk.net. 60 IN CNAME e-11439-eu-west-1.adzerk.net.
e-11439-eu-west-1.adzerk.net. 60 IN CNAME e-prod-alb-s102-eu-west-1-00l.adzerk.net.
e-prod-alb-s102-eu-west-1-00l.adzerk.net. 60 IN A 54.73.215.17
e-prod-alb-s102-eu-west-1-00l.adzerk.net. 60 IN A 54.72.193.79
~$ dig @86.54.11.11 kafkaproducer-appcc.mc-events.pt
[...]
;; ANSWER SECTION:
kafkaproducer-appcc.mc-events.pt. 158 IN CNAME c1e12a1286.l11rbz.net.
c1e12a1286.l11rbz.net. 158 IN A 31.214.213.44
[...]
~$ dig @86.54.11.11 mobile-collector.newrelic.com
[...]
;; ANSWER SECTION:
mobile-collector.newrelic.com. 3600 IN A **0.0.0.0**
[...]
~$ dig @86.54.11.11 s.zkcdn.net
[...]
;; ANSWER SECTION:
s.zkcdn.net. 3600 IN A **0.0.0.0**
[...]
If the DNS server returns the result “0.0.0.0”, then it’s the same as saying it is blocked as it did not translate the domain to a proper existing IP address. Said that, we can safely say that the following domain name resolutions are blocked on DNS4EU:
* s.zkcdn.net
* mobile-collector.newrelic.com
* marketingcloudapis.com.
# Conclusion
By adopting a private DNS service that helps me protect against malicious sites and blocks advertising, I unintentionally sabotaged my grocery store loyalty card. This shouldn’t happen, using the App with a virtual loyalty card should behave the same way as a plastic physical one used on the supermarket checkout. Or at least still work even if the complementary marketing sorcery fails to cast the spell. In this case it fails. And it fails on many levels: fails to work, fails to tell you why it’s not working, fails to give you the discounts you are entitled as a regular customer and fails to respect your online safety.
I bothered to read the privacy policy from the supermarket chain and it states and I quote:
> Combining data to improve the shopping experience and personalize content and advertisements: MCH may analyze your personal data from various sources—specifically by combining your Member data, Transactional data, and Browsing data (collected via cookies and/or similar technologies on MCH digital assets, such as the website or app, or on Partners’ digital assets)—provided you have given your consent and linked your Continente Card on said assets. The analysis of personal data described above will be used to improve your shopping experience and to personalize content and advertisements, with the aim of promoting products and services that may interest you, whether on MCH assets or on Partners’ assets.
> The legal basis for processing data for the purpose described above is your consent.
So the legal basis for their purposes is my consent. I’m not a legal professional but this is very wrong. Consent is something you give freely and explicitly. Not the case when you are misinformed, there is money to be saved, or even earned in cashback campaigns and no alternative ou opt-out option is made available.
My final take on this whole deal is:
* The loyalty card app should work even when telemetry and ad placement is not possible.
* Someone should review the legal basis because there is a clear unbalance between data subject rights and the data processing performed, with not opt-out option.