JSONP -- medveten XSS med risk för CSRF

0 kommentarer

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 owasp.org, owasp.se och google.com.
  • Innehåll från http://www.owasp.org får inte läsa eller ändra innehåll från http://www.owasp.se eller http://www.google.com pga olika domäner
  • Innehåll från http://www.owasp.org får inte läsa eller ändra innehåll från https://www.owasp.org pga olika protokoll
  • Innehåll från http://www.owasp.org:80 får inte läsa eller ändra innehåll från http://www.owasp.org: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 av teknikerna 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://owasp.se/statistics/jsonp/chapterMemberCount"></script>
... resulterar i det icke-fungerande skriptet ...
<script>{"count":"384"}</script>

Men att hämta JSONP med parametern 'callback' och stoppa in mellan script-taggarna:
<script type="text/javascript" src="http://owasp.se/statistics/jsonp/chapterMemberCount?jsoncallback=callback"></script>
... resulterar i det fungerande skriptet ...
<script>callback({"count":"384"})<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 (en ordentlig beskrivning av hur du gör i ren JavaScript finns på Stefan Petterssons blogg).

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 OWASP Sweden:

<!DOCTYPE html>
<html>
<head>

<title>Medlemmar i OWASP Sweden</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://owasp.se/statistics/jsonp/chapterMemberCount?&jsoncallback=?';

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

</script>
</head>

<body>
__Totalt antal medlemmar i chaptret: <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 i OWASP Sweden:

@DisableXMLHttpRequest
@GET
@Path("/statistics/jsonp/chapterMemberCount")
@Produces( { "application/x-javascript", MediaType.APPLICATION_JSON } )
public JSONWithPadding chapterMemberCountJsonP (
______@Context HttpServletRequest request,
______@QueryParam("jsoncallback") @DefaultValue("fn") String callback,
______@QueryParam("token") String antiCsrfToken )
{
__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 kan 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! Och det hålet måste å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/chapterMemberCount")
@Produces( { "application/x-javascript", MediaType.APPLICATION_JSON } )
public JSONWithPadding chapterMemberCountJsonP (
______@Context HttpServletRequest request,
______@QueryParam("jsoncallback") @DefaultValue("fn") String callback,
______@QueryParam("token") String antiCsrfToken )
{
__// Check the token
__...
__return new JSONWithPadding( new GenericEntity<Statistic>(getStats(request)) { }, callback);
}

/John Wilander

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

Genomgång av OWASP Testing Guide (del 4 av 5)

0 kommentarer
OWASP (Open Web Application Security Project) har en testguide (OWASP Testing Guide, finns gratis på webben och i tryckt form till självkostnadspris) som innehåller 66 tester i 10 olika kategorier. I denna serie av blogginlägg kommer jag att gå igenom dessa 66 tester, vilka sårbarheter de testar och varför det är viktigt att testa dina applikationer för dessa sårbarheter.

I förra blogginlägget pratade jag om auktorisering, vilket är processen för att kolla ifall du har rätt att utföra de transaktionerna du anropar, och hur man testar affärslogiken i en webbapplikation. I detta inlägg tänkte jag gå igenom datavalidering och överlastningsattacker.

Kategori
Ref. Nummer
Testnamn
Sårbarhet






Data Validation Testing
OWASP-DV-001
Testing for Reflected Cross Site Scripting
Reflected XSS
OWASP-DV-002
Testing for Stored Cross Site Scripting
Stored XSS
OWASP-DV-003
Testing for DOM based Cross Site Scripting
DOM XSS
OWASP-DV-004
Testing for Cross Site Flashing
Cross Site Flashing
OWASP-DV-005
SQL Injection
SQL Injection
OWASP-DV-006
LDAP Injection
LDAP Injection
OWASP-DV-007
ORM Injection
ORM Injection
OWASP-DV-008
XML Injection
XML Injection
OWASP-DV-009
SSI Injection
SSI Injection
OWASP-DV-010
XPath Injection
XPath Injection
OWASP-DV-011
IMAP/SMTP Injection
IMAP/SMTP Injection
OWASP-DV-012
Code Injection
Code Injection
OWASP-DV-013
OS Commanding
OS Commanding
OWASP-DV-014
Buffer overflow
Buffer overflow
OWASP-DV-015
Incubated Sårbarheter Testing
Incubated Sårbarheter
OWASP-DV-016
Testing for HTTP  Splitting/Smuggling

HTTP Splitting, Smuggling
Datavalidering är det område där det brukar oftast gå fel, så det är värt att lägga lite extra krut på dessa tester. Det är också under datavalidering som de två högsta problemen under OWASP Top 10 ligger (diverse Injection sårbarheter samt Cross Site Scripting (XSS)). Det underliggande problemet är att indata tolkas som kod, antingen i webbläsaren eller på servern.

Testfall DV-001, DV-002 och DV-003 
Cross Site Scripting (XSS) är en sårbarhet som uppstår när man låter inmatad data tolkas som kod som körs i webbläsaren (oftast JavaScript, men andra skript språk som webbläsaren kan tolka är också applicerbara). Cross Site Scripting har ett par varianter:
  • Typ 1 (reflekterad): Data som skickas med anropet visar (reflekteras) på den resulterande sidan. Sökfält är överrepresenterade i denna typ av sårbarheter.
  • Typ 2 (lagrad): Data som skickas med anropet lagras på servern som senare visar den för användaren. Denna typ av sårbarhet brukar vara överrepresenterad i alla typer av kommentarsfält.
  • Typ 0 (DOM): DOM-baserad XSS har mycket gemensamt med Typ 1 (reflekterad), fast man attackerar webbläsarens DOM-träd istället. 
Man kan även utföra attacker på Flash-objekt på liknande sätt, då oftast genom att skicka attack data till Flash-objektet.

När man testar för XSS-sårbarheter så brukar man skicka in ett litet JavaScript:

<script>alert(”XSS”);</script>

Som skapar en alert-dialog med ordet ”XSS” i sig. Tyvärr är det så att systemägare/systemansvarig tror att det är det som är problemet (att kunna skapa en massa alert-dialoger). Detta är dock inte det enda som en attackerare kan göra, utan en attackerare kan köra valfri kod i JavaScript i användarens webbläsare. För att få en idé vad sådana skript kan åstadkomma kan man titta på BeEF (browser exploitation framework) som man kan ladda ner gratis på http://www.bindshell.net/tools/beef/. Jag planerar att skriva en artikel om just BeEF vid ett senare tillfälle.

Testfall DV-005 
När det kommer till injection-attacker så är SQL-injection den mest kända. SQL-injection är en attack där indata tolkas som SQL-kod i databasen. Låt oss ta en titt på lite SQL-kod i en applikation, i detta fall inloggningsfunktionen i Insecure Web Application (IWA) som vi använder i våra utbildningar:

sql = "SELECT * FROM users WHERE user = '" + user + "' AND password = '" + password + "'"; 

där den röda texten är indata från användaren. Detta uttryck läses som: Välj alla rader från tabellen “users” där användarnamnet är vad som skickades in från formuläret och lösenordet är vad som skickades in från formuläret.

Om man som attackerare matar in följande indata:

user = 'OR 1=1 --
password = blabla 


Så får man följande SQL-uttryck:

SELECT * FROM users WHERE user = '' OR 1=1 --' AND password = 'blabla

“--“ är ett kommentarstecken i SQL, vilket betyder att allt efter ”--” ignoreras. Detta resulterar då i följande SQL-uttryck:

SELECT * FROM users WHERE user = '' OR 1=1 

Vilket läses som: Välj alla rader från tabellen “users” där användarnamnet är tomt eller 1=1 (dvs. sant). Detta resulterar att databasen ger applikationen alla fält i tabellen, och applikationen använder det första som brukar vara administratörs-kontot. Voilá, instant admin access.

SQL-injektioner kan till exempel användas för att komma förbi inloggningskrav, som exemplet ovan; för att ladda ner databasen; för att skapa, modifiera eller ta bort data ur databasen samt i vissa fall ta kontroll över databasservern (via så kallade Stored Procedures).

Kategori
Ref. Nummer
Testnamn
Sårbarhet


Denial of Service Testing
OWASP-DS-001
Testing for SQL Wildcard Attacks
SQL Wildcard Sårbarheter
OWASP-DS-002
Locking Customer Accounts
Locking Customer Accounts
OWASP-DS-003
Testing for DoS Buffer Overflows
Buffer Overflows
OWASP-DS-004
User Specified Object Allocation
User Specified Object Allocation
OWASP-DS-005
User Input as a Loop Counter
User Input as a Loop Counter
OWASP-DS-006
Writing User Provided Data to Disk
Writing User Provided Data to Disk
OWASP-DS-007
Failure to Release Resources
Failure to Release Resources
OWASP-DS-008
Storing too Much Data in Session
Storing too Much Data in Session
Belastningsattacker finns i två varianter: bandbreddsattacker och applikationsattacker. Jag kommer inte gå igenom bandbreddsattacker, där man skickar så mycket data eller förfrågningar till servern så att den inte har några lediga resurser kvar till legitima användare. I korta drag så är det en ”störst internetuppkoppling vinner” tävling, och det finns inget som applikationen man testar kan göra något åt utan man måste kolla på infrastrukturer runt omkring.

Testfall DS-001 
SQL-injection attacker kan även användas för att skapa belastningsattacker. SQL har stöd för jokertecken, och vissa kan kräva mycket systemresurser för att analysera. Ett exempel på sådana jokertecken är "[]","[^]","_" och "%". Om man matar in teststrängen _[^!_%/%a?F%_D)_(F%)_%([)({}%){()}£amp;N%_)$*£()$*R"_)][%](%[x])%a][$*"£$-9]_ så kan man få en SQL-fråga som normalt returnerar på mindre än en sekund ta 6 sekunder eller mer, och skickar man flera sådana så kan man allokera alla SQL-kopplingar som applikationen har till databasen, vilket resulterar i att legitima användare inte får några databasresurser för att köra applikationen.

Testfall DS-002 
Om applikationen låser konton efter ett antal försök så kan en attackerare låsa alla konton i applikationen (om attackeraren vet vilka de är genom testfall AT-002), vilket hindrar legitima användare från att logga in och använda systemet. Så här har vi ett litet problem: Om man blockerar testfall AT-002 och AT-004 så kan man skapa en belastningsattack av typen DS-002. Vilket som är farligast måste bedömas från applikation till applikation, det finns inte något bra sätt att hantera detta som fungerar i alla tillfällen.

Det var allt för den här gången. I nästa och sista delen av den här artikelserien så kommer jag gå igenom testfall för webbtjänster (Webservice) och AJAX-specifika attacker.

/Michael Boman