Säkerhet och ASP.NET AJAX
ASP.NET AJAX 1.0 möjliggör enkel och snabb utveckling av både klient- och serverorientade webbapplikationer med AJAX-stöd, men det finns ett antal säkerhetsaspekter som är nödvändiga att känna till när du ska implementera ASP.NET AJAX i din webbapplikation.
Till att börja med tänkte jag gå igenom några olika scenarion som egentligen inte är specifika för just ASP.NET AJAX-ramverket, utan mer handlar om de utmaningar som den nya programmeringsmodell som AJAX för med sig rent generellt (egentligen borde jag säga "nygamla" programmeringsmodell eftersom många utvecklare arbetat med liknande tekniker under många år innan AJAX blev populärt, ett exempel är remote scripting som användes i traditionell ASP).
Denna programmeringsmodell innebär att mer logik lyfts ut på klienten och att kommunikationen mellan webbläsaren och servern går från att ha varit fokuserad på begäran och leverans av kompletta gränssnitt i form av HTML-sidor till klienten, samt postningar av formulärdata tillbaka till servern, till att handla mer om ren dataöverföring mellan klient och server. I och med att mer ansvar läggs i klienten och fler metoder, som tidigare varit interna, exponeras på servern i form av webservices ökar den potentiella attackytan.
AJAX ger fler anrop mot servern jämfört med traditionell webbteknik
Ett populärt användningsområde för AJAX är att kontinuerligt visa upp träffar från sökningar i en databas i takt med att sökbegreppet skrivs in i ett input-fält. Det som händer när texten matas in i sökformuläret är att en POST eller GET görs mot servern vid varje inmatat tecken eller med ett visst intervall. Det här innebär att där det tidigare endast skedde ett anrop mot servern per sökning kan nu antalet anrop mångdubblas. I exemplet nedan söker jag på sajten Quotiki efter 'George Bernard Shaw' vilket resulterar i 14 st anrop mot servern:
(klicka för en större bild)
När jag testar att söka efter 'Microsoft' på en av de större svenska sajterna för prisjämförelse sker inte mindre än 36 st olika anrop mot söktjänsten! Det här kanske främst är en utmaning vad gäller skalbarheten och prestandan i din lösning - är du verkligen säker på att din applikation klarar av en flerdubbling av antalet anrop mot servern? Det ger även en potentiell attackyta för Denial-of-service attacker som kan utföras med mindre arbetsinsats och som blir svårare att skydda sig emot.
Antalet gränssnitt som tar emot data ökar
En annan vanlig funktion som byggs med AJAX är att kontrollera om ett fält i ett formuär blivit korrekt ifyllt direkt efter att fokus lämnar fältet - t.ex. när man fyller i användarnamnet vid en nyregistrering. Det som sker bakom kulissera är ett anrop mot servern för att kontrollera att inte användarnamnet redan är upptaget, isåfall notifieras användaren. Problemet med denna lösning kan vara i de fall då en befintlig applikation 'AJAX-ifieras' genom att formulären i applikationen med tiden uttökas med AJAX-funktionalitet. Om inte ordentlig validering av indata görs även i de nya webbservicegränssnitten som skapas så kan det i värsta fall öppna upp för attacker - t.ex. i form av SQL-injections eller Cross Site Scripting (ofta förkortat XSS).
Det var Cross-Site Scripting i kombination med AJAX som utnyttjades när MySpace i oktober 2005 utsattes för en attack i form av en XSS-mask som temporärt lyckades sänka hela MySpace-sajten. Masken fungerade så att alla som besökte användaren Samy's profil blev adderad till Samy's kontakter och fick texten "Samy is my hero" tillagd sin profil - samtidigt smittades besökarens profil med masken, som sedan lade till alla som tittade på dennes profil till Samy's kontakter osv. På mindre än 20 h hade över en miljon användare blivit infekterade - och allt detta skedde helt omärkligt för användarna med hjälp av XMLHTTP-postningar i bakgrunden. MySpace hade försökt att kontrollera all indata för att se till att det inte gick att injecera oönskade HTML-taggar eller script i sidorna - men det har visat sig oerhört svårt att skapa funktioner som ger 100% skydd om de, som i detta fallet, bygger på "Blacklists", d.v.s. där man försöker filtrera bort alla teckenkombinationer som anses skadliga. Ponera t.ex. att du filtrerar bort strängen "onload", för att undvika något i stil med <img src="image.jpg" onload="javascript:alert('bad code!');">. Ett sätt att enkelt komma runt filtret är då att istället skriva ononloadload vilket när strängen filtreras ändå resulterar i ett onload. På liknande sätt går det att komma runt de flesta filter av denna typ.
Så hur skyddar man sig då?
Till att börja med så ska man aldrig någonsin lita på den data som skickas från klienten. Om det ska göras valideringar på klientsidan så ska dessa göras för att skapa en bättre användarupplevelse och inte för att helt säkerställa att data är av korrekt typ, längd, format osv, eller att den inte innehåller oönskad kod - sådana slutgilitiga kontroller måste ske på serversidan. I ASP.NET och ASP.NET AJAX finns det ett visst inbyggd skydd vid mottagande av formulärdata, som även fungerar när en formulärkontroll ligger inom en asp:UpdatePanel och data därmed postas i asynkrona anrop med XMLHttpRequest mot servern.
I nedanstående mycket enkla exempel har jag lagt en TextBox, en Label och en Button i en webform. Klickar jag på knappen skrivs texten från min TextBox ut i Label:n. Jag har även lagt till en UpdatePanel så att postningen av formulärdata sker asynkront. När jag försöker skriva in och skicka något som ASP.NET AJAX uppfattar som potentiellt skadligt så får jag ett felmeddelande - som endast avslöjar att ett "Unknown error" uppstått:
I min eventlog har jag även fått ett Exception som beskriver mer i detalj vad som hänt:
...Exception information:
Exception type: HttpRequestValidationException
Exception message: A potentially dangerous Request.Form value was detected from the client (TextBox1="<script>alert('bu!')..."). ...
När det gäller anrop mot webservices, som har dekorerats med attributet [System.Web.Script.Services.ScriptService()] för att möjliggöra skapandet av ASP.NET AJAX client-proxies, så sker ingen motsvarande validering av input. Dessa fungerar i det avseendet precis som vilken ASP.NET-webservice som helst.
Vårt eminenta PAG-team (PAG står för Prescriptive Architecture Guidance) har skrivit ett antal bra artiklar som handlar om input-validering och hur man skyddar sig mot XSS och SQL-injections:
https://msdn2.microsoft.com/en-us/library/ms998372.aspx#pagpractices0001_inputanddatavalidation
https://msdn2.microsoft.com/en-us/library/ms998274.aspx
https://msdn2.microsoft.com/en-us/library/ms998271.aspx
Se även Scott Guthries artikel i hans Tips&Tricks-serie som handlar om hur man skyddar sig mot SQL-injections - den innehåller en bra översikt och många bra länkar.
Autentisera användaren och behörighetskontrollera åtkomsten till dina webservices
När det gäller åtkomsten till dina webservices för AJAX-anrop så ska denna naturligtvis vara så restriktiv som möjligt - håll antalet helt publika tjänster till ett absolut minimum och låt inte dessa hantera uppdateringar, ändringar eller raderingar av data. ASP.NET AJAX-ramverket är helt integrerat med Membership-providermodellen i ASP.NET - vilket innebär att det går alldeles utmärkt att slå på Forms-baserad autentisering - även på tjänster som kommer anropas direkt från klientscript. ASP.NET AJAX scriptbibliotek och klassen Sys.Services möjliggör att inloggning och utloggning kan göras direkt från klienten med hjälp av Javascript - som alltså anropar Membership-tjänsten på servern för atutentisering. Nedan ett exempel på hur inloggningsfunktionen kan se ut:
// This function calls the login method of the
// authentication service to verify
// the credentials entered by the user.
// If the credentials are authenticated, the
// authentication service issues a forms
// authentication cookie.
function OnClickLogin()
{
// Set the default callback functions.
SetDefaultLoginCompletedCallBack();
SetDefaultLogoutCompletedCallBack();
SetDefaultFailedCallBack();
// Call the authetication service to authenticate
// the credentials entered by the user.
Sys.Services.AuthenticationService.login(username.value,
password.value, false,null,null,null,null,"User Context");
}
Här finns integrationen med Membership-providern beskriven i dokumentationen. Forms authentication kan även kombineras med URL Authorization för en finkornigare åtkomstkontroll.
En god idé är även att slå av alla traditionella anropssätt mot din webservice, om det är så att den endast ska kunna kommunicera med AJAX-klienter. Det gör du genom att lägga till följande i elementet <system.web> i din webconfig-fil:
<
webServices>
<protocols>
<remove name="HttpGet"/>
<remove name="HttpSoap"/>
<remove name="HttpSoap12"/>
<remove name="HttpPost"/>
<remove name="HttpPostLocalhost"/>
<remove name="HttpGet"/>
<remove name="Documentation"/>
</protocols>
</webServices>
"Men Robert", invänder du säkert nu - "om du tar bort HttpPost - hur är det då möjligt att posta data till webservicen???"
Jo - det är nämligen så att när data postas med hjälp av XMLHttpRequest så sätts Content-Type HTTP-headern i anropet till application/json istället för application/x-www-form-urlencoded eller multipart/form-data
som headern skulle vara i en vanlig formulärpostning. Det, tillsammans den säkerhetsspärr i webbläsaren som hindrar att anrop från XMLHttpRequest kan skickas mot någon annan domän än den som webbsidan ursprungligen hämtades från, gör att du får ett skydd mot den typ av attacker som kallas för Cross Site Request Forgery (CSRF). Denna typ av attack innebär i korthet att ett script i en hackad eller illasinnad sajt lyckas posta data till en annan sajt som du nyligen gjort en korrekt inloggning i - och därmed kan skicka med den cookie som autentiserar anropet.
Ett anropssätt som är extra farligt att ha påslaget i sin webservice är HttpGet eftersom det ger möjlighet att anropa tjänsten via en helt vanlig länk, som användaren t.ex. klickar på i ett e-postmeddelande eller som göms i en image-tag. HttpGet är dock avslaget per default i ASP.NET.
Naturligtvis bör du även säkra upp kommunikationen i nätverket med hjälp av SSL ifall det är känsligt data som kommer att skickas mellan klienten och servern. Hoppas du har fått en klarare uppfattning om hur du kan säkra upp dina applikationer när du börjar använda ASP.NET AJAX - hör gärna av dig om du har några frågor - och till sist: lita aldrig på dina klienter!
Comments
Anonymous
February 21, 2007
Jag tycker du kartlägger och tar fram vilka säkerhetsproblem som finns med AJAX. Bra läsning samt bra bifogade länkar! Har du någon mer sida om just regular expressions? jag tycker denna är ganska bra: http://regexlib.com/Anonymous
February 21, 2007
RegExLib är bra - jag gillar deras Regex Tester, smidig att testa nya uttryck med. Kolla in vår How To: Use Regular Expressions to Constrain Input in ASP.NET http://msdn2.microsoft.com/en-us/library/ms998267.aspxAnonymous
February 21, 2007
Hur fungerar stödet mot HTML-kod om validateRequest är av?Anonymous
February 22, 2007
Då fungerar det inte alls. D.v.s. om du stänger av valideringen genom att sätta ValidateRequest="false" i Page-direktivet i din webform så kommer ingen validering ske av input till sidan. Default-inställningen är att denna är påslagen, så du måste isåfall explicit stänga av den - om du mot förmodan skulle vilja det.Anonymous
February 22, 2007
I see. Får se till att ha det avaktiverat då.Anonymous
April 04, 2007
Tomas Gilså på IDG skrev idag om en potentiell säkerhetsrisk som - under vissa förutsättningar - kan