Současné hrozby: hromadný sběr dat, problémy v procesorech a prohlížeče

4. 2. 2019
Doba čtení: 7 minut

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
Svět se neustále mění a s ním se mění i bezpečnostní hrozby. Jaké problém nás nejvíce ohrožovaly v uplynulém roce a na co si musíme v tom letošním dávat pozor? Příliš mocné prohlížeče, chyby v CPU nebo těžba dat.

Poslední lednový den proběhl Seminář o bezpečnosti sítí a služeb pořádaný sdružením CESNET. Hovořilo se o aktuálních bezpečnostních problémech, hrozbách a technologiích k jejich řešení. Jedna z přednášek se věnovala také současné bezpečnostní situaci, přednášejícími byli Radoslav Bodó ze sdružení CESNET a Jakub UrbanecTrend Micro.

Svět se změnil, data ochotně předáváme

Jisté je, že se svět za posledních deset let výrazně proměnil. Co nejde najít Googlem, to na internetu není, začal přednášku Radoslav Bodó. Hledal jsem například pyžamo s čepičkou a našlo mi to obchody v mém okolí. Co tam nebylo, to pro mě neexistuje. Změna proběhla také v e-governmentu, máme kvalifikovaný podpis, datové schránky, Czech Point a další vymoženosti. Je to obrovský posun.

Během uplynulé dekády přišel také cloud, což bylo dříve nepředstavitelné. Chránili jsme si data, nechtěli jsme je nikomu dát. Dnes jsme rádi, že můžeme odsouvat tuny odpadu do cloudu a rádi za to i platíme. Proměnila se i komunikace, všechno se názorově ovlivňuje, dezinformuje, manipuluje. Naše vláda s námi komunikuje přes americkou službu jménem Twitter! To bylo dříve naprosto nepředstavitelné.

Cambridge Analytica ví

Velkým bezpečnostním tématem loňského roku byla kauza Cambridge Analytica. Zneužili API Facebooku, zaplatili asi 270 tisíc lidí a posbírali informace nejen o nich, ale i o jejich kamarádech, vysvětlil v další části přednášky Jakub Urbanec. Ukázalo se, že jejich software je schopen na základě nasbíraných dat sestavit psychometrický profil uživatele. Zjistíte tak, do jakých skupin lidé patří – jestli jsou černí nebo bílí, kdo z nich je gay nebo jestli je republikán nebo demokrat. Pak je možné lidi cíleně ovlivňovat. Vypadá to jako sci-fi, ale ukázalo se, že se to skutečně používá: ovlivňují se tím volby, výsledky referend a podobně. Tenhle problém je obrovský, můžete profilovat konkrétní lidi.

Nejdále je v tomhle ohledu Čína, kde mají kreditový systém známkující každého občana. Posbíráte všechny informace o lidech, které máte k dispozici: co nakupuje, zda je introvert, jestli píše články proti čínskému prezidentovi a tak dál. Zároveň přidáte informace z kamer, finanční situaci a další informace a z toho vypočítáte skóre. Jako v nějaké hře, bohužel to není hra, řekl Urbanec.


CESNET

Radoslav Bodó a Jakub Urbanec

Pokud má občan vysoký kredit, má v běžném životě spoustu výhod. Když ale máte špatné skóre, nekoupíte si jízdenky na rychlovlaky nebo letenku. Výsledek ovlivňuje také váš partner a rodina. Pokud mají lidé kolem vás nízký kredit, budete ho mít i vy. Pokud vám Facebook a Google připadal jako velký bratr, tohle je úplně jiná liga.

Totálním hitem roku 2018 byla směrnice GDPR. Ta říká, že data existují, jsou někde uložena a je s nimi nakládáno. Je potřeba vědět, kdo, kdy, jak a co s daty dělá a je potřeba data je chránit. GDPR děsilo spoustu lidí a stále je možné za něj schovat prakticky cokoliv. Když budete mít nějaký problém, můžete ho vyřešit jakkoliv a vysvětlit to odvoláním na GDPR.

Jak „háknout“ elektrárnu?

Na internetu je spousta snadno dostupných dat a metadat. S jejich znalostí je možné provádět zajímavé kousky, některé až děsivé. Co může udělat útočník, který se rozhodně poškodit dejme tomu elektrárnu?

Když se někdo rozhodne zaútočit na elektrárnu, je potřeba ji nejprve najít. Služba Descartesmap například umožňuje vyhledávat na mapách podobné objekty, pohodlně je tak možné nechat si vyhledat objekty, které vypadají jako elektrárna. Podrobné informace včetně polohy jsou ale také k nalezení na Wikipedii. Stačí si tedy pohodlně vybrat, na co budu útočit.

Jak ale zjistit IP, když znám polohu objektu? Můžete využít služby GeoIP a hledat podle GPS koordinátů nebo podle PSČ. Znáte pak polohu objektu a jeho IP adresu. Dalším krokem je nalezení rozhraní ICS systému a můžete útočit. Můžete takhle složitě hledat, nebo můžete použít hotovou službu. Tou je například Shodan, který umí přímo vyhledat zranitelné řídicí systémy: větrné elektrárny, solární elektrárny a další. Není to problém zabezpečit, ale když se k tomu dostanu za pár sekund já, co s tím dělají velké mocnosti.

Meltdown a Spectre

V minulých letech jsme sledovali zranitelnosti od chyb v operačních systémech, programovacích knihovnách a aplikačních serverech. V minulém roce se ale přišlo na to, že bezpečnostní chyby jsou už na úrovni procesorů. Ty umožňují číst z paměti počítače data, ke kterým by útočník neměl mít přístup.

