Skrytý sledovač: Jak Meta a Yandex tajně monitorují vaše prohlížení na Androidu

4. 6. 2025

Sdílet

mobil, sledování
Autor: Radan Dolejš s podporou AI
Společnosti Meta a Yandex našly způsob, jak propojit vaše prohlížení s vašimi profily v jejich aplikacích. Dle Googlu jde o „hrubé porušení“ podmínek obchodu Play.

Mezinárodní tým výzkumníků odhalil důmyslné schéma, které společnosti Meta a Yandex využívají k tomu, aby na zařízeních s operačním systémem Android dokázaly skrytě propojit vaši webovou aktivitu s identitou na sociálních sítích. A to i tehdy, využíváte-li k prohlížení anonymní režim prohlížeče.

Sledování funguje prostřednictvím kódu vloženého do milionů webových stránek prostřednictvím Meta Pixelu, respektive služby Yandex Metrica.

Využívají vaši zaměstnanci pokročilou datovou analytiku?

Za normálních okolností aplikace jako Facebook nevidí, jaké webové stránky navštěvujete, Meta a Yandex však našly způsob, jak prostřednictvím svých trackerů s využitím legitimních internetových protokolů základní bezpečnostní ochranu běžně oddělující prohlížení webu od aplikací prolomit.

Způsob, jakým Meta a Yandex tuto ochranu obcházejí, se v technických detailech liší, obě však využívají něco, co výzkumníci označují jako weird protocol misuse, aby získaly neověřený přístup k specifickým lokálním portům, na nichž pak aplikace Facebooku, Instagramu či Yandexu v pozadí tiše naslouchají a propojují přístupy na web s uživatelskými účty přihlášenými do dané aplikace. To vše se děje bez jakéhokoliv upozornění či vědomí uživatele.

Budoucnost šifrování: Jak se firmy připravují na postkvantovou éru s kyberexpertem Petrem Kunstátem
Budoucnost šifrování: Jak se firmy připravují na postkvantovou éru s kyberexpertem Petrem Kunstátem
0:00/

Dle Googlu takové chování porušuje podmínky obchodu Play i očekávání uživatelů týkající se ochrany osobních údajů v systému Android. Zástupce Googlu rovnou uvedl, že jde o „hrubé porušení“ těchto podmínek. Meta reagovala s tím, že s Googlem jedná o řešení „možného nedorozumění“ ohledně uplatňování jeho zásad.

„Poté, co jsme se dozvěděli o znepokojení Googlu, jsme se rozhodli danou funkci pozastavit, dokud problém společně nevyřešíme,“ vyjádřil se zástupce Mety.

Pravda však je, že Meta s touto praxí začala loni v září a průběžně ji aktualizovala, aby si před ochranou prohlížeče udržovala náskok. Jak výzkumníci konstatují, když Chrome zablokoval jednu techniku, Meta ji pozměnila „během několika dní“. 

Yandex prý tento typ propojování uživatelských dat používá dokonce už od roku 2017; k aktuální kauze se jeho zástupci nevyjádřili.

prace_s_linuxem_tip

Miliardy ukradených cookies jsou volně na netu. Jsou aktivní a jsou i z Česka Přečtěte si také:

Miliardy ukradených cookies jsou volně na netu. Jsou aktivní a jsou i z Česka

Výzkumníci upozorňují, že vůči podobnému sledování jsou zranitelné zejména prohlížeče Chrome a Firefox; i přesto, že Google se snaží na nová zjištění reagovat, dle výzkumníků není obtížné systém obejít.

Alternativou můžou být menší prohlížeče jako DuckDuckGo nebo Brave, které prý v tomto směru nabízejí lepší ochranu. Zcela nejúčinnější ochranou však dle nich je vůbec do systému Android aplikace Facebook, Instagram a Yandex neinstalovat.

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }