JSONP — medveten XSS med risk för CSRF

Same origin policy (SOP) är vid sidan av webbläsarbråket den ständiga utmaningen i modern webbutveckling. Säkerhetsfolket försvarar den med näbbar och klor, svarthattarna letar säkerhetshål i den, och utvecklarna försöker kringå den för att kunna bygga nya tjänster med innehåll från olika källor. SOP är en strikt policy som antingen är på eller av, konfiguration är inte möjlig.

Same origin policy i ett nötskal

I princip säger SOP att webbinnehåll från olika källor inte får läsa eller ändra på varann. I Unix-termer så säger SOP att e(X)ecute är tillåtet men inte (R)ead eller (W)rite. Några exempel är på sin plats.
Antag att du bygger en webbapplikation med innehåll som du hämtar från flera källor, t ex en kombination av information och funktioner från aftonbladet.se, hitta.se och blocket.se.
  • Innehåll från http://www.aftonbladet.se får inte läsa eller ändra innehåll från http://www.hitta.se eller http://www.blocket.se pga olika domäner
  • Innehåll från http://www.aftonbladet.se får inte läsa eller ändra innehåll från https://www.aftonbladet.se pga olika protokoll
  • Innehåll från http://www.aftonbladet.se:80 får inte läsa eller ändra innehåll från http://www.aftonbladet.se:8080 pga olika portar (gäller alla webbläsare utom Internet Explorer)

Kringå SOP med hjälp av JSONP

Det finns flera sätt för utvecklare att arbeta sig runt SOP, en bra summering är Solving Cross-Domain Issues When Building Mashups. En populär teknik heter JSON with Padding (JSONP) och kan beskrivas som medveten cross site scripting.
Man utnyttjar det faktum att JavaScript får laddas från valfri källa, dvs SOP gäller inte script-taggen.
<script>Här får du hämta från valfri källa och köra som JavaScript</script>
Om nu källan istället för att bara svara med JSON (JavaScript Object Notation) lägger till (“paddar”) utgående data med ett funktionsanrop så kommer det anropet köras.
Hämta vanlig JSON och stoppa in den mellan script-taggarna:
<script type=”text/javascript” src=”http://aftonbladet.se/statistics/jsonp/snackMemberCount”></script>
… resulterar i det icke-fungerande skriptet …
<script>{“count”:”384040″}</script>
Men att hämta JSONP med parametern ‘callback’ och stoppa in mellan script-taggarna:
<script type=”text/javascript” src=”http://aftonbladet.se/statistics/jsonp/snackMemberCount?jsoncallback=callback”></script>
… resulterar i det fungerande skriptet …
<script>callback({“count”:”384040″})<script>
Om du tänder och släcker script-taggen, dvs skriver ut den och tar bort den, så kan du till och med “polla” en tjänst (här finns en ordentlig beskrivning av hur du gör i ren JavaScript).

En JSONP-klient

Så här kan en JSONP-klient byggd med JQuery se ut, i det här fallet en klient som hämtar antalet medlemmar i Snack:
<!DOCTYPE html>
<html>
<head>
<title>Medlemmar Aftonbladet Snack</title>

<script type=”text/javascript” src=”http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js”></script>
<script type=”text/javascript”>

URL_COUNT = ‘https://aftonbladet.se/statistics/jsonp/snackMemberCount?&jsoncallback=?’;

$(document).ready(function() {
__$.getJSON(URL_COUNT,
____function(data){
______$(‘#count’).html(data.count);
____}
__);
});

</script>
</head>
<body>
__Totalt antal medlemmar i Aftonbladet Snack: <span id=”count”></div>
</body>
</html>

En JSONP-tjänst på servern

På serversidan får man använda något ramverk som stödjer JSONP, t ex JAX-RS Jersey som nedan eller den bundlade JSON-pluginen i Struts 2. Här är serversidans RESTful service för antalet medlemmar:
@DisableXMLHttpRequest
@GET
@Path(“/statistics/jsonp/snackMemberCount”)
@Produces( { “application/x-javascript”, MediaType.APPLICATION_JSON } )
public JSONWithPadding snackMemberCountJsonP (
______@Context HttpServletRequest request,
______@QueryParam(“jsoncallback”) String callback)
{
__return new JSONWithPadding( new
______GenericEntity<Statistic>
________(getStats(request)) { }, callback);
}

Medveten Cross Site Scripting?

Hur var det nu med medveten cross site scripting? Jo, det är ju precis vad JSONP är. Du hämtar data från en annan källa och skriver ut det mellan script-taggar. Med andra ord litar du blint på det skript som kommer tillbaka och det finns inget som hindrar källan att lägga till valfri JavaScript som du gladeligen kommer köra. Men eftersom det liksom ligger i designens natur och syfte kan man inte se det som ett säkerhetshål, snarare en medveten risk. Använd med andra ord bara JSONP när du anser dig kunna lita på källan.

Risk för Cross Site Request Forgery?

Sen var det frågan om cross site request forgery (CSRF). I det scenariot så har attackeraren inkluderat request till JSONP-tjänsten på sin sida och alla offer som surfar in där kommer under ytan utföra requesten med sin session. CSRF-attacker är alltid blinda eftersom svaret från tjänsten kommer till offrets webbläsare och inte till attackeraren. Men med JSONP kan attackeraren “padda” med JavaScript som läcker ut informationen och alltså åstadkomma icke-blind CSRF. Det är ett säkerhetshål om informationen anses känslig! Och det hålet måste i så fall åtgärdas på servern precis som vid vanlig CSRF, dvs med en token som klienten förväntas skicka tillbaka. Det lite otrevliga med det är att din RESTful service inte blir tillståndslös vilket den egentligen ska vara.
@DisableXMLHttpRequest
@GET
@Path(“/statistics/jsonp/snackMemberCount”)
@Produces( { “application/x-javascript”, MediaType.APPLICATION_JSON } )
public JSONWithPadding snackMemberCountJsonP (
______@Context HttpServletRequest request,
______@QueryParam(“jsoncallback”) String callback,
______@QueryParam(“token”) String antiCsrfToken )
{
__// Check the token
__…
__return new JSONWithPadding(
______new GenericEntity<Statistic>
________(getStats(request)) { }, callback);
}

(Tack Stefan Pettersson, för ditt bidrag till det här blogginlägget!)

A Holistic View on Developer Productivity

What does developer productivity mean, really? Is it churning out more code or less code? Is it to have less bugs in production or shipping code more often? Is it doing a lot of things or just one thing? Let’s think about this for a moment. I believe developer productivity is about getting more things […]


Improving the usability of Aftonbladet Video-clip pages

We have recently started the process of improving the usability of video-clip pages. In order to get an idea of where Aftonbladet stands compared to other world-class online video/news providers, we conducted an online test answered by 110 visitors of Aftonbladet TV. In this test we compared their perception of an Aftonbladet TV video-clip page […]


Schibsted’s 1st iOS Deployment Meet-up

Schibsted’s 1st iOS Deployment Meet-up Thursday, 28th of April 2016: getting to know each other, guests arrive Friday, 29th of April 2016: the meet-up date We here at Aftonbladet had been planning on having a meet-up with iOS developers across various Schibsted companies for many months. We had a range of topics in mind for […]


Hackday: The Future of Storytelling is social, engaging and rewarding

We gathered students, journalists, developers and designers to get together and conceptualize something new for the news industry. This was our first organized hack event – The Future of Storytelling Hack. The hack was a team-based, news-media-focused prototyping and experimentation event within storytelling over two days at Kungsbrohuset, Schibsted and Aftonbladets headquarter in Stockholm. A good story used to […]