Chyby využívají toho, že v procesoru jsou paměťové keše, které přednačítají stránky z pomalé RAM. Předpokládá se, že pokud potřebujete bajt z paměti, budete pravděpodobně brzy potřebovat další okolní data. Proto se jich rovnou do keše uloží víc. Tím se výrazně urychluje přístup k paměti. Zároveň se používá vykonávání mimo pořadí, aby bylo možné optimalizovat využití jednotlivých částí procesorů.

Procesy běží v privilegovaném či neprivilegovaném režimu. V privilegovaném běží obvykle operační systém a má přístup k celé paměti, v tom neprivilegovaném se zpracovávají běžné procesy, u kterých se kontroluje přístup paměti. Problém je, že kontrola přístupu do paměti se při vykonávání mimo pořadí provádí až na konci. Běh těchto těchto instrukcí má vedlejší efekty, které se odrazí v naplnění keše. Tím se daný proces může postranním kanálem dozvědět, jaká data jsou mimo jeho paměť – stačí změřit přístupovou dobu a zjistit, zda je stránka v keši či nikoliv. Může tak postupně vyčíst libovolnou paměť, třeba tu, kde má jádro uložené přístupové údaje.

Čtěte: Jak funguje Meltdown a Spectre

To je princip útoku Meltdown. Představte si to jako restauraci, kde se neuklízí po hostech. Když tam přijdu, uvidím špinavé talíře a uvidím, co hosté přede mnou jedli, ačkoliv jsou dávno pryč, vysvětlil Bodó základní princip tohoto útoku.

Spectre zase využívá toho, že se kód může větvit a je možné odhadovat, kam se dál vydá a zneužije se predikce v procesoru. Na rozdíl od Meltdown nemůžu číst libovolnou paměť, ale jen tu, která patří mému procesu. I přes toto omezení je to stále velmi dobře využitelné: JavaScript může číst z paměti prohlížeče data, PHP by mohlo na sdíleném hostingu číst data jiných uživatelů a podobně. Během roku 2018 byly tyhle koncepty dále rozpracovány a objevila se spousta dalších variant.

Dohromady jde o využití postranních efektů pokročilých funkcí procesorů, které se používají pro zvyšování výkonu. Není to pouze problém Intelu, podobně jsou postiženy i třeba procesory od AMD či ARM. Explotace na desktopu není až tak zásadní problém, jako hrozby na úrovni cloudů a jiných sdílených prostředí. Útok je to obtížný, ale není pouze teoretický.

Oprava vyžaduje záplaty jader operačního systémů a zároveň je potřeba opravit firmware v procesorech. Problém je, že to má výkonnostní dopad, protože se ve správných chvílích vypíná predikce skoků a další funkce, které mají za úkol zvyšovat výkon.

DNS ReBind

Že jsme schovaní za firewallem a NATem, nemusí znamenat, že jsme v bezpečí. Zatím ale stále ještě platí, že přímý útok přes NAT není možný. Nepřímých jsme ale viděli spoustu. Pro útok DNS ReBind potřebujeme být blízko sítě a mít možnost se sítí komunikovat, obvykle stačí mít přístup k uživatelově prohlížeči skrze naši otevřenou webovou stránku.

Princip spočívá v tom, že javascriptový kód na napadené stránce začne ohrožovat počítače a zařízení v místní síti. Teoreticky by tomu měla zabránit same-origin policy, což je klíčová bezpečnostní zásada webových prohlížečů. Bráni tomu, aby skripty z jedné domény mohly komunikovat se skripty z jiné domény.

Toto omezení je ale možné obejít právě pomocí DNS ReBind. Základem je DNS server ovládaný útočníkem, který odpovídá na dotazy v útočníkově doméně. DNS server vrací odpovědi s velmi krátkým TTL a brání tak v jejich efektivnímu kešování na straně klienta. 

První dotaz na danou doménu vrátí IP adresu skutečného hostingu, na kterém je útočný skript. Ten se normálně načte, spustí a poté požádá o kontakt se svou doménou, ale DNS server už vrátí interní IP adresu některého stroje, který budeme chtít napadnout. Protože doménové jméno zůstalo zachováno, prohlížeč komunikaci nezablokuje. Skript pak má přístup dovnitř sítě a může například získat citlivá data. Současné sítě je možné tímto způsobem napadnout.

QUIC aneb HTTP/3

Google začal už poměrně dávno používat na svých službách protokol QUIC, který je nyní standardizovat jako protokol HTTP/3. Ten je postaven na UDP, šetří RTT a umožňuje migraci spojení. Jenže zatímco u TCP musíte při sestavování spojení prokázat, že umíte přijímat a odesílat pakety pro danou adresu, v případě UDP to dělat nemusíte. Můžete si posílat cokoliv.

linux_sprava_tip

Je tak možné velmi snadno vytvářet vlastní pakety s cizí IP adresou a komunikace běžně prochází přes NAT. Tím se otevírají nové možnosti útoků. QUIC například umí přenést HTTP hlavičky jen jednou a poté si na ně pro urychlení jen odkazovat. Mohli byste třeba teoreticky zfalšovat paket, který by přečetl cizí hlavičky s cookies. Problém je, že ten protokol neprošel seriózním prověřením za hranicemi Google.

Prohlížeče se staly centrálním bodem v našem počítači a jsou velmi mocné. Máme tu tedy programovatelnou aplikační platformu, která používá proprietární protokoly, proprietární šifrování a má velkou kontrolu nad našimi počítači. Google se tak stal novým Microsoftem. Dává nám věci zadarmo, ale něco si za to bere.

Autor článku

Petr Krčmář pracuje jako šéfredaktor serveru Root.cz. Studoval počítače a média, takže je rozpolcen mezi dva obory. Snaží se dělat obojí, jak nejlépe umí.

'; 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'; } }
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.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:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { 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(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream 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 = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ 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(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ 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(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »