Bezpečnostní incidenty v roce 2015: co už se stalo a co nás teprve čeká

29. 5. 2015
Doba čtení: 9 minut

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
Bezpečnostních incidentů bude rozhodně přibývat, otázka zní, zda jsou uživatelé a správci schopni je včas odhalit a zabránit jim. Ukazuje se, že řada sítí nemá dostatečné monitorovací nástroje a problémů si všimne až oběť případného útoku. Jak se takové útoky objevují? Co zajímavého už se stalo?

Ve čtvrtek 27. května proběhla konference Internet a Technologie 15, na které zazněla řada témat týkajících se bezpečnosti. Není se čemu divit, počítačová bezpečnost je dnes velkým tématem i mimo počítačový svět. Hrozeb přibývá a s nimi musí přibývat i nové způsoby odhalování a obrany.

Nejzajímavější bezpečnostní incidenty v roce 2015

Michal Prokop z CSIRT.CZ na konferenci hovořil o tom, k jakým zajímavým incidentům se tým už v letošním roce dostal. Jedním ze zdrojů dat jsou honeypoty, které skupina provozuje od dubna 2015. Rozhodli jsme se aktivně informovat správce sítí o problému přicházejícím z jejich sítě. Za pouhé dva měsíce provozu bylo takto získáno více než 2300 unikátních IP adres. Už jsme stihli kontaktovat 46 zemí, které přislíbily spolupráci. Zatím nedošlo k významnějšímu poklesu útoků, ale věříme, že k němu dojde.

Zajímavým incidentem bylo například šíření trojského koně Geodo, který vychází ze staršího malware Feodo. Ten původní rozesílal falešné faktury Deutsche Telekom, O2 a Vodafone – tímto způsobem se malware šířil. Nová verze už ukradla více než 50 tisíc přihlašovacích údajů na SMTP servery. V Česku jsme zachytili 1900 IP adres, ze kterých malware komunikoval.

Často se útočí na servery, které vystavují přístup k databázi do celého internetu. Podobně bývá otevřený cachovací software webových aplikací. Jen v Česku jej najdete na 614 IP adresách. Stává se také, že se chybou konfigurace otevře do internetu třeba celá databáze elektronického obchodu včetně osobních údajů, informací o klientech a podobně.

V posledním roce je velmi aktivní také malware Ferret a Madness, který je určen pro Windows XP, 7 a 8. Stroje pak byly zneužívány pro DDoS útoky do jiných zemí. Zajímavé je, že tyto malware jsou známé už od roku 2013 a otázkou tedy je, jak může dva roky starý malware ještě napadat uživatelské počítače. Ferret napadl v Česku více než 60 tisíc strojů, Madness více než 70 tisíc.

Dalším rozšířeným problémem byly phishingové maily, které se tvářily jako zpráva od společnosti DHL. V příloze byl opět trojský kůň. Existuje několik desítek variant tohoto viru, které se do počítače stahují z 395 adres v 18 zemích. Většina těchto variant byla dávno známá, ale CSIRT tým objevil i několik nových kousků, které předal antivirovým společnostem.

CSIRT tým spolupracuje i s policií, se kterou v minulosti řešil například několik phishingových stránek, malware na doménách mimo Česko a také podvodné aplikace pro Android. Byli jsme také požádáni o spolupráci při řešení obchodování s databázemi platebních karet. Celkem se obchodovalo z 28 domén v 7 zemích. Nepřekvapí vás, že většina mířila do Ruska. Obchody měly navíc tuhý kořínek, protože jakmile byla zablokována problémová IP adresa, doména okamžitě mířila jinam.

Mezi národními CSIRT/CERT týmy probíhá čilá komunikace. Například rumunský tým předal tomu českému seznam routerů s možností jednoduchého přihlašování, tchajwanský upozornil na IP adresy komunikující s botnetem a Švýcaři předali informace o neznámém trojském koni s 1200 IP adresami v Česku. Nyní sledujeme také velký útok na banku v Gruzii, který se navíc postupně rozšiřuje na další zdroje. Možná jde o něco podobného, co jsme v Česku pozorovali před dvěma lety.

CZ.NIC také provozuje službu Malicious Domain Manager (MDM), která automaticky zasílá držitelům informace o problémech na jejich doméně. Hlásí například, že se doména objevila v některé databázi problémových adres. Službu provozujeme čtyři roky a za tu dobu jsme poslali přibližně sto tisíc hlášení na různá URL.

SSL na hostingu po bezpečnostních kalamitách

Tomáš Hála z ACTIVE 24 hovořil o současném stavu šifrování po kauze Prism. Ta spustila dříve nebývalý zájem veřejnosti o možnosti odposlouchávání komunikace na internetu a o šifrování. Samozřejmě tu různé možnosti odposlechu byly vždycky, ale do té doby to širokou veřejnost moc nezajímalo. Poté vyšlo RFC 7258, které říká, že i odposlech komunikace je útok, proto by se u každého nově navrhovaného protokolu měly už v návrhu řešit a omezovat možnosti jeho odposlechu. Dalším milníkem byl konec podpory Windows XP, který významně zrychlil nasazování SNI.

