Naše úsilí o připojení se zaměřuje na rozšíření přístupu k internetu a jeho přijetí po celém světě. Patří sem naše práce na technologiích, jako je Terragraph, naše spolupráce s mobilními operátory na úsilí o rozšíření přístupu na venkově, naše práce v rámci projektu Telecom Infra a programy, jako je Free Basics. Jak jsme pokračovali v práci na Free Basics, naslouchali jsme zpětné vazbě a doporučením občanské společnosti a dalších zúčastněných stran. Vyvinuli jsme program Discover speciálně proto, abychom se těmito doporučeními zabývali a zapracovali je do nového produktu, který podporuje konektivitu. Dnes Facebook Connectivity a naši partneři ze společností Bitel, Claro, Entel a Movistar zahajují zkušební provoz služby Discover v Peru.
Poskytování této služby a zároveň zajištění bezpečnosti lidí před potenciálními bezpečnostními riziky bylo náročnou technickou výzvou. Chtěli jsme vyvinout model, který by nám umožnil bezpečně prezentovat webové stránky ze všech dostupných domén včetně jejich zdrojů (skripty, média, styly atd.). Níže vás seznámíme s modelem, který jsme vytvořili, s jedinečnými volbami architektury, které jsme na této cestě učinili, a s kroky, které jsme podnikli ke zmírnění rizik.
- Kde jsme začali
- Počáteční architektura
- Návrh domény
- Cookies
- Vylepšení toho, co jsme vytvořili
- Vylepšení architektury v nástroji Discover
- JavaScript a fixace cookies
- Řešení dvou rámců
- Vnitřní rámec
- Vnější rámec
- Interakce se stránkou
- Asynchronní fixace souborů cookie
- Clickjacking
- Phishing
- Soubory cookie na straně klienta
- Protokol Bootstrap
- S protokolem localStorage
- Bez protokolu localStorage
Kde jsme začali
Pro Free Basics bylo naším úkolem najít způsob, jak poskytnout bezplatnou službu lidem, kteří používají mobilní web, a to i na funkčních telefonech bez podpory aplikací třetích stran. Službu mohli poskytovat partneři mobilních operátorů, ale omezení sítě a zařízení brány znamenala, že bezplatný mohl být pouze provoz do určitých destinací (obvykle rozsah IP adres nebo seznam doménových jmen). S více než 100 partnery po celém světě a s časem a obtížemi spojenými se změnou konfigurace síťových zařízení operátorů jsme si uvědomili, že musíme přijít s novým přístupem.
Tento nový přístup vyžadoval, abychom nejprve vytvořili webovou proxy službu, kde by operátor mohl službu zdarma zpřístupnit na jediné doméně: freebasics.com. Odtud bychom jménem uživatele načítali webové stránky a doručovali je do jeho zařízení. I v moderních prohlížečích existují určité problémy s architekturou webového proxy serveru. Na webu jsou klienti schopni vyhodnocovat bezpečnostní hlavičky HTTP, jako jsou CORS (cross-origin resource sharing) a CSP (Content Security Policy), a využívat soubory cookie přímo z webu. V konfiguraci proxy serveru však klient komunikuje s proxy serverem a proxy server funguje jako klient webu. Prokládání webových stránek třetích stran prostřednictvím jediného jmenného prostoru porušuje některé předpoklady týkající se způsobu ukládání souborů cookie, rozsahu přístupu skriptů ke čtení nebo úpravám obsahu a způsobu vyhodnocování CORS a CSP.
Pro řešení těchto obav jsme zpočátku zavedli některá přímá omezení, včetně toho, které weby lze navštěvovat pomocí Free Basics, a nemožnosti spouštět skripty. Druhé jmenované se postupem času stalo větším problémem, protože mnoho webových stránek, včetně mobilních, začalo spoléhat na JavaScript pro kritické funkce, včetně vykreslování obsahu.
Počáteční architektura
Abychom se přizpůsobili omezené funkčnosti mnoha bran mobilních operátorů, zvažovali jsme alternativní architektury, včetně:
- Kooperativního řešení, kdy webové stránky mohou přidělit subdoménu (např,
free.example.com
) a přeložit ji do našeho IP prostoru pro operátory, aby ji mohli bezplatně zpřístupnit uživateli.
Toto řešení mělo klady:
- umožňovalo přímou end-to-end komunikaci mezi klientem a serverem.
- Vyžadovalo minimální zásahy na straně proxy serveru.
Mělo však i některé nevýhody:
- Stránky se musely k tomuto schématu přihlásit, což majitelům stránek přinášelo další technické náklady.
- Prohlížeče by si musely vyžádat konkrétní doménu prostřednictvím SNI (Server Name Indication), aby proxy server věděl, kam se má připojit. Podpora SNI však není univerzální, což toto řešení učinilo méně schůdným.
- Pokud by účastníci omylem brouzdali přímo na
example.com
, a ne na subdoménufree.example.com
, byli by zpoplatněni – a nemuseli by být nutně přesměrováni na subdoménu, pokud by provozovatel neimplementoval nějakou další logiku.
- Zapouzdření IPv4 v IPv6, kdy můžeme zapouzdřit celý prostor IPv4 do jediné volné datové podsítě IPv6. Přizpůsobený DNS resolver pak rekurzivně resolvuje IPv4 a odpovídá zapouzdřenými odpověďmi IPv6.
Toto řešení mělo také klady:
- Nevyžadovalo spolupráci majitele webu.
- Nebylo potřeba SNI pro překlad vzdálené IP.
A nevýhody:
- Prohlížeče by viděly doménu
www.example.com.freebasics.com
, ale certifikátwww.example.com
by vedl k chybě. - Takto podporovalo IPv6 jen několik bran operátorů.
- Ještě méně zařízení podporovalo IPv6, zejména starší verze OS.
Žádné z těchto řešení nebylo životaschopné. Nakonec jsme se rozhodli, že nejlepší možnou architekturou bude origin collapsing, kdy náš proxy server běží v rámci jediného jmenného prostoru domény origin collapsed pod freebasics.com
. Provozovatelé pak mohou snadněji povolovat přenosy do tohoto cíle a udržovat své konfigurace jednoduché. Každý origin třetí strany je zakódován v subdoméně, takže můžeme zaručit, že překlad jmen bude vždy směřovat provoz na volnou IP adresu.
Například:
https://example.com/path/?query=value#anchor
Je přepsáno na:
https://https-example-com.0.freebasics.com/path/?query=value#anchor
Na straně serveru existuje rozsáhlá logika, která zajišťuje správnou transformaci odkazů a hrefů. Stejná logika pomáhá zajistit, aby i weby pouze s protokolem HTTP byly bezpečně doručeny přes protokol HTTPS na serveru Free Basics mezi klientem a proxy serverem. Toto schéma přepisování adres URL nám umožňuje používat jediný jmenný prostor a certifikát TLS namísto vyžadování samostatného certifikátu pro každou subdoménu na internetu.
Všechny internetové počátky se stávají sourozenci pod 0.freebasics.com
, což vyvolává určité bezpečnostní úvahy. Nemohli jsme využít přidání domény do seznamu veřejných přípon, protože bychom museli pro každý původ vydat jiný soubor cookie, což by nakonec překročilo limity souborů cookie prohlížeče.
Cookies
Na rozdíl od webových klientů, kteří mohou využívat soubory cookie přímo z webu, vyžaduje služba proxy odlišné nastavení. Free Basics ukládá uživatelské soubory cookie na straně serveru z několika důvodů:
- Mobilní prohlížeče nižší úrovně mají často omezenou podporu souborů cookie. Pokud vydáme byť jen jeden soubor cookie pro každý web pod naší doménou proxy serveru, mohli bychom být omezeni na nastavení pouhých desítek souborů cookie. Pokud by společnost Free Basics nastavila soubory cookie na straně klienta pro každý web pod
0.freebasics.com
, starší prohlížeče by rychle narazily na limity pro místní ukládání souborů cookie – a dokonce i moderní prohlížeče by dosáhly limitu na doménu. - Omezení jmenného prostoru domény, které jsme museli implementovat, také vylučuje použití sourozeneckých a hierarchických souborů cookie. Například soubor cookie nastavený na libovolné subdoméně na adrese
.example.com
by byl normálně čitelný na libovolné jiné subdoméně. Jinými slovy, pokuda.example.com
nastaví soubor cookie na.example.com
, pak by jejb.example.com
měla být schopna přečíst. V případě Free Basics bya-example-com.0.freebasics.com
nastavoval soubor cookie naexample.com.0.freebasics.com
, což není podle normy povoleno. Protože to nefunguje, ostatní původy, napříkladb-example-com.0.freebasics.com
, by neměly přístup k souborům cookie nastaveným pro jejich nadřazenou doménu.
Aby služba proxy mohla přistupovat k této nádobě se soubory cookie na straně serveru, využívá Free Basics dva soubory cookie na straně klienta:
- Soubor cookie
datr
, identifikátor prohlížeče používaný pro účely integrity webu. - The
ick
(internet cookie key), který obsahuje kryptografický klíč používaný k zašifrování cookie jar na straně serveru. Protože je tento klíč uložen pouze na straně klienta, nemůže být nádoba se soubory cookie na straně serveru dešifrována společností Free Basics, pokud uživatel službu nevyužívá.
Abychom pomohli chránit soukromí a bezpečnost uživatelů při ukládání jejich souborů cookie do nádoby se soubory cookie na straně serveru, zajistíme, aby:
- Soubory cookie na straně serveru byly šifrovány pomocí klíče
ick
, který je uložen pouze na straně klienta. - Když klient poskytne
ick
, server jej při každém požadavku zapomene, aniž by byl kdy zaznamenán. - Obě cookie na straně klienta označíme jako
Secure
aHttpOnly
. - Index souboru cookie hashujeme pomocí klíče na straně klienta, takže soubor cookie není zpětně dohledatelný, když klíč není přítomen.
Povolením spouštění skriptů riskujeme fixaci souborů cookie na straně serveru. Abychom tomu zabránili, vylučujeme použití JavaScriptu z Free Basics. Navíc, přestože součástí Free Basics může být jakákoli webová stránka, prověřujeme každou stránku individuálně z hlediska možných vektorů zneužití bez ohledu na její obsah.
Vylepšení toho, co jsme vytvořili
Pro podporu modelu sloužícího jakékoli webové stránce s možností bezpečnějšího spouštění skriptů jsme museli výrazně přehodnotit naši architekturu, abychom zabránili hrozbám, jako je možnost skriptů číst nebo fixovat soubory cookie uživatele. JavaScript je velmi obtížné analyzovat a zabránit spuštění nechtěného kódu.
Pro příklad uvádíme několik způsobů, jak by útočník mohl vložit kód, který bychom museli být schopni filtrovat:
setTimeout();location = ' javascript:alert(1) <!--';location = 'javascript\n:alert(1) <!--';location = '\x01javascript:alert(1) <!--';var location = 'javascript:alert(1)';for(location in {'javascript:alert(1)':0}); = 'javascript:alert(1)';location.totally_not_assign=location.assign;location.totally_not_assign('javascript:alert(1)');location] = 'javascript:alert(1)';Reflect.set(location, 'href', 'javascript:alert(1)')new Proxy(location, {}).href = 'javascript:alert(1)'Object.assign(window, {location: 'javascript:alert(1)'});Object.assign(location, {href: 'javascript:alert(1)'});location.hash = '#%0a alert(1)';location.protocol = 'javascript:';
Model, který jsme vymysleli, rozšířil návrh Free Basics, ale také chrání soubor cookie, který uchovává šifrovací klíč, před přepsáním skripty. Používáme vnější rámec, kterému důvěřujeme a který potvrzuje, že s vnitřním rámcem, který prezentuje obsah třetích stran, nebylo manipulováno. Následující část podrobně ukazuje, jak zmírňujeme fixaci relace a další útoky, jako je phishing a clickjacking. Vyložíme metodu, jak bezpečně zobrazit obsah třetích stran a zároveň umožnit spuštění JavaScriptu.
Vylepšení architektury v nástroji Discover
Odkazy na doménu se v tomto bodě změní na naši novou doménu, podobně původem zhroucenou discoverapp.com
.
Při povolování JavaScriptu z webů třetích stran jsme museli uznat, že to umožňuje určité vektory, na které jsme se museli připravit, protože skripty mohou měnit a přepisovat odkazy, přistupovat k libovolné části DOM a v nejhorším případě fixovat cookies na straně klienta.
Řešení, které jsme vymysleli, muselo řešit fixaci souborů cookie, takže namísto snahy analyzovat a blokovat určitá volání skriptů jsme se rozhodli detekovat je v okamžiku, kdy k nim dochází, a učinit je nepoužitelnými. Toho dosáhneme následujícím způsobem:
- Při registraci vygenerujeme nový, bezpečný náhodný soubor
ick
. - Soubor
ick
odešleme prohlížeči jako souborHttpOnly
cookie. - Poté vytvoříme HMAC hodnotu nazvanou
ickt
z digestuick
adatr
(abychom se vyhnuli fixaci obou) a uložíme kopiiickt
na klientovi na místo vlocalStorage
, kam potenciální útočník nemůže zapisovat. Námi používané umístění jehttps://www.0.discoverapp.com
, které nikdy neslouží pro obsah třetích stran. Vzhledem k tomu, že tento původ je sourozencem všech původů třetích stran, nemůže dojít ke snížení domény nebo jakémukoli jinému druhu modifikace domény a původ je považován za důvěryhodný. - Vkládáme
ickt
, odvozený z cookieick
viděného v požadavku, dovnitř HTML v každé odpovědi proxy třetí strany. - Při načtení stránky porovnáme vložený
ickt
s důvěryhodnýmickt
pomocíwindow.postMessage()
a v případě neshody zrušíme platnost relace odstraněním souborů cookiedatr
aick
. - Zabráníme interakci uživatele se stránkou, dokud tento proces nedokončíme.
Jako další ochranu nastavíme nový soubor cookie datr
, pokud zjistíme více souborů cookie na stejném místě, a vložíme časové razítko, abychom mohli vždy použít ten nejnovější.
Řešení dvou rámců
Pro ověření potřebujeme způsob, jak se stránka třetí strany může dotazovat na hodnotu ickt
a ověřit ji. Toho dosáhneme vložením stránky třetí strany do <iframe>
na stránce v zabezpečeném původu a injektováním kusu JavaScriptu do stránky třetí strany. Vytvoříme zabezpečený vnější rámec a vnitřní rámec třetí strany.
Vnitřní rámec
Vnitřním rámcem injektujeme skript do každé proxované stránky, kterou obsluhujeme. Spolu s ním injektujeme také hodnotu ickt
vypočítanou z ick
viděnou v požadavku. Chování vnitřního rámce je následující:
- Kontrola s vnějším rámcem:
-
postMessage
nahoru sickt
vloženým do stránky. - Čekat.
- Pokud skript dostane potvrzení od zabezpečeného původu, necháme uživatele se stránkou interagovat.
- Pokud skript čeká příliš dlouho nebo dostane odpověď od neočekávaného původu, přesuneme rámec na chybovou obrazovku bez obsahu třetí strany (naše stránka „Oops“), protože je možné, že vnější rámec buď neexistuje, nebo je jiný, než vnitřní rámec očekává.
-
- Kontrola pomocí
parent
:-
postMessage
naparent
. - Čekat.
- Pokud skript dostane odpověď s
source===parent
a původem pod.0.discoverapp.com
, bude pokračovat. - Pokud skript čeká příliš dlouho nebo dostane odpověď od neočekávaného původu, přejdeme na stránku „Oops“.
-
Několik poznámek k vnitřnímu rámci:
- Potenciální útočníci by se i v případě jeho obejití mohli fixovat pouze na původ, na kterém mohou dosáhnout spuštění kódu, takže vektory fixace cookies by byly zbytečné.
- Předpokládáme, že neškodný původ nebude úmyslně obcházet protokol vnitřního a vnějšího zasílání zpráv.
Vnější rámec
Vnější rámec je tu proto, aby potvrdil, že vnitřní rámec je konzistentní:
- Ujišťujeme se, že vnější rámec je vždy horním rámcem s JavaScriptem a
X-Frame-Options: DENY
. - Počkáme na
postMessage
. - Pokud vnější rámec obdrží zprávu:
- Je od původu vnitřního rámce?
- Pokud ano, ohlásí správnou hodnotu
ickt
?- Pokud ano, pošleme potvrzovací zprávu.
- Pokud ne, smažeme relaci, odstraníme všechny soubory cookie a přejdeme na bezpečný původ.
- Pokud vnější rámec neobdrží zprávu po dobu několika sekund nebo pokud dílčí rámec není nejvyšším vnitřním rámcem, odstraníme umístění z adresního řádku bezpečného rámce.
Interakce se stránkou
Aby se předešlo závodním podmínkám, kdy by osoba mohla zadat heslo pod zafixovaným souborem cookie dříve, než vnitřní rámec dokončí ověření, je důležité zabránit interakci osob se stránkou před dokončením ověřovací sekvence vnitřního rámce.
Aby se tomu zabránilo, server přidává style="display:none"
do prvku <html>
každé stránky. Vnitřní rámec jej odstraní, jakmile obdrží potvrzení vnějšího rámce.
Kód JavaScriptu je stále povolen ke spuštění a zdroje jsou stále načítány. Pokud však osoba nezadala na stránce žádný vstup, prohlížeč neudělá nic, co by potenciální útočník nemohl udělat pouhou návštěvou webu – pokud tedy web již není zranitelný vůči podvržení požadavku (CSRF).
Při volbě tohoto řešení jsme pak museli řešit další možné výsledky, konkrétně:
- Asynchronní fixace souborů cookie.
- Clickjacking kvůli rámování.
- Phishing vydávající se za doménu Discover.
Do této chvíle ochrana, kterou jsme implementovali, počítala se synchronní fixací, ale může k ní dojít i asynchronně. Abychom tomu zabránili, použijeme klasickou metodu prevence CSRF. Požadujeme, aby příkazy POST nesly parametr dotazu s datr
, který je vidět při načítání stránky. Parametr dotazu pak porovnáme s cookie datr
viděným v požadavku. Pokud se neshodují, požadavek nevyplníme.
Abychom zabránili úniku datr
, vložíme zašifrovanou verzi datr
dovnitř vnitřního rámce a zajistíme, aby byl tento parametr dotazu přidán ke každému objektu <form>
a XHR
. Protože stránka nemůže sama odvodit token datr
, přidaný datr
je ten, který je v daném okamžiku vidět.
U anonymních požadavků vyžadujeme, aby měly také parametr dotazu datr
. Anonymita je zachována, protože ji nepropustíme na web třetí strany – chybí cookie ick
, takže nemůžeme použít cookie jar. V tomto případě však nejsme schopni validovat proti souboru cookie datr
, takže anonymní POSTy lze provádět v rámci zafixovaných relací. Protože jsou však anonymní a chybí jim ick
, nemohou uniknout žádné citlivé informace.
Clickjacking
Pokud web odešle X-Frame-Options: DENY
, nebude se načítat ve vnitřním rámci. Toto záhlaví používají webové stránky, aby zabránily vystavení určitým typům útoků, jako je například clickjacking. Tuto hlavičku z odpovědi HTTP odstraníme, ale požádáme vnitřní rámec, aby pomocí postMessage
ověřil, že parent
je rám okna top
. Pokud ověření selže, přesměrujeme uživatele na stránku „Oops“.
Phishing
„Adresní řádek“, který poskytujeme v zabezpečeném rámu, slouží k tomu, aby byl uživateli vystaven nejvyšší původ vnitřního rámu. Může však být zkopírován phishingovými stránkami, které se vydávají za Discover. Škodlivým odkazům bráníme v navigaci pryč ze služby Discover tím, že pomocí <iframe sandbox>
zabráníme navigaci nahoře. Vnějšímu rámci lze uniknout pouze přímou navigací na jiný web.
Soubor document.cookie
umožňuje JavaScriptu číst a upravovat soubory cookie, které nejsou označeny HttpOnly
. Bezpečná podpora je náročná v systému, který udržuje soubory cookie na serveru.
Přístup k souborům cookie: Když je přijat požadavek, proxy server vyjmenuje všechny soubory cookie, které jsou viditelné pro daný původ. Poté připojí ke stránce s odpovědí užitečné zatížení JSON. Kód na straně klienta je injektován tak, aby shimoval document.cookie
a učinil tyto soubory cookie viditelnými pro ostatní skripty, jako by to byly skutečné soubory cookie na straně klienta.
Modifikace souborů cookie:
Důvěra ve schopnosti prohlížeče CORS by v tomto případě nestačila – origin a.example.com
, který se pokusí nastavit cookie na example.com
, bude prohlížečem zablokován, protože tyto originy jsou sourozenci a nejsou hierarchické.
I přesto, když server obdrží nový soubor cookie nastavený klientem, nemůže bezpečně vynutit, zda je cílová doména povolena; původ pisatele je znám pouze u klienta a ne vždy je zaslán serveru způsobem, kterému můžeme důvěřovat.
Aby server donutil klienta prokázat, že je oprávněn nastavovat soubory cookie na konkrétní doméně, pošle kromě užitečného zatížení JSON také seznam kryptografických tokenů pro každý z původů, u kterých je žádající původ oprávněn nastavovat soubory cookie. Tyto tokeny jsou osoleny hodnotou ick
, takže je nelze přenášet mezi uživateli.
Podložka na straně klienta pro document.cookie
se stará o rozlišení a vložení tokenu do skutečného textu cookie, který je odeslán proxy serveru. Proxy pak může ověřit, že zapisující origin skutečně disponuje tokenem pro zápis do cílové domény cookie, a uloží jej do nádoby cookie na straně serveru a při příštím požadavku na stránku jej opět odešle klientovi.
Protokol Bootstrap
Model obsahuje tři typy originů: portálový origin (Discover portal atd.), zabezpečený origin (vnější rámec) a přepisovací origin (vnitřní rámec). Každý z nich má jinou potřebu:
- Portálový origin vyžaduje
datr
. - Zabezpečený origin vyžaduje
ickt
. - Přepisovací origin vyžaduje
datr
aick
.
S protokolem localStorage
Tady je znázorněn proces zavádění pro většinu moderních mobilních prohlížečů:
Je důležité si uvědomit, že aby se zabránilo reflexi, koncový bod zavádění v bezpečném originu vždy vydává nový ick
a ickt
; ick
nikdy nezávisí na vstupu uživatele. Všimněte si, že protože jsme nastavili domain=.discoverapp.com
na ick
i datr
, jsou k dispozici ve všech typech původu a ickt
je k dispozici pouze v zabezpečeném původu.
Bez protokolu localStorage
Protože některé prohlížeče, například Opera Mini (oblíbená v mnoha zemích, kde Discover působí), nepodporují localStorage
, nemůžeme hodnoty ick
a ickt
uložit. To znamená, že musíme použít jiný protokol:
Rozhodli jsme se oddělit přepisovací původ od zabezpečeného původu, aby nesdílely stejnou příponu hostitele podle veřejného seznamu přípon. Pro ukládání zabezpečené kopie ickt
(jako cookie) používáme www.0.discoverapp.com
a všechny originy třetích stran přesouváme pod 0.i.org
. V dobře ošetřeném prohlížeči nastavení souboru cookie na zabezpečený původ znepřístupní všechny přepisovací původy.
Protože jsou nyní původy oddělené, stává se náš proces zavádění dvoufázovým procesem. Dříve jsme mohli nastavit ick
ve stejném požadavku, který jsme poskytli localStorage
s ickt
. Nyní musíme zavést dva originy, v samostatných požadavcích, aniž bychom otevřeli fixační vektory ick
.
Vyřešíme to tak, že nejprve zavedeme zabezpečený origin se souborem cookie ickt
a uživateli poskytneme šifrovanou verzi ick
s klíčem, který zná pouze proxy server. K zašifrovanému textu ick
je připojena nonce, kterou lze použít k dešifrování daného ick
v přepisovacím originu a nastavení cookie, ale pouze jednou.
Útočník si může vybrat buď:
- Použít nonce k odhalení cookie
ick
. - Předat ji uživateli, aby zafixoval její hodnotu.
V obou případech nemůže útočník současně znát a vnutit uživateli konkrétní hodnotu ick
. Tento proces také synchronizuje datr
mezi původy.
Tato architektura prošla značným interním i externím testováním zabezpečení. Věříme, že jsme vyvinuli návrh, který je dostatečně robustní, aby odolal typům útoků na webové aplikace, s nimiž se setkáváme ve volné přírodě, a bezpečně poskytoval připojení, které je pro mobilní operátory udržitelné. Po spuštění služby Discover v Peru plánujeme zahájit další zkušební provoz služby Discover s partnerskými operátory v řadě dalších zemí, kde jsme prováděli beta testování funkcí produktu, včetně Thajska, Filipín a Iráku. Předpokládáme, že služba Discover bude v těchto dalších zemích spuštěna v nejbližších týdnech, a prozkoumáme další zkoušky, kterých se budou chtít zúčastnit partnerští operátoři.
Rádi bychom poděkovali Berku Demirovi za pomoc při této práci.
Ve snaze o větší srozumitelnost našeho jazyka jsme tento příspěvek upravili tak, aby se slovo „whitelist“ nahradilo slovem „allowlist“.
Ve snaze o větší srozumitelnost našeho jazyka jsme tento příspěvek upravili tak, aby se slovo „whitelist“ nahradilo slovem „allowlist“.