Bruk av JSON, og da spesielt JSONp, innfører noen sikkerhetsproblemer dersom vi ikke bruker det riktig.
Posten JSON-ressurser og eksempel JavaScript-kode beskrev hvordan man kan bruke JSON som dataformat når man laster inn data via javascript, og nederst i artikkelen ble det gitt en advarsel i forhold til sikkerhet. Dersom man bruker JSONp (“JSON with padding” – et JSON script som inneholder en callback funksjon) med dynamiske script tagger (script-tagger legges inn i DOM av javascript), må man passe seg når man overfører sensitive data som krever autentisering.
Cross domain policy – reddende engel eller fiende?
Alle de vanligste browserne implementerer en policy for kjøring av javascript kalt Cross Domain Policy. Cross domain policy sier bl.a. at javascript i et vindu kun kan lese innhold i andre vinduer/tabber/frames/iframes dersom innholdet i begge vinduer kommer fra samme server og er overført over samme protokoll. For å kunne kommunisere må altså begge URLer ha samme hostnavn, samme portnummer og begge enten benytte http eller https. Man kan overstyre hostnavn-restriksjonen til å gjelde domene ved å sette document.domain i begge vinduer.
Denne policyen gjelder også for AJAX-kall (XmlHttpRequest), der den sier at man kun kan gjøre AJAX-kall tilbake til samme hostnavn, port og over samme protokoll. Dette vil riktignok bli endret i nyere versjoner av f.eks. Firefox og IE8, der man vil tillate AJAX-kall innenfor samme domene (XDomainRequest).
Mange frustrere utviklere har slitt med Cross Domain Policy, men det er en svært god grunn til at den er der. Uten den ville internett vært et svært utrygt sted. Dersom man hadde besøkt en hacket side, kunne denne siden opprettet en iframe til f.eks. gmail og lest din mail via javascript. Cross Domain Policy hindrer dette, og den eneste muligheten en hacker har til å komme rundt denne policy er å utnytte såkalte Cross Site Scripting (XSS)-hull i applikasjonen (og det har du vel ikke?).
Ved å legge inn dynamiske script tagger som igjen kaller en callback på egen side, omgår man Cross Domain Policyen, og dette er det viktig å være klar over. Man lar et script fra et domene påvirke innholdet i siden på et annet domene direkte i browseren. Det er umulig å styre hva dette skriptet gjør, så man bør tenke seg om før man legger inn en script-tag til et domene man ikke selv eier.
JSONp hijacking
Hvorfor er dette farlig? La oss tenke oss at vi har en side (http://eksempel/transactionlist) som laster inn sensitive data via JSONp eller AJAX-kall (http://eksempel/transactions/json) og kaller en callback funksjon:
showTransactions({ ... json data ... })
Selve JSON innholdet på http://eksempel/transactions/json krever at brukeren er autentisert. Brukeren logger seg inn på http://eksempel/transactionlist, og får en autentiserings-cookie (f.eks. sesjonsID) i browseren. Siden det er en cookie, vil browseren automatisk sende denne med for hvert AJAX eller JSONp kall til URLer som begynner med http://eksempel/
Selve siden vår, http://eksempel/transactionlist, kan f.eks. se slik ut:
<html>
...
<script type="text/javascript">
function showTransactions(jsondata) {
... vis html tabell over transaksjonene ...
}
</script>
<script src="http://eksempel/transactions/json"></script>
...For å utnytte dette, kan en hacker opprette en egen side som definerer den samme callback-funksjonen:
<html>
...
<script type="text/javascript">
function showTransactions(jsondata) {
... send data til hackers server ...
}
</script>
<script src="http://eksempel/transactions/json"></script>
...Når hackeren nå legger inn en script tag som peker til vår server, vil browseren forsøke å laste innholdet. Dersom den klarer det, vil den kjøre callbacken med innholdet, og innholdet vil bli stjålet. Hvis hackeren nå klarer å lure oss til å besøke siden hans, og vi er logget inn på vår originale side (noe vi ofte er når vi kjører browsere med mange faner), vil innholdet vårt bli stjålet. Dette er en form for Cross Site Request Forgery (XSRF).
Hvordan kan jeg beskytte meg?
Dersom man ønsker å bruke JSONp, men laster den fra egen server, er det enkelt å beskytte seg. I stedenfor å laste JSON innholdet via en dynamisk script-tag, kan vi laste det via AJAX (XmlHttpRequest). Først i hver json-respons legger vi inn f.eks. et kommentartegn, ugyldig kode, eller kode som kjører uendelig lenge. Google benytter f.eks. while(1);. Dette kan vi bruke slik:
while(1); showTransactions({ ... json data ... })
Når vi får tilbake resultatet fra AJAX-kallet, fjerner vi første linje, og kjører så resten.
Dersom en hacker igjen forsøker å stjele innholdet, har han ingen mulighet til å fjerne while(1)-linjen. Når man bruker script-tagger, som hackeren må gjøre for å omgå Cross Domain Policy, har man ikke mulighet til å endre innholdet før det kjøres. Scriptet kjøres direkte med en gang det er lastet, og det er ikke mulig å lese ut hva som egentlig var innholdet i scriptet som ble lastet inn.
Oppsummert
Bruk kun JSONp med dynamiske script-tagger til data som ikke krever autentisering eller data som ikke er spesielt sensitive. Dersom du skal bruke JSONp for denne typen innhold, må du sørge for å beskytte det og heller laste det via XmlHttpRequest (AJAX).