Förstå förfrågningsförfalskning på flera platser i .NET

Du kan bara producera säkra webbapplikationer genom att ta hänsyn till säkerhet, från början. Detta kräver att man tänker på de potentiella sätt som någon kan angripa din webbplats när du skapar varje sida, form och åtgärd. Det kräver också förståelse av de vanligaste typerna av säkerhetsproblem och hur man hanterar dem.

Den vanligaste typen av säkerhetshål i en webbsida gör det möjligt för en angripare att utföra kommandon på uppdrag av en användare men okänd för användaren. Förfalskningsangreppet på webbplatsen begär utnyttjande av det förtroende som en webbplats redan har skapat med en användares webbläsare.

I den här handledningen diskuterar vi vad en förfalskningsangrepp på flera sidor är och hur den utförs. Då bygger vi ett enkelt ASP.NET MVC-program som är sårbart för denna attack och fixar programmet för att förhindra att det händer igen.


Vad är Cross-Site Request Forgery?

Förfalskningsangreppet på plats på webbplatsen förutsätter först att offret redan har autentiserats på en målwebbplats, till exempel en bankplats, Paypal eller annan webbplats som ska attackeras. Denna autentisering måste lagras på ett sätt så att om användaren lämnar webbplatsen och återkommer, ses de fortfarande som inloggade av målwebbplatsen. Anfallaren måste då få offret att komma åt en sida eller länk som kommer att utföra en förfrågan eller ett inlägg till målwebbplatsen. Om attacken fungerar, kommer målwebbplatsen att se en förfrågan från offret och genomföra förfrågan som den användaren. Detta möjliggör faktiskt att angriparen utför alla åtgärder som krävs på den riktade webbplatsen som offer. Det potentiella resultatet kan överföra pengar, återställa ett lösenord eller ändra en e-postadress på den riktade webbplatsen.

Hur attacken fungerar

Åtgärden att få offret att använda en länk kräver inte att de klickar på en länk. En enkel bildlänk kan räcka till:

Inklusive en länk som denna på en annars till synes oskyldig foruminlägg, bloggkommentar eller sociala medier kan få en användare omedveten. Mer komplexa exempel använder JavaScript för att bygga en fullständig HTTP-postförfrågan och skicka in den till målwebbplatsen.


Bygga ett sårbart webbprogram i ASP.NET MVC

Låt oss skapa en enkel ASP.NET MVC-applikation och lämna den sårbar för denna attack. Jag använder Visual Studio 2012 för dessa exempel men det kommer också att fungera i Visual Studio 2010 eller Visual Web Developer 2010 fungerar om du har installerat support för MVC 4 som kan hämtas och installeras från Microsoft.


Börja med att skapa ett nytt projekt och välj att använda Internetprojekt mall. Endera View Engine kommer att fungera, men här använder jag ASPX-visningsmotorn.

Vi lägger till ett fält i tabellen UserProfile för att lagra en e-postadress. Under Server Explorer bygga ut Dataanslutningar. Du borde se Standardanslutning skapad med informationen för inloggningar och medlemskap. Högerklicka på Användarprofil bord och klicka Öppna tabelldefinition. På tomlinjen under Användarnamn tabell lägger vi till en ny kolumn för e-postmeddelandet. Namn kolumnen e-postadress, ge den typen nvarchar (MAX), och kolla på Tillåt Nulls alternativ. Klicka nu Uppdatering för att spara den nya versionen av tabellen.

Detta ger oss en grundläggande mall för en webbapplikation, med inloggningsstöd, som mycket liknar vad många författare skulle börja med att försöka skapa en applikation. Om du kör appen nu ser du den som visas och är funktionell. Tryck F5 eller använd DEBUG -> Starta debugging från menyn för att ta fram webbplatsen.