Velmi závažnou událostí bylo objevení chyby Heartbleed v dubnu 2014. Problém je, že ta chyba existovala už dva roky a nikdo neví, zda nebyla někým už dlouho zneužívána. Zpětně to zjistit prakticky nelze. Kromě záplat bylo také potřeba vyměnit SSL certifikáty i s klíčem, protože právě privátní klíče mohly uniknout. Vznikl také fork OpenSSL nazvaný LibreSSL. Přineslo to také jeden trend: chyby jsou oznamovány s velkou pompou, musí mít vlastní logo a webovou stránku.

Další zajímavou událostí bylo v polovině roku 2014 oznámení Google, že bude stránky na HTTPS mírně zvýhodňovat ve výsledcích vyhledávání. V říjnu 2014 pak přišel útok nazvaný Poodle, který ukončil život starého protokolu SSLv3. Nejednalo se o implementační problém, ale o chybu v samotném principu, proto svět SSLv3 opustil.

Na začátku letošního roku se objevil další útok na SSL zvaný Freak, později pak ještě další s názvem Logjam. Druhá jmenovaná se netýkají zdaleka jen webu, ale i VPN, e-mailové komunikace a dalších protokolů, varoval Hála.

Tomáš Hála pak popsal, jak na tyto problémy reaguje webhostingová firma ACTIVE 24. Optimalizuje konfiguraci šifer podle SSL Labs, pravidelně skenuje celý svůj IP rozsah, rozšiřuje HTTPS, nasazuje HSTS a SPDY a testuje DANE a Public Key Pinning. Bohužel DANE chybí rozšíření v prohlížečích, u Public Key Pinning je situace lepší.

Společnost připravuje aktivní HTTPS ve výchozím stavu u všech hostovaných webů. Nabídneme také instalaci a provoz SSL zcela zdarma a samoobslužnou instalaci certifikátů, řekl Hála. Zároveň budou všechny zákaznické weby podporovat SPDY a později HTTP/2.0 a SSL bude dostupné i na IPv6. Tomu zatím nebránila technická překážka, ale je potřeba vše zavést do našich interních systémů.

Zpracování informací o útocích z veřejných zdrojů

Robert Šefr z CSIRT.CZ si na začátku přednášky položil otázku, zda vůbec dochází v Česku k bezpečnostním incidentům. Ano, ale otázkou je, zda je odhalí. Ne každý má dostatečný monitoring sítě, aby si takového incidentu všiml. Na mnoho problémů se přijde až později, když oběť ohlásí například útok z jiné sítě.

Mnohem lepší je z tohoto hlediska proaktivní přístup, kdy se sbírají a vyhodnocují zdroje informací o hrozbách a sdílí mezi různými organizacemi. Existuje například mnoho projektů, které ukazují na problémové IP adresy a domény. Mezi ne patří například Shadowserver, Abuse.ch, Clean MX, Phishtank a podobně. Dalším je například projekt Turris Greylist, který vzniká na základě analýzy logů z bezpečnostních sond v routerech Turris.

Potíž všech těchto zdrojů je ale v různých formátech dat: csv, xml, json, stix nebo openioc. Podobně i metody doručení informací. Někdo nabízí soubory přes HTTP, někdo rozesílá informace mailem, někdo používá ke streamování API a je v tom zmatek. Není pak jednoduché posbírat informace na jedno místo a vyhodnotit je. Navíc každý ze zmíněných projektů se zabývá jiným druhem hrozeb – některé sledují hostování malware, jiné hodnotí nebezpečné weby, další pak označují IP adresy řídicích strojů botnetů. Nestačí jen vědět, že konkrétní adresa je problematická. Musíte vědět, jaké nebezpečí vám od ní může plynout.

Problém vyhodnocování zdrojů není nový, zabývá se jím několik různých projektů: Abusehelper, Megatron, Collective Intelligence Framework nebo třeba IntelMQ. Všechny ty projekty jsme si prošli a vyzkoušeli, zda jsou pro nás dostatečně použitelné a zda si je můžeme přizpůsobovat svým potřebám. Občas to bylo velmi složité a zmatené. Nakonec se tým CSIRT.CZ rozhodl používat IntelMQ, což je projekt národních CSIRTů.

Jedná se o systém samostatných komponent, které stahují údaje z různých zdrojů, převádějí je do jednotného formátu a doplňují je o další informace. K problémové adrese je možné třeba doplnit geolokaci a rozlišit, zda IP adresa pochází z Uruguaye nebo z Česka. Výsledkem pak budou vyfiltrované bezpečnostní notifikace, které jsou automaticky zasílané na mail vlastníka IP adresy.

