Vores bestræbelser på konnektivitet fokuserer på at udvide internetadgang og udbredelse i hele verden. Dette omfatter vores arbejde med teknologier som Terragraph, vores samarbejde med mobiloperatører om indsatsen for at udvide adgangen i landdistrikterne, vores arbejde som en del af Telecom Infra Project og programmer som Free Basics. I takt med at vi har arbejdet videre med Free Basics, har vi lyttet til feedback og anbefalinger fra civilsamfundet og andre interessenter. Vi har udviklet Discover specifikt for at imødekomme og indarbejde disse anbefalinger i et nyt produkt, der understøtter konnektivitet. I dag lancerer Facebook Connectivity og vores partnere hos Bitel, Claro, Entel og Movistar et forsøg med Discover i Peru.
Det var en hård teknisk udfordring at levere denne tjeneste, samtidig med at folk blev beskyttet mod potentielle sikkerhedsrisici. Vi ønskede at udvikle en model, der ville gøre det muligt for os at præsentere websider fra alle tilgængelige domæner på en sikker måde, herunder deres ressourcer (scripts, medier, stilark osv.). Nedenfor gennemgår vi den model, vi byggede, de unikke arkitekturvalg, vi traf undervejs, og de skridt, vi har taget for at mindske risici.
- Hvor vi startede
- Første arkitektur
- Domænedesign
- Cookies
- Forbedring af det, vi havde bygget
- Arkitekturforbedringer i Discover
- JavaScript og cookiefiksering
- Two-frames-løsning
- Indre ramme
- Overste ramme
- Sideinteraktion
- Asynkron cookiefiksering
- Clickjacking
- Phishing
- Client-side cookies
- Bootstrap-protokol
- Med localStorage-protokol
- Uden localStorage-protokol
Hvor vi startede
For Free Basics var vores udfordring at finde en måde at levere en tjeneste uden omkostninger til folk, der bruger det mobile web, selv på funktionstelefoner uden understøttelse af apps fra tredjeparter. Partnere i mobilselskaber kunne levere tjenesten, men begrænsninger i netværks- og gatewayudstyr betød, at kun trafik til visse destinationer (normalt IP-adresseområder eller en liste over domænenavne) kunne gøres gratis. Med mere end 100 partnere på verdensplan og den tid og de vanskeligheder, der er forbundet med at ændre netværksudstyrskonfigurationer hos operatørerne, indså vi, at vi var nødt til at finde en ny tilgang.
Denne nye tilgang krævede, at vi først opbyggede en webbaseret proxytjeneste, hvor operatøren kunne stille tjenesten gratis til rådighed for et enkelt domæne: freebasics.com. Herfra ville vi hente websider på vegne af brugeren og levere dem til deres enhed. Selv i moderne browsere er der visse problemer med webbaserede proxy-arkitekturer. På nettet er klienterne i stand til at vurdere HTTP-sikkerhedsoverskrifter som CORS (cross-origin resource sharing) og CSP (Content Security Policy) og anvende cookies direkte fra webstedet. Men i en proxyserverkonfiguration interagerer klienten med proxyen, og proxyen fungerer som en klient for webstedet. Proxying af websteder fra tredjeparter gennem et enkelt navnerum krænker nogle antagelser om, hvordan cookies lagres, hvor meget adgang scripts har til at læse eller redigere indhold, og hvordan COS og CSP evalueres.
For at imødekomme disse bekymringer indførte vi i første omgang nogle enkle begrænsninger, herunder hvilke websteder der kunne besøges med Free Basics, og at det ikke var muligt at køre scripts. Sidstnævnte er blevet et større problem med tiden, da mange websteder, herunder mobilwebsteder, er begyndt at være afhængige af JavaScript til kritiske funktioner, herunder gengivelse af indhold.
Første arkitektur
Domænedesign
For at imødekomme den begrænsede funktionalitet hos mange mobiloperatørers gateways overvejede vi alternative arkitekturer, herunder:
- En samarbejdsløsning, hvor websteder kan tildele et subdomæne (f.eks,
free.example.com
) og opløser det til vores IP-område, så operatørerne kan gøre det gratis for brugeren.
Denne løsning havde fordele:
- Det gav mulighed for direkte end-to-end-kommunikation mellem klient og server.
- Det krævede minimal indgriben på proxysiden.
Den havde dog også nogle ulemper:
- Stederne skulle vælge at deltage i denne ordning, hvilket medførte ekstra tekniske omkostninger for webstedsejerne.
- Browsere skulle anmode om et bestemt domæne via Server Name Indication (SNI), så proxyen vidste, hvor den skulle oprette forbindelse. Understøttelse af SNI er imidlertid ikke universel, hvilket gjorde denne løsning mindre levedygtig.
- Hvis abonnenter ved et uheld surfede direkte til
example.com
i stedet for til underdomænetfree.example.com
, ville de pådrage sig gebyrer – og ville ikke nødvendigvis blive omdirigeret til underdomænet, medmindre operatøren havde implementeret en ekstra logik.
- IPv4-i-IPv6-indkapsling, hvor vi kan indkapsle hele IPv4-området i et enkelt frit data-IPv6-subnet. En tilpasset DNS-resolver løser derefter IPv4 rekursivt og svarer med indkapslede IPv6-svar.
Denne løsning havde også fordele:
- Det krævede ikke samarbejde fra webstedsejeren.
- Der var ikke behov for SNI til at opløse fjern-IP’en.
Og ulemper:
- Browsere ville se
www.example.com.freebasics.com
-domænet, menwww.example.com
-certifikatet ville resultere i en fejl. - Kun nogle få carriers gateways understøttede IPv6 på denne måde.
- Et endnu mindre antal enheder understøttede IPv6, især ældre OS-versioner.
Ingen af disse var en brugbar løsning. I sidste ende besluttede vi, at den bedst mulige arkitektur ville være origin collapsing, hvor vores proxy kører inden for et enkelt origin-collapsed domain namespace under freebasics.com
. Operatører kan så lettere tillade trafik til denne destination og holde deres konfigurationer enkle. Hver tredjepartsoprindelse er kodet i et underdomæne, så vi kan garantere, at navneopløsning altid vil lede trafikken til en fri IP.
Til eksempel:
https://example.com/path/?query=value#anchor
omskrevet til:
https://https-example-com.0.freebasics.com/path/?query=value#anchor
Der er omfattende server-side logik på plads for at sikre, at links og hrefs transformeres korrekt. Den samme logik er med til at sikre, at selv HTTP-only-websteder leveres sikkert over HTTPS på Free Basics mellem klienten og proxyen. Denne URL-omskrivningsordning gør det muligt for os at bruge et enkelt navnerum og TLS-certifikat i stedet for at kræve et separat certifikat for hvert enkelt underdomæne på internettet.
Alle internetoprindelser bliver søskende under 0.freebasics.com
, hvilket giver anledning til visse sikkerhedsovervejelser. Vi kunne ikke drage fordel af at tilføje domænet til Public Suffix List, da vi skulle udstede en anden cookie for hver oprindelse, hvilket i sidste ende ville overskride browserens cookie-grænser.
Cookies
I modsætning til webklienter, som kan gøre brug af cookies direkte fra webstedet, kræver proxytjenesten en anden opsætning. Free Basics gemmer brugernes cookies på serversiden af flere grunde:
- Mobilbrowsere på lavere niveau har ofte begrænset cookie-understøttelse. Hvis vi udsteder blot én cookie pr. websted under vores proxy-domæne, kan vi være begrænset til at indstille blot ti cookies. Hvis Free Basics skulle indstille cookies på klientsiden for hvert websted under
0.freebasics.com
, ville ældre browsere hurtigt ramme grænserne for lokal lagring af cookies – og selv moderne browsere ville nå en grænse pr. domæne. - Den begrænsning af domænenavnsrummet, som vi skulle implementere, udelukkede også brugen af søskende og hierarkiske cookies. F.eks. ville en cookie, der blev sat på et underdomæne på
.example.com
, normalt kunne læses på ethvert andet underdomæne. Med andre ord, hvisa.example.com
sætter en cookie på.example.com
, så skulleb.example.com
kunne læse den. I tilfældet med Free Basics villea-example-com.0.freebasics.com
indstille en cookie påexample.com.0.freebasics.com
, hvilket ikke er tilladt i henhold til standarden. Da det ikke virker, ville andre oprindelser, somb-example-com.0.freebasics.com
, ikke kunne få adgang til cookies, der er indstillet for deres overordnede domæne.
For at give proxytjenesten adgang til denne server-side cookiekrukke anvender Free Basics to klient-side cookies:
-
datr
-cookien, en browseridentifikator, der bruges til integritetsformål for webstedet. - Den
ick
(internetcookie-nøgle), som indeholder en kryptografisk nøgle, der bruges til at kryptere server-side cookie jar. Da denne nøgle kun opbevares på klientsiden, kan server-side cookie jar ikke dekrypteres af Free Basics, når brugeren ikke benytter tjenesten.
For at hjælpe med at beskytte brugernes privatliv og sikkerhed, når de gemmer deres cookies i en server-side cookie jar, sørger vi for, at:
- Server-side cookies krypteres med en
ick
, der kun opbevares på klienten. - Når klienten giver
ick
, glemmes den af serveren i hver anmodning uden nogensinde at blive logget. - Vi markerer både klient-side cookies som
Secure
ogHttpOnly
. - Vi hasher indekset for en cookie ved hjælp af klientsidens nøgle, så cookien ikke kan spores tilbage til brugeren, når nøglen ikke er til stede.
Giver man scripts mulighed for at køre, risikerer man, at server-side-cookies bliver fikseret. For at forhindre dette udelukker vi brugen af JavaScript fra Free Basics. Selv om ethvert websted kan være en del af Free Basics, gennemgår vi desuden hvert websted individuelt for potentielle misbrugsvektorer, uanset indhold.
Forbedring af det, vi havde bygget
For at understøtte en model, der betjener ethvert websted, med mulighed for at køre scripts mere sikkert, var vi nødt til at gentænke vores arkitektur betydeligt for at forhindre trusler, såsom scripts, der enten kan læse eller fikserer brugerens cookies. JavaScript er ekstremt vanskeligt at analysere og forhindre, at utilsigtet kode bliver udført.
Som et eksempel er her nogle måder, hvorpå en angriber kunne injicere kode, som vi skulle kunne filtrere:
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:';
Den model, vi fandt frem til, udvidede Free Basics-designet, men den beskytter også den cookie, der lagrer krypteringsnøglen, mod at blive overskrevet af scripts. Vi bruger en ydre ramme, som vi stoler på, til at attestere, at der ikke bliver manipuleret med den indre ramme, som viser indhold fra tredjepart. I det følgende afsnit vises i detaljer, hvordan vi afbøder sessionsfiksering og andre angreb som f.eks. phishing og clickjacking. Vi udlægger en metode til sikkert at servere indhold fra tredjepart, samtidig med at vi muliggør JavaScript-eksekvering.
Arkitekturforbedringer i Discover
Referencer til domænet på dette punkt vil ændre sig til vores nye domæne, et tilsvarende oprindelsessammenfaldet discoverapp.com
.
Ved at tillade JavaScript fra tredjepartswebsteder har vi måttet erkende, at dette muliggør visse vektorer, som vi var nødt til at forberede os på, da scripts kan ændre og omskrive links, få adgang til enhver del af DOM og i værste fald fikserer klient-side-cookies.
Den løsning, vi fandt frem til, skulle tage højde for cookiefiksering, så i stedet for at forsøge at analysere og blokere visse scriptkald besluttede vi at opdage det, når det sker, og gøre det ubrugeligt. Dette opnås ved følgende:
- Ved tilmelding genererer vi en ny, sikker, tilfældig
ick
. - Vi sender
ick
til browseren som enHttpOnly
cookie. - Vi HMAC’er derefter en værdi kaldet
ickt
fra en digest af bådeick
ogdatr
(for at undgå fiksering for begge) og gemmer en kopi afickt
på klienten, på et sted ilocalStorage
, som en potentiel angriber ikke kan skrive til. Den placering, vi bruger, erhttps://www.0.discoverapp.com
, som aldrig serverer indhold fra tredjepart. Da denne oprindelse er søskende til alle tredjepartsoprindelser, kan domænesænkning eller enhver anden form for domænemodifikation ikke finde sted, og oprindelsen anses for at være betroet. - Vi indlejrer
ickt
, der er afledt afick
-cookien, som ses i anmodningen, inde i HTML’en i hvert tredjeparts-proxysvar. - Når siden indlæses, sammenligner vi den indlejrede
ickt
med den betroedeickt
ved hjælp afwindow.postMessage()
og ugyldiggør sessionen, hvis der er en uoverensstemmelse, ved at slettedatr
– ogick
-cookies. - Vi forhindrer brugerens interaktion med siden, indtil denne proces er afsluttet.
Som yderligere beskyttelse indstiller vi en ny datr
-cookie, hvis vi registrerer flere cookies på samme sted, og indlejrer et tidsstempel, så vi altid kan bruge den seneste.
Two-frames-løsning
Til validering har vi brug for en måde, hvorpå en tredjepartsside kan forespørge ickt
-værdien og validere den. Det gør vi ved at indlejre tredjepartssiden i en <iframe>
i en side på den sikre oprindelse og injicere et stykke JavaScript i tredjepartssiden. Vi opbygger en sikker ydre ramme og en indre ramme fra tredjepart.
Indre ramme
I den indre ramme injicerer vi et script i hver proxieside, vi serverer. Vi injicerer også ickt
-værdien, der er beregnet ud fra ick
, som ses i anmodningen, sammen med det. Den indre rammes adfærd er som følger:
- Kontroller med ydre ramme:
-
postMessage
til top medickt
indlejret i siden. - Venter.
- Hvis scriptet får en kvittering fra den sikre oprindelse, lader vi brugeren interagere med siden.
- Hvis scriptet venter for længe eller får et svar fra en uventet oprindelse, navigerer vi rammen til en fejlskærm uden indhold fra tredjepart (vores “Ups”-side), fordi det er muligt, at den ydre ramme enten ikke er der eller er anderledes, end den indre ramme forventer.
-
- Kontroller med
parent
:-
postMessage
tilparent
. - Venter.
- Hvis scriptet får et svar med
source===parent
og oprindelse under.0.discoverapp.com
, fortsætter det. - Hvis scriptet venter for længe, eller får et svar fra en uventet oprindelse, navigerer vi til “Ups”-siden.
-
Nogle bemærkninger om den indre ramme:
- Selv hvis den omgås, vil potentielle angribere kun kunne fiksere på en oprindelse, som de kan opnå kodeudførelse på, hvilket gør cookie-fikseringsvektorer overflødige.
- Vi antager, at en godartet oprindelse ikke bevidst vil omgå den indre-udvendige meddelelsesprotokol.
Overste ramme
Den ydre ramme er der for at attestere, at den indre ramme er konsistent:
- Vi sørger for, at den ydre ramme altid er den øverste ramme med JavaScript og
X-Frame-Options: DENY
. - Venter på
postMessage
. - Hvis den ydre ramme modtager en meddelelse:
- Er den fra en indre rammes oprindelse?
- Hvis ja, rapporterer den så den korrekte
ickt
-værdi?- Hvis ja, så send en bekræftelsesmeddelelse.
- Hvis nej, slet sessionen, slet alle cookies, og naviger til en sikker oprindelse.
- Hvis den ydre ramme ikke modtager en besked i et par sekunder, eller hvis underrammen ikke er den øverste indre ramme, fjerner vi stedet fra den sikre rammes adresselinje.
Sideinteraktion
For at undgå racebetingelser, hvor en person kan indtaste en adgangskode under en fikseret cookie, før den indre ramme har afsluttet verifikationen, er det vigtigt at forhindre, at folk interagerer med siden, før den indre rammes verifikationssekvens er afsluttet.
For at forhindre dette tilføjer serveren style="display:none"
til <html>
-elementet på hver side. Den indre ramme fjerner det, når den får den ydre rammes bekræftelse.
JavaScript-kode har stadig lov til at køre, og der hentes stadig ressourcer. Men så længe personen ikke har indtastet noget input til siden, gør browseren intet, som en potentiel angriber ikke kunne have gjort ved blot at besøge webstedet – medmindre webstedet allerede er sårbart over for cross-site request forgery (CSRF).
Da vi valgte denne løsning, måtte vi løse problemerne med andre mulige resultater, især:
- Asynkron cookie-fiksering.
- Clickjacking på grund af framing.
- Phishing, der udgiver sig for at være Discover-domænet.
Hertil har de beskyttelser, vi har implementeret, taget højde for synkrone fikseringer, men de kan også forekomme asynkront. For at forhindre dette bruger vi en klassisk CSRF-forebyggelsesmetode. Vi kræver, at POST’er skal indeholde en forespørgselsparameter med datr
, der ses, når siden indlæses. Vi sammenligner derefter forespørgselsparameteren med den datr
-cookie, der blev set i anmodningen. Hvis de ikke stemmer overens, opfylder vi ikke anmodningen.
For at undgå datr
-lækage indlejrer vi en krypteret version af datr
inde i den indre ramme og sikrer, at denne forespørgselsparameter tilføjes til hvert <form>
– og XHR
-objekt. Da siden ikke selv kan udlede datr
-tokenet, er den datr
, der tilføjes, den datr
, der ses på det pågældende tidspunkt.
For anonyme anmodninger kræver vi, at de også skal have datr
-forespørgselsparameteren. Anonymiteten er bevaret, fordi vi ikke lækker den til tredjepartswebstedet – ick
-cookien mangler, så vi kan ikke bruge cookiekrukken. I dette tilfælde er vi dog ikke i stand til at validere i forhold til datr
-cookien, så anonyme POST’er kan foretages under fastlåste sessioner. Men da de er anonyme og mangler ick
, kan der ikke lækkes følsomme oplysninger.
Clickjacking
Når et websted sender X-Frame-Options: DENY
, indlæses det ikke i en indre ramme. Denne header bruges af websteder til at forhindre eksponering for visse typer angreb, f.eks. clickjacking. Vi fjerner denne header fra HTTP-svaret, men beder den indre ramme om at verificere, at parent
er top
-vinduets ramme ved hjælp af postMessage
. Hvis valideringen mislykkes, navigerer vi brugeren til “Oops”-siden.
Phishing
Den “adresselinje”, som vi giver i den sikre ramme, bruges til at eksponere den øverste indre rammes oprindelse for brugeren. Den kan dog kopieres af phishing-websteder, der udgiver sig for at være Discover. Vi forhindrer skadelige links i at navigere væk fra Discover ved at forhindre topnavigation ved hjælp af <iframe sandbox>
. Den yderste ramme kan kun undslippes ved at navigere direkte til et andet websted.
Den document.cookie
giver JavaScript mulighed for at læse og ændre cookies, der ikke er markeret HttpOnly
. Det er en udfordring at understøtte dette på en sikker måde i et system, der vedligeholder cookies på serveren.
Accessing cookies: Når der modtages en anmodning, opregner proxyen alle de cookies, der er synlige for den pågældende oprindelse. Den vedhæfter derefter en JSON-payload til svarsiden. Der injiceres kode på klientsiden for at shim document.cookie
og gøre disse cookies synlige for andre scripts, som om de var rigtige klientside-cookies.
Modificering af cookies: Hvis scripts får lov til vilkårligt at indstille cookies, som serveren derefter accepterer, kan dette føre til fiksering, hvor oprindelse evil.com
kan indstille en følsom cookie på example.com
.
Tro på browserens CORS-funktioner vil ikke være nok i dette tilfælde – oprindelse a.example.com
, der forsøger at indstille en cookie på example.com
, vil blive blokeret af browseren, da disse oprindelser er søskende og ikke hierarkiske.
Selvfølgelig kan serveren, når den modtager en ny cookie indstillet af klienten, ikke på sikker vis håndhæve, om måldomænet er tilladt; forfatterens oprindelse er kun kendt på klienten og sendes ikke altid til serveren på en måde, vi kan stole på.
For at tvinge klienten til at bevise, at den er berettiget til at indstille cookies på et bestemt domæne, sender serveren, ud over JSON-nyttelasten, en liste over kryptografiske tokens for hver af de oprindelser, hvor den anmodende oprindelse har tilladelse til at indstille cookies. Disse tokens saltes med ick
-værdien, så de ikke kan overføres mellem brugere.
Klient-side shim for document.cookie
tager sig af opløsningen og indlejringen af tokenet i den faktiske cookie-tekst, der sendes til proxyen. Proxyen kan derefter kontrollere, at den skrivende oprindelse rent faktisk har besiddet tokenet til at skrive til cookiens måldomæne, og gemmer det i cookiekrukken på serversiden og sender det til klienten igen, næste gang der anmodes om siden.
Bootstrap-protokol
Modellen indeholder tre oprindelsestyper: portal oprindelse (Discover portal osv.), sikker oprindelse (ydre ramme) og omskrivningsoprindelse (indre ramme). Hver har et andet behov:
- Portal origin kræver
datr
. - Sikker origin kræver
ickt
. - Rewrite origin kræver
datr
ogick
.
Med localStorage-protokol
Her er en repræsentation af bootstrap-processen for de fleste moderne mobilbrowsere:
Det er vigtigt at bemærke, at for at undgå refleksion udsender bootstrap-slutpunktet på den sikre oprindelse altid et nyt ick
og ickt
; ick
afhænger aldrig af brugerens input. Bemærk, at fordi vi indstiller domain=.discoverapp.com
på både ick
og datr
, er de tilgængelige i alle origin-typer, og ickt
er kun tilgængelig på den sikre origin.
Uden localStorage-protokol
Da visse browsere, såsom Opera Mini (populær i mange lande, hvor Discover opererer), ikke understøtter localStorage
, er vi ikke i stand til at gemme værdierne ick
og ickt
. Det betyder, at vi er nødt til at bruge en anden protokol:
Vi besluttede at adskille rewrite origin fra secure origin, så de ikke deler det samme hostsuffiks i henhold til Public Suffix List. Vi bruger www.0.discoverapp.com
til opbevaring af den sikre kopi af ickt
(som en cookie) og flytter alle tredjepartsoprindelser under 0.i.org
. I en velopdragen browser vil indstillingen af en cookie på den sikre oprindelse gøre den utilgængelig for alle rewrite origins.
Da oprindelserne nu er adskilt, bliver vores bootstrap-proces en to-trins-proces. Før kunne vi indstille ick
i den samme anmodning, som vi provisionerer localStorage
med ickt
. Nu er vi nødt til at bootstrappe to origins i separate anmodninger uden at åbne ick
-fikseringsvektorer.
Vi løser dette ved først at bootstrappe den sikre origin med ickt
-cookien og give brugeren en krypteret version af ick
med en nøgle, som kun proxyen kender. Den krypterede ick
-tekst er ledsaget af en nonce, der kan bruges til at dekryptere den pågældende ick
i rewrite origin og indstille en cookie, men kun én gang.
En angriber kunne vælge at enten:
- Brug nonce’en til at afsløre
ick
-cookien. - Give den til brugeren for at fastlægge dens værdi.
I begge tilfælde kan angriberen ikke samtidig kende og påtvinge en bestemt ick
-værdi på en bruger. Processen synkroniserer også datr
mellem oprindelserne.
Denne arkitektur har været igennem omfattende interne og eksterne sikkerhedstests. Vi mener, at vi har udviklet et design, der er robust nok til at modstå de typer af webapplikationsangreb, som vi ser i naturen, og som på sikker vis leverer en konnektivitet, der er bæredygtig for mobiloperatører. Efter lanceringen af Discover i Peru planlægger vi at udrulle yderligere Discover-forsøg med partneroperatører i en række andre lande, hvor vi har beta-testet produktfunktioner, herunder Thailand, Filippinerne og Irak. Vi forventer, at Discover vil være live i disse yderligere lande i de kommende uger, og vi vil undersøge yderligere forsøg, hvor partneroperatører ønsker at deltage.”
Vi vil gerne takke Berk Demir for hans hjælp til dette arbejde.
I et forsøg på at være mere inkluderende i vores sprogbrug har vi redigeret dette indlæg for at erstatte “whitelist” med “allowlist”.”