Låt oss skapa ett testkonto som vi kan använda för det här exemplet. Klicka på Registrera länka och skapa ett konto med något användarnamn och lösenord som du vill. Här kommer jag att använda ett konto som heter testuser. Efter skapandet ser du att jag är inloggad som testanvändare. När du har gjort det, avsluta och låt oss lägga till en sida i den här applikationen så att användaren kan ändra deras e-post.


Innan vi skapar den sidan för att ändra e-postadressen måste vi först göra en ändring i programmet så att koden är medveten om den nya kolumnen som vi just lagt till. Öppna AccountModels.cs fil under modeller mapp och uppdatera Användarprofil klass för att matcha följande. Detta berättar för klassen om vår nya kolumn där vi lagrar e-postadressen för kontot.

[Tabell ("UserProfile")] Public Class UserProfile [Key] [DatabaseGeneratedAttribute (DatabaseGeneratedOption.Identity)] public int UserId get; uppsättning;  allmän sträng Användarnamn get; uppsättning;  allmän sträng EmailAddress get; uppsättning; 

Öppna AccountController.cs fil. Efter RemoveExternalLogins funktion lägg till följande kod för att skapa en ny åtgärd. Detta kommer att få det aktuella e-postmeddelandet för den inloggade användaren och överföra den till vyn för åtgärden.

allmän ActionResult ChangeEmail () // Hämta det inloggade användarnycket användarnamn = WebSecurity.CurrentUserName; string currentEmail; använder (UsersContext db = nya UsersContext ()) UserProfile user = db.UserProfiles.FirstOrDefault (u => u.UserName.ToLower () == användarnamn); currentEmail = user.EmailAddress;  returnera Visa (currentEmail); 

Vi måste också lägga till motsvarande vy för den här åtgärden. Detta ska vara en fil med namnet ChangeEmail.aspx under Views \ Account mapp:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage"%"  Ändra e-postadress   

Ändra e-postadress

Nuvarande email adress: <%= Model ?? "Ingen aktuell e-postadress"%"

<% using(Html.BeginForm()) %> <% %>

Detta ger oss en ny sida som vi kan använda för att ändra e-postadressen för den inloggade användaren.


Om vi ​​kör den här sidan och gå till / Konto / ChangeEmail åtgärd, vi ser nu att vi för närvarande inte har ett email. Men vi har en textruta och en knapp som vi kan använda för att korrigera det. För det första måste vi skapa den åtgärd som kommer att utföras när formuläret på denna sida skickas in.

[HttpPost] allmän ActionResult ChangeEmail (ChangeEmailModel-modell) string användarnamn = WebSecurity.CurrentUserName; använder (UsersContext db = nya UsersContext ()) UserProfile user = db.UserProfiles.FirstOrDefault (u => u.UserName.ToLower () == användarnamn); user.EmailAddress = model.NewEmail; db.SaveChanges ();  // Och för att verifiera förändring, få e-postmeddelandet från profilen ChangeEmailModel newModel = new ChangeEmailModel (); använder (UsersContext db = nya UsersContext ()) UserProfile user = db.UserProfiles.FirstOrDefault (u => u.UserName.ToLower () == användarnamn); newModel.CurrentEmail = user.EmailAddress;  Retur Visa (newModel); 

Efter att ha gjort den här ändringen kör du webbplatsen och går igen till / Konto / ChangeEmail handling som vi just skapat. Du kan nu ange en ny e-postadress och klicka på Byta e-mail knappen och se till att e-postadressen uppdateras.


Attacking Site

Som skrivet är vår ansökan sårbar för en begäran om förfalskning på hela webbplatsen. Låt oss lägga till en webbsida för att se denna attack i åtgärd. Vi lägger till en sida på webbplatsen som kommer att ändra e-posten till ett annat värde. I HomeController.cs filen lägger vi till en ny åtgärd som heter AttackForm.

public ActionResult AttackForm () return View (); 

Vi lägger också till en vy för det här namnet AttackForm.aspx under / Visningar / Home mapp. Det ska se ut så här:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage"%"  Attack Form   

Attack Form