Dále budou data využívána jako podklady pro analýzu dat z Turrisu. Už teď Turris s některými zdroji data pracuje, my chceme databázi problémových IP adres rozšířit a sjednotit. Pokud se například uživatel s nakaženým počítačem připojí do sítě s Turrisem, router už bude mít informace o této hrozbě a dokáže ji odfiltrovat, případně o tom informovat. Informace pak budou vizualizovány pomocí otevřených nástrojů jako Elasticsearch, Ligstash a Kibana.

Projekt Turris v roce 2015

Bedřich Košata z CZ.NIC shrnul současný stav projektu Turris, jehož hlavním cílem je sbírat data bezpečnostního charakteru z koncových sítí. Na začátku roku měl být distribuován Turris 1.1, ke kterému měl být doplněn také ADSL model s názvem SMRT. Bohužel právě ten může za zdržení distribuce nových routerů. Testy modemu vypadaly dobře, ale při ostrém nasazení v síti O2 se ukázaly problémy s výkonem. Nemohli jsme garantovat plnou rychlost. Problémem je použitý čip, který nepatří k běžně používaným ADSL čipům v komerčně dostupných modemech. Většina s výrobců se s vámi nebaví, pokud neodeberete alespoň stovky tisíc kusů. Proto je náš výběr omezený.

Nakonec tedy vývojáři došlo k rozhodnutí projekt SMRT zastavit a začít na něm pracovat znovu a lépe. Začneme tedy distribuovat Turris 1.1 bez modemu. Během prázdnin by měly být routery mezi uživateli. SMRT je tedy ve fázi klinické smrti, ale podle Košaty je stále zájem podobné zařízení uživatelům dodat, jen teď není jasné, kdy bude a v jaké podobě.

Zároveň s hardware je vyvíjen i operační systém Turris OS, který je postaven na OpenWRT. V nejbližší době dojde k přechodu na souborový systém JFFS2 a UBIFS, což by s vylepšeným vícebitovým ECC mělo vést k větší stabilitě.

Nejdůležitější funkci routeru je bezpečnostní analýza, která pracuje na mnoha různých úrovních. Je například sledováno spojení s podezřelými IP adresami, které jsou týmem CSIRT.CZ hlášeny jako problémové – běží na nich například řídicí servery botnetu. Stejně tak sledujeme, zda se nějaké zařízení nesnaží komunikovat s těmito adresami zpětně – odhalili jsme tak už několik napadených počítačů v síti uživatelů.

Detailněji je sledován i specifický provoz, například SMTP nebo SIP. Uživatel například obvykle využívá SMTP server svého poskytovatele. Jakmile se snaží kontaktovat stovky SMTP serverů, ukazuje to na nákazu snažící se rozesílat ve velkém spam.

Zadní strana nové verze je stejná, šest LAN, tři anténní konektory a USB 2.0

Aby bylo možné zjistit podrobnější informace, běží na Turrisu několik různých honeypotů. Jedním z nich je například SSH honeypot, který dovolí útočníkovi přihlášení do virtuálního prostředí a zadávání příkazů. Z logů je pak možné sledovat, co útočník dělal, co stahoval a o co se snažil. Tento honeypot neběží uvnitř routeru, ale provoz je z portu 22 přesměrován do CZ.NIC. Obcházíme tím riziko skutečného útoku, kdy by se útočníkovi podařilo z honeypotu vylámat do reálného systému.

Původně se CZ.NIC nechtěl zabývat ničím tak zastaralým jako je telnet, ale ukazuje se, že se útočníci stále snaží na port 23 připojit. Vytvořili jsme proto miniaturní honeypot, který dovoluje simulovat proces přihlášení. Poté nám posílá jméno a heslo, které útočník zkoušel. Nejčastěji jsou samozřejmě zkoušeny kombinace jako root/root, admin/admin nebo root/12345. Používají se ale i složitější kombinace, podle kterých je možné najít souvislost mezi různými útoky. Používá se konkrétní slovník, který dokola využívá sofistikovaná hesla z jednoho slovníku. Takto byl objeven problém botnet v routerech společnosti Asus.

Na přelomu let 2015 a 2016 by měl být k dispozici komerční router Turris Lite. Stále chceme zůstat maximálně otevření, ale už půjde o zařízení, které si koupíte. Mělo by jít o nejvýkonnější domácí router na trhu. Vzhledem k současné situaci to asi nebude problém, řekl s humorem Košata.

Konkrétní detaily ještě nejsou hotové, ale už teď je jasné, že uvnitř poběží dvoujádrový ARM Cortex A9 taktovaný na více než 1 GHz, 5+1 gigabit ethernet, SFP pro optickou konektivitu, dvě USB 3.0, tři miniPCIe sloty (jeden s možností mSATA) a nejméně 256 MB RAM a 2GB eMMC flash paměť.

(Foto: Igor Kytka a Jiří Průša, CZ.NIC)

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 »