Vårt arbete med uppkoppling fokuserar på att öka tillgången till och användningen av internet runt om i världen. Detta omfattar vårt arbete med teknik som Terragraph, vårt samarbete med mobiloperatörer för att utöka tillgången på landsbygden, vårt arbete som en del av Telecom Infra Project och program som Free Basics. Samtidigt som vi har fortsatt att arbeta med Free Basics har vi lyssnat på feedback och rekommendationer från det civila samhället och andra intressenter. Vi har utvecklat Discover specifikt för att ta itu med och införliva dessa rekommendationer i en ny produkt som stöder uppkoppling. Idag lanserar Facebook Connectivity och våra partner på Bitel, Claro, Entel och Movistar ett försök med Discover i Peru.
Att tillhandahålla den här tjänsten samtidigt som människor skyddas från potentiella säkerhetsrisker var en tuff teknisk utmaning. Vi ville utveckla en modell som skulle göra det möjligt för oss att på ett säkert sätt presentera webbsidor från alla tillgängliga domäner, inklusive deras resurser (skript, media, formatmallar osv.). Nedan går vi igenom den modell vi byggde, de unika arkitekturval vi gjorde på vägen och de åtgärder vi har vidtagit för att minska riskerna.
- Vad vi började
- Främre arkitektur
- Domänutformning
- Cookies
- Förbättra det vi hade byggt
- Arkitekturförbättringar i Discover
- JavaScript och cookiefixering
- Två-frames-lösning
- Inre ram
- Uttre ram
- Sidans interaktion
- Asynkron cookiefixering
- Clickjacking
- Phishing
- Client-side cookies
- Bootstrap-protokollet
- Med localStorage-protokoll
- Och utan localStorage-protokollet
Vad vi började
För Free Basics var vår utmaning att hitta ett sätt att tillhandahålla en kostnadsfri tjänst till personer som använder den mobila webben, även på funktionstelefoner utan stöd för appar från tredje part. Partner till mobiloperatörer kunde tillhandahålla tjänsten, men begränsningar i nätverks- och gatewayutrustning innebar att endast trafik till vissa destinationer (vanligen IP-adressområden eller en lista över domännamn) kunde tillhandahållas kostnadsfritt. Med mer än 100 partners globalt och den tid och svårighet som det innebär att ändra nätverksutrustningen hos operatörerna, insåg vi att vi behövde hitta ett nytt tillvägagångssätt.
Detta nya tillvägagångssätt krävde att vi först byggde en webbaserad proxytjänst där operatören kunde göra tjänsten tillgänglig gratis för en enda domän: freebasics.com. Därifrån skulle vi hämta webbsidor för användarens räkning och leverera dem till deras enhet. Även i moderna webbläsare finns det vissa problem med webbaserade proxyarkitekturer. På webben kan klienterna utvärdera HTTP-säkerhetsrubriker som CORS (cross-origin resource sharing) och CSP (Content Security Policy) och använda cookies direkt från webbplatsen. Men i en proxyserverkonfiguration interagerar klienten med proxyn och proxyn fungerar som en klient till webbplatsen. Om webbplatser från tredje part skickas via ett enda namnområde bryter man mot vissa antaganden om hur kakor lagras, hur mycket tillgång skript har till att läsa eller redigera innehåll och hur CORS och CSP utvärderas.
För att lösa dessa problem införde vi inledningsvis några enkla begränsningar, bland annat vilka webbplatser som kan besökas med Free Basics och oförmågan att köra skript. Det sistnämnda har blivit ett större problem med tiden eftersom många webbplatser, inklusive mobila webbplatser, har börjat förlita sig på JavaScript för kritisk funktionalitet, inklusive innehållsrendering.
Främre arkitektur
Domänutformning
För att tillgodose den begränsade funktionaliteten hos många mobiloperatörers gateways övervägde vi alternativa arkitekturer, bland annat:
- En samarbetslösning där webbplatser kan tilldela en subdomän (t.ex,
free.example.com
) och lösa den till vårt IP-utrymme så att operatörerna kan göra det gratis för användaren.
Denna lösning hade fördelar:
- Den möjliggjorde direkt kommunikation från slut till slut mellan klient och server.
- Den krävde minimala ingrepp på proxysidan.
Den hade dock också vissa nackdelar:
- Sajter måste välja att ansluta sig till detta system, vilket medförde extra tekniska kostnader för webbplatsägarna.
- Browsers måste begära en specifik domän med hjälp av Server Name Indication (SNI), så att proxyn vet var den ska ansluta. Stödet för SNI är dock inte universellt, vilket gjorde denna lösning mindre gångbar.
- Om abonnenter av misstag surfar till
example.com
direkt, i stället för till underdomänenfree.example.com
, skulle de drabbas av avgifter – och de skulle inte nödvändigtvis omdirigeras till underdomänen om inte operatören hade infört någon extra logik.
- IPv4-in-IPv6-kapsling, där vi kan kapsla in hela IPv4-utrymmet i ett enda IPv6-subnät med fria data. En anpassad DNS-resolver löser sedan IPv4 rekursivt och svarar med inkapslade IPv6-svar.
Denna lösning hade också fördelar:
- Det krävdes inget samarbete från webbplatsens ägare.
- Det fanns inget behov av SNI för att lösa upp fjärr-IP.
Och nackdelar:
- Browsers såg
www.example.com.freebasics.com
-domänen, menwww.example.com
-certifikatet resulterade i ett fel. - Bara ett fåtal nätoperatörer hade stöd för IPv6 på det här sättet.
- Ännu färre enheter hade stöd för IPv6, särskilt äldre OS-versioner.
Ingen av dessa var en fungerande lösning. I slutändan bestämde vi oss för att den bästa möjliga arkitekturen skulle vara origin collapsing, där vår proxy körs inom ett enda origin-collapsed domain namespace under freebasics.com
. Operatörerna kan då lättare tillåta trafik till denna destination och hålla sina konfigurationer enkla. Varje ursprung från tredje part kodas i en underdomän, så vi kan garantera att namnupplösningen alltid kommer att leda trafiken till en fri IP.
Till exempel:
https://example.com/path/?query=value#anchor
skrivs om till:
https://https-example-com.0.freebasics.com/path/?query=value#anchor
Det finns en omfattande logik på serversidan för att se till att länkar och hrefs omvandlas på rätt sätt. Samma logik hjälper till att se till att även HTTP-only-webbplatser levereras säkert via HTTPS på Free Basics mellan klienten och proxyn. Detta system för omskrivning av webbadresser gör det möjligt för oss att använda ett enda namnområde och ett enda TLS-certifikat, i stället för att kräva ett separat certifikat för varje underdomän på Internet.
Alla internetursprung blir syskon under 0.freebasics.com
, vilket ger upphov till vissa säkerhetsöverväganden. Vi kunde inte dra nytta av att lägga till domänen i Public Suffix List, eftersom vi skulle behöva utfärda en annan cookie för varje ursprung, vilket så småningom skulle överskrida webbläsarens cookie-gränser.
Cookies
I motsats till webbklienter, som kan använda sig av cookies direkt från webbplatsen, kräver proxytjänsten en annan inställning. Free Basics lagrar användarcookies på serversidan av flera skäl:
- Mobilwebbläsare på lägre nivå har ofta begränsat stöd för cookies. Om vi utfärdar även bara en kaka per webbplats under vår proxydomän kan vi vara begränsade till att ställa in bara tiotals kakor. Om Free Basics skulle ställa in cookies på klientsidan för varje webbplats under
0.freebasics.com
skulle äldre webbläsare snabbt nå gränserna för lokal lagring av cookies – och även moderna webbläsare skulle nå en gräns per domän. - Den begränsning av domännamnsutrymmet som vi behövde genomföra förhindrade också användningen av syskonkakor och hierarkiska cookies. Till exempel skulle en cookie som ställts in på en underdomän på
.example.com
normalt kunna läsas på alla andra underdomäner. Med andra ord, oma.example.com
ställer in en cookie på.example.com
börb.example.com
kunna läsa den. I fallet med Free Basics skullea-example-com.0.freebasics.com
ställa in en cookie påexample.com.0.freebasics.com
, vilket inte är tillåtet enligt standarden. Eftersom det inte fungerar skulle andra ursprung, somb-example-com.0.freebasics.com
, inte kunna få tillgång till cookies som ställts in för deras överordnade domän.
För att låta proxytjänsten få tillgång till denna serverbaserade cookieburk använder Free Basics två cookies på klientsidan:
-
datr
-cookien, en webbläsaridentifierare som används för integritetsändamål på webbplatsen. -
ick
(internet cookie key), som innehåller en kryptografisk nyckel som används för att kryptera den serverbaserade kakburken. Eftersom denna nyckel endast lagras på klientsidan kan server-side cookie jar inte dekrypteras av Free Basics när användaren inte använder tjänsten.
För att hjälpa till att skydda användarnas integritet och säkerhet när de lagrar sina cookies i en server-side cookie jar, ser vi till att:
- Server-side cookies krypteras med en
ick
som endast lagras på klienten. - När klienten tillhandahåller
ick
glöms den bort av servern i varje begäran utan att någonsin loggas. - Vi markerar båda klientsidans cookies som
Secure
ochHttpOnly
. - Vi hashar indexet för en cookie med hjälp av nyckeln på klientsidan så att cookien inte kan spåras tillbaka till användaren när nyckeln inte finns med.
Att låta skript köras riskerar att fixera cookies på serversidan. För att förhindra detta utesluter vi användningen av JavaScript från Free Basics. Även om vilken webbplats som helst kan ingå i Free Basics granskar vi dessutom varje webbplats individuellt för potentiella missbruksvektorer, oavsett innehåll.
Förbättra det vi hade byggt
För att stödja en modell som betjänar vilken webbplats som helst, med möjlighet att köra skript på ett säkrare sätt, behövde vi ompröva vår arkitektur avsevärt för att förhindra hot, till exempel att skript kan antingen läsa eller fixa användarens cookies. JavaScript är extremt svårt att analysera och förhindra att oavsiktlig kod exekveras.
Som ett exempel är här några sätt som en angripare skulle kunna injicera kod som vi skulle behöva kunna filtrera:
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:';
Modellen vi kom fram till utökade Free Basics-designen, men den skyddar också kakan som lagrar krypteringsnyckeln från att skrivas över av skript. Vi använder en yttre ram som vi litar på för att intyga att den inre ramen, som visar innehåll från tredje part, inte manipuleras. I följande avsnitt visas i detalj hur vi begränsar sessionsfixering och andra attacker, t.ex. phishing och clickjacking. Vi presenterar en metod för att på ett säkert sätt visa innehåll från tredje part och samtidigt möjliggöra JavaScript-exekvering.
Arkitekturförbättringar i Discover
Referenser till domänen vid denna tidpunkt kommer att ändras till vår nya domän, en liknande ursprungskollapsad discoverapp.com
.
I samband med att vi har tillåtit JavaScript från webbplatser från tredje part har vi varit tvungna att erkänna att detta möjliggör vissa vektorer som vi behövde förbereda oss för, eftersom skript kan ändra och skriva om länkar, få tillgång till vilken del av DOM som helst och, i värsta fall, fixera cookies på klientsidan.
Lösningen vi kom fram till behövde hantera cookiefixering, så i stället för att försöka analysera och blockera vissa skriptanrop bestämde vi oss för att upptäcka det när det sker och göra det oanvändbart. Detta uppnås på följande sätt:
- När vi registrerar oss genererar vi en ny, säker slumpmässig
ick
. - Vi skickar
ick
till webbläsaren som enHttpOnly
cookie. - Vi HMACar sedan ett värde som kallas
ickt
från en sammanfattning av bådeick
ochdatr
(för att undvika fixering för båda) och lagrar en kopia avickt
på klienten, på en plats ilocalStorage
som en potentiell angripare inte kan skriva till. Den plats vi använder ärhttps://www.0.discoverapp.com
, som aldrig serverar innehåll från tredje part. Eftersom detta ursprung är syskon till alla tredjepartsursprung kan inte domänförlängning eller någon annan typ av domänändring ske, och ursprunget anses vara pålitligt. - Vi bäddar in
ickt
, som härstammar frånick
-cookien som ses i begäran, i HTML i varje tredjeparts proxysvar. - När sidan laddas jämför vi den inbäddade
ickt
med den betroddaickt
med hjälp avwindow.postMessage()
och ogiltigförklarar sessionen om den inte stämmer överens genom att raderadatr
– ochick
-cookies. - Vi förhindrar användarens interaktion med sidan tills denna process är klar.
Som ytterligare skydd ställer vi in en ny datr
-cookie om vi upptäcker flera cookies på samma plats, och bäddar in en tidsstämpel så att vi alltid kan använda den senaste.
Två-frames-lösning
För validering behöver vi ett sätt för en tredjepartssida att fråga efter ickt
-värdet och validera det. Vi gör detta genom att bädda in tredjepartssidan i en <iframe>
i en sida på det säkra ursprunget och injicera en bit JavaScript i tredjepartssidan. Vi bygger en säker yttre ram och en inre ram för tredje part.
Inre ram
I den inre ramen injicerar vi ett skript i varje proxysida vi serverar. Vi injicerar också ickt
-värdet beräknat från ick
som ses i begäran tillsammans med det. Den inre ramens beteende är följande:
- Kontrollera med yttre ram:
-
postMessage
to top medickt
inbäddad i sidan. - Vänta.
- Om skriptet får en bekräftelse från det säkra ursprunget låter vi användaren interagera med sidan.
- Om skriptet väntar för länge eller får ett svar från ett oväntat ursprung, navigerar vi ramen till en felskärm utan innehåll från tredje part (vår ”Oops”-sida), eftersom det är möjligt att den yttre ramen antingen inte finns där eller är annorlunda än vad den inre ramen förväntar sig.
-
- Kontrollera med
parent
:-
postMessage
tillparent
. - Vänta.
- Om skriptet får ett svar med
source===parent
och ursprung under.0.discoverapp.com
kommer det att fortsätta. - Om skriptet väntar för länge, eller om det får ett svar från ett oväntat ursprung, navigerar vi till sidan ”Oops”.
-
Några anteckningar om den inre ramen:
- Även om den kringgås skulle potentiella angripare endast kunna fixera sig på ett ursprung som de kan uppnå kodkörning på, vilket gör kakfixeringsvektorer överflödiga.
- Vi antar att ett välvilligt ursprung inte avsiktligt kommer att kringgå meddelandeteknikprotokollet för inner-outer.
Uttre ram
Den yttre ramen är till för att intyga att den inre ramen är konsekvent:
- Vi ser till att den yttre ramen alltid är den översta ramen med JavaScript och
X-Frame-Options: DENY
. - Vänta på
postMessage
. - Om den yttre ramen tar emot ett meddelande:
- Är det från ett ursprung i den inre ramen?
- Om ja, rapporterar den då det korrekta
ickt
-värdet?- Om ja, skicka ett bekräftelsemeddelande.
- Om nej, radera sessionen, radera alla cookies och navigera till ett säkert ursprung.
- Om den yttre ramen inte tar emot ett meddelande på några sekunder eller om underramen inte är den översta inre ramen, tar vi bort platsen från den säkra ramens adressfält.
Sidans interaktion
För att undvika kapplöpningstillstånd där en person kan skriva in ett lösenord under en fixerad cookie innan den inre ramen har slutfört verifieringen, är det viktigt att förhindra att människor interagerar med sidan innan den inre ramens verifieringssekvens har slutförts.
För att förhindra detta lägger servern till style="display:none"
till <html>
-elementet på varje sida. Den inre ramen tar bort det när den får den yttre ramens bekräftelse.
JavaScript-kod får fortfarande köras och resurser hämtas fortfarande. Men så länge personen inte har angett något på sidan gör webbläsaren ingenting som en potentiell angripare inte skulle kunna göra genom att besöka webbplatsen – såvida inte webbplatsen redan är sårbar för cross-site request forgery (CSRF).
Då vi valde den här lösningen var vi tvungna att ta hänsyn till andra möjliga utfall, särskilt:
- Synkron cookiefixering.
- Clickjacking på grund av framing.
- Phishing som utger sig för att vara Discover-domänen.
Hos de skydd som vi har implementerat har vi fram till nu tagit hänsyn till synkrona fixeringar, men de kan också uppstå asynkront. För att förhindra detta använder vi en klassisk CSRF-preventionsmetod. Vi kräver att POST:er har en frågeparameter med den datr
som ses när sidan laddas. Vi jämför sedan frågeparametern med den datr
cookie som sågs i begäran. Om de inte stämmer överens uppfyller vi inte begäran.
För att undvika datr
-läckage bäddar vi in en krypterad version av datr
i den inre ramen och ser till att denna frågeparameter läggs till i varje <form>
– och XHR
-objekt. Eftersom sidan inte kan härleda datr
-token på egen hand, är den datr
som läggs till den datr
som ses vid den tidpunkten.
För anonyma förfrågningar kräver vi att de också har datr
-frågeparametern. Anonymiteten bevaras eftersom vi inte läcker den till webbplatsen för tredje part – ick
-kakan saknas, så vi kan inte använda kakburken. I det här fallet kan vi dock inte validera mot datr
-cookien, så anonyma POST:er kan göras under fixerade sessioner. Men eftersom de är anonyma och saknar ick
kan ingen känslig information läcka ut.
Clickjacking
När en webbplats skickar X-Frame-Options: DENY
laddas den inte i en inre ram. Den här rubriken används av webbplatser för att förhindra att de utsätts för vissa typer av attacker, t.ex. clickjacking. Vi tar bort den här rubriken från HTTP-svaret men ber den inre ramen att verifiera att parent
är top
-fönsterramen med hjälp av postMessage
. Om valideringen misslyckas navigerar vi användaren till sidan ”Oops”.
Phishing
Det ”adressfält” som vi tillhandahåller i den säkra ramen används för att exponera det översta inre ramens ursprung för användaren. Den kan dock kopieras av nätfiskewebbplatser som utger sig för att vara Discover. Vi förhindrar att skadliga länkar navigerar bort från Discover genom att förhindra toppnavigering med hjälp av <iframe sandbox>
. Den yttre ramen kan endast undkomma genom att direkt navigera till en annan webbplats.
Med document.cookie
kan JavaScript läsa och ändra cookies som inte är markerade HttpOnly
. Att stödja detta på ett säkert sätt är en utmaning i ett system som behåller cookies på servern.
Accessing cookies: När en begäran tas emot räknar proxyn upp alla cookies som är synliga för detta ursprung. Den bifogar sedan en JSON-nyttolast till svarssidan. Kod på klientsidan injiceras för att shim document.cookie
och göra dessa kakor synliga för andra skript, som om de vore riktiga kakor på klientsidan.
Modifiera kakor: Om skript tillåts att godtyckligt ställa in cookies som servern sedan accepterar kan detta leda till fixering, där ursprung evil.com
kan ställa in en känslig cookie på example.com
.
Det räcker inte med att lita på webbläsarens CORS-funktioner i det här fallet – ursprung a.example.com
som försöker ställa in en cookie på example.com
kommer att blockeras av webbläsaren, eftersom dessa ursprung är syskon och inte hierarkiska.
Till och med detta kan servern, när den tar emot en ny cookie som ställts in av klienten, inte på ett säkert sätt tvinga fram om måldomänen är tillåten; författarens ursprung är endast känt på klienten och skickas inte alltid till servern på ett sätt som vi kan lita på.
För att tvinga klienten att bevisa att den är berättigad att ställa in cookies på en viss domän skickar servern, utöver JSON-nyttolasten, en lista med kryptografiska token för vart och ett av de ursprung där det begärande ursprunget är tillåtet att ställa in cookies. Dessa token är saltade med ick
-värdet, så att de inte kan överföras mellan användare.
Klientsidans shim för document.cookie
tar hand om upplösningen och inbäddningen av token i den faktiska cookie-texten som skickas till proxyn. Proxyn kan sedan kontrollera att det skrivande ursprunget verkligen besatt tokenet för att skriva till kakans måldomän och lagrar det i kakburken på serversidan och skickar det till klienten igen nästa gång sidan begärs.
Bootstrap-protokollet
Modellen innehåller tre ursprungstyper: portalursprung (Discover portal etc.), säkert ursprung (yttre ram) och rewrite-ursprung (inre ram). Var och en har olika behov:
- Portalursprung kräver
datr
. - Säkert ursprung kräver
ickt
. - Rewrite origin kräver
datr
ochick
.
Med localStorage-protokoll
Här är en representation av bootstrap-processen för de flesta moderna mobila webbläsare:
Det är viktigt att notera att för att undvika reflektion utfärdar bootstrap-slutpunkten vid det säkra ursprunget alltid en ny ick
och ickt
; ick
är aldrig beroende av användarinmatning. Observera att eftersom vi har satt domain=.discoverapp.com
på både ick
och datr
är de tillgängliga i alla ursprungstyper, och ickt
är endast tillgänglig på det säkra ursprunget.
Och utan localStorage-protokollet
Om vissa webbläsare, t.ex. Opera Mini (populärt i många av de länder där Discover är verksamt), inte har stöd för localStorage
, kan vi inte lagra värdena ick
och ickt
. Detta innebär att vi måste använda ett annat protokoll:
Vi beslutade att separera rewrite origin från secure origin så att de inte delar samma host suffix enligt Public Suffix List. Vi använder www.0.discoverapp.com
för att lagra den säkra kopian av ickt
(som en kaka) och flyttar alla tredjepartsoriginaler till 0.i.org
. I en välskött webbläsare gör inställningen av en cookie på det säkra ursprunget att det blir otillgängligt för alla rewrite origins.
Då ursprungskällorna nu är åtskilda blir vår bootstrap-process en tvåstegsprocess. Tidigare kunde vi ställa in ick
i samma begäran som vi tillhandahåller localStorage
med ickt
. Nu måste vi starta upp två ursprung, i separata förfrågningar, utan att öppna ick
-fixeringsvektorer.
Vi löser detta genom att starta upp det säkra ursprunget med ickt
-cookien först och ge användaren en krypterad version av ick
, med en nyckel som endast proxyn är känd av proxyn. Den krypterade ick
-texten åtföljs av en nonce som kan användas för att dekryptera just ick
i rewrite origin och ställa in en cookie, men bara en gång.
En angripare kan välja att antingen:
- Använda nonce för att avslöja
ick
-cookien. - Ge den till användaren för att fastställa dess värde.
I båda fallen kan angriparen inte samtidigt känna till och tvinga fram ett visst ick
-värde för en användare. Processen synkroniserar också datr
mellan ursprunget.
Denna arkitektur har genomgått omfattande interna och externa säkerhetstester. Vi anser att vi har utvecklat en konstruktion som är tillräckligt robust för att motstå de typer av webbapplikationsattacker som vi ser i naturen och som på ett säkert sätt levererar uppkoppling som är hållbar för mobiloperatörer. Efter lanseringen av Discover i Peru planerar vi att lansera ytterligare Discover-försök med partneroperatörer i ett antal andra länder där vi har betatestat produktfunktioner, bland annat Thailand, Filippinerna och Irak. Vi räknar med att Discover kommer att vara live i dessa ytterligare länder under de kommande veckorna, och vi kommer att undersöka ytterligare försök där partneroperatörer vill delta.
Vi vill tacka Berk Demir för hans hjälp med det här arbetet.
I ett försök att vara mer inkluderande i vårt språkbruk har vi redigerat det här inlägget så att vi har ersatt ”whitelist” med ”allowlist.”