Den här sidan har en dold form för att attackera dig genom att ändra ditt e-postmeddelande:

Vår sida meddelar med tillfredsställelse sin dåliga avsikt, vilket givetvis inte skulle vara en riktig attack. Den här sidan innehåller en dold form som inte är synlig för användaren. Den använder sedan Javascript för att automatiskt skicka in denna blankett när sidan laddas.


Om du kör webbplatsen igen och gå till / Hem / AttackForm sida ser du att det laddas upp bra, men utan yttre indikation på att någonting har hänt. Om du nu går till / Konto / ChangeEmail sida men du ser att din e-post har ändrats till [email protected]. Här gör vi givetvis det självklart, men i en riktig attack kanske du inte märker att din e-post har ändrats.


Mitigating Cross-Site Request Forgery

Det finns två primära sätt att mildra denna typ av attack. Först kan vi kontrollera det hänskjutande som webbförfrågan kommer från. Detta bör berätta för ansökan när en formulärinsändning inte kommer från vår server. Detta har dock två problem. Många proxyservrar tar bort denna hänvisningsinformation, avsiktligt för att skydda integritet eller som en bieffekt, vilket innebär att en legitim begäran inte kunde innehålla denna information. Det är också möjligt för en angripare att förfalska hänvisningen, även om det ökar komplexiteten i attacken.

Den mest effektiva metoden är att kräva att det finns en användarspecifik token för varje formulärinsändning. Den här symbolens värde bör genereras slumpmässigt varje gång formuläret skapas och formuläret accepteras endast om token ingår. Om token saknas eller ett annat värde ingår, tillåter vi inte formulärinsändningen. Detta värde kan lagras antingen i användarens sessionsläge eller i en cookie för att vi ska kunna verifiera värdet när formuläret skickas.

ASP.NET gör denna process enkel, eftersom CSRF-stöd är inbyggt. För att kunna använda det behöver vi bara göra två ändringar på vår webbplats.


Åtgärda problemet

Först måste vi lägga till det unika token till formuläret för att ändra användarens email när vi visar det. Uppdatera formuläret i ChangeEmail.aspx se under / Konto / ChangeForm:

<% using(Html.BeginForm())  %> <%: Html.AntiForgeryToken() %> <%: Html.TextBoxFor(t=>t.NewEmail)%>  <%  %>

Den här nya raden: <%: Html.AntiForgeryToken() %> berättar ASP.NET för att skapa en token och placera den som ett dolt fält i formuläret. Dessutom hanterar ramverket att placera det på en annan plats där programmet kan komma åt det senare för att verifiera det.

Om vi ​​laddar upp sidan nu och tittar på källan ser vi den här nya raden, i formuläret, som görs till webbläsaren. Detta är vår token:

Vi måste också göra en förändring av vår åtgärd för att låta den veta att vi har lagt till den här symbolen och att den ska verifiera symbolet innan vi accepterar det formuläret.

Återigen är det enkelt i ASP.NET MVC. På toppen av den åtgärd som vi skapade för att hantera den upplagda formuläret, den med [HttpPost] attribut läggs till lägger vi till ett annat attribut som heter [ValidateAntiForgeryToken]. Detta gör starten på vår åtgärd ser nu ut som följande:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult ChangeEmail (ChangeEmailModel-modell) string användarnamn = WebSecurity.CurrentUserName; * resten av funktionen utelämnad *

Låt oss testa detta. Gå först till / Konto / ChangeEmail sida och återställ e-postmeddelandet för ditt konto till ett känt värde. Då kan vi återvända till / Hem / AttackForm sida och igen försöker angreppskoden att ändra vårt e-postmeddelande. Om du återvänder till / Konto / ChangeEmail sida igen, den här gången ser du att ditt tidigare inmatade e-postmeddelande fortfarande är säkert och intakt. De förändringar som vi gjort i vår form och handling har skyddat den här sidan från attacken.

Om du skulle titta på attackformuläret direkt (enkelt gjort genom att ta bort