Prevence útoků založených na otevřeném přesměrování (C#)
Tento kurz vysvětluje, jak můžete zabránit útokům s otevřeným přesměrováním v aplikacích ASP.NET MVC. Tento kurz popisuje změny, které byly provedeny v kontroleru Účtů v ASP.NET MVC 3, a ukazuje, jak tyto změny použít v existujících aplikacích ASP.NET MVC 1.0 a 2.
Co je útok s otevřeným přesměrováním?
Libovolnou webovou aplikaci, která přesměruje na adresu URL určenou prostřednictvím požadavku, jako je řetězec dotazu nebo data formuláře, může být potenciálně manipulována a přesměrovat uživatele na externí škodlivou adresu URL. Tato manipulace se označuje jako útok s otevřeným přesměrováním.
Kdykoli logika aplikace přesměruje na zadanou adresu URL, musíte ověřit, že s adresou URL přesměrování nedošlo k manipulaci. Přihlášení použité ve výchozím ovládacím panelu AccountController pro ASP.NET MVC 1.0 a ASP.NET MVC 2 je zranitelné vůči útokům s otevřeným přesměrováním. Naštěstí je snadné aktualizovat stávající aplikace tak, aby používaly opravy z ASP.NET MVC 3 Preview.
Abychom pochopili ohrožení zabezpečení, podívejme se na to, jak přesměrování přihlášení funguje ve výchozím projektu webové aplikace ASP.NET MVC 2. Pokus o návštěvu akce kontroleru s atributem [Authorize] v této aplikaci přesměruje neautorizované uživatele do zobrazení /Account/LogOn. Toto přesměrování na /Account/LogOn bude obsahovat parametr řetězce dotazu returnUrl, aby se uživatel po úspěšném přihlášení mohl vrátit na původně požadovanou adresu URL.
Na snímku obrazovky níže vidíme, že pokus o přístup k zobrazení /Account/ChangePassword, když nejste přihlášení, vede k přesměrování na /Account/LogOn? ReturnUrl=%2fAccount%2fChangePassword%2f.
Obrázek 01: Přihlašovací stránka s otevřeným přesměrováním
Vzhledem k tomu, že parametr querystring ReturnUrl není ověřený, může ho útočník upravit tak, aby do parametru vložil libovolnou adresu URL a provedl tak otevřený útok přesměrování. Abychom si to ukázali, můžeme upravit parametr ReturnUrl na https://bing.com, aby výsledná přihlašovací adresa URL byla /Account/LogOn? ReturnUrl=https://www.bing.com/. Po úspěšném přihlášení k webu jsme přesměrováni na https://bing.comadresu . Vzhledem k tomu, že toto přesměrování není ověřeno, může místo toho odkazovat na škodlivý web, který se pokusí oklamat uživatele.
Složitější útok s otevřeným přesměrováním
Otevřené útoky přesměrování jsou obzvláště nebezpečné, protože útočník ví, že se pokoušíme přihlásit na konkrétní web, což nás činí zranitelnými vůči phishingovým útokům. Útočník může například uživatelům webu poslat škodlivé e-maily, aby se pokusili zachytit jejich hesla. Podívejme se, jak by to fungovalo na webu NerdDinner. (Upozorňujeme, že živý web NerdDinner byl aktualizován, aby byl chráněn před útoky s otevřeným přesměrováním.)
Nejprve nám útočník pošle odkaz na přihlašovací stránku na webu NerdDinner, který obsahuje přesměrování na jeho zkameněnou stránku:
http://nerddinner.com/Account/LogOn?returnUrl=http://nerddiner.com/Account/LogOn
Všimněte si, že návratová adresa URL odkazuje na nerddiner.com, kterému ve slově večeře chybí "n". V tomto příkladu se jedná o doménu, kterou útočník řídí. Když přejdeme na výše uvedený odkaz, přejdeme na legitimní přihlašovací stránku NerdDinner.com.
Obrázek 02: Přihlašovací stránka NerdDinner s otevřeným přesměrováním
Když se správně přihlásíme, akce LogOn ASP.NET MVC AccountController nás přesměruje na adresu URL zadanou v parametru řetězce dotazu returnUrl. V tomto případě je to adresa URL, kterou útočník zadal, což je http://nerddiner.com/Account/LogOn
. Pokud nejsme extrémně opatrní, je velmi pravděpodobné, že si toho nevšimneme, zejména proto, že útočník pečlivě zajistil, aby jeho zkažená stránka vypadala přesně jako legitimní přihlašovací stránka. Tato přihlašovací stránka obsahuje chybovou zprávu s žádostí, abychom se znovu přihlásili. Neohrabuje nás, museli jsme heslo chybně zadat.
Obrázek 03: Přihlašovací obrazovka Forged NerdDinner
Když znovu zadáme uživatelské jméno a heslo, zkamenělá přihlašovací stránka uloží informace a pošle nás zpátky na legitimní NerdDinner.com web. V tomto okamžiku už nás NerdDinner.com web ověřil, takže zkažená přihlašovací stránka se může přesměrovat přímo na tuto stránku. Konečným výsledkem je, že útočník má naše uživatelské jméno a heslo a my nevíme, že jsme mu je poskytli.
Zobrazení ohroženého kódu v akci LogOn kontroleru účtu
Kód akce LogOn v aplikaci ASP.NET MVC 2 je zobrazený níže. Všimněte si, že po úspěšném přihlášení vrátí kontroler přesměrování na returnUrl. Vidíte, že se neprovádí žádné ověření s parametrem returnUrl.
Výpis 1 – akce logon ASP.NET MVC 2 v AccountController.cs
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Teď se podíváme na změny akce logon ASP.NET MVC 3. Tento kód byl změněn tak, aby ověřil parametr returnUrl voláním nové metody v pomocné třídě System.Web.Mvc.Url s názvem IsLocalUrl()
.
Výpis 2 – akce logon ASP.NET MVC 3 v AccountController.cs
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("",
"The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Toto bylo změněno, aby se ověřil parametr návratové adresy URL voláním nové metody v pomocné třídě System.Web.Mvc.Url . IsLocalUrl()
Ochrana aplikací ASP.NET MVC 1.0 a MVC 2
Můžeme využít ASP.NET změny MVC 3 v našich stávajících aplikacích ASP.NET MVC 1.0 a 2 přidáním pomocné metody IsLocalUrl() a aktualizací akce LogOn pro ověření parametru returnUrl.
Metoda UrlHelper IsLocalUrl() ve skutečnosti volá metodu v System.Web.WebPages, protože toto ověření je také používáno ASP.NET webové stránky aplikace.
Výpis 3 – metoda IsLocalUrl() z ASP.NET MVC 3 UrlHelper class
public bool IsLocalUrl(string url) {
return System.Web.WebPages.RequestExtensions.IsUrlLocalToHost(
RequestContext.HttpContext.Request, url);
}
IsUrlLocalToHost Metoda obsahuje skutečnou logiku ověřování, jak je znázorněno v výpisu 4.
Výpis 4 – Metoda IsUrlLocalToHost() z třídy System.Web.WebPages RequestExtensions
public static bool IsUrlLocalToHost(this HttpRequestBase request, string url)
{
return !url.IsEmpty() &&
((url[0] == '/' && (url.Length == 1 ||
(url[1] != '/' && url[1] != '\\'))) || // "/" or "/foo" but not "//" or "/\"
(url.Length > 1 &&
url[0] == '~' && url[1] == '/')); // "~/" or "~/foo"
}
V naší aplikaci ASP.NET MVC 1.0 nebo 2 přidáme do AccountController metodu IsLocalUrl(), ale pokud je to možné, doporučujeme ji přidat do samostatné pomocné třídy. Provedeme dvě malé změny ASP.NET MVC 3 verze IsLocalUrl(), aby fungovala uvnitř AccountController. Nejprve ji změníme z veřejné metody na privátní, protože k veřejným metodám v řadičích je možné přistupovat jako k akcím kontroleru. Za druhé upravíme volání, které kontroluje hostitele adresy URL vůči hostiteli aplikace. Toto volání využívá místní pole RequestContext ve třídě UrlHelper. Místo toho, abyste tuto funkci používali. RequestContext.HttpContext.Request.Url.Host, použijeme ho. Request.Url.Host. Následující kód ukazuje upravenou metodu IsLocalUrl() pro použití s třídou kontroleru v aplikacích ASP.NET MVC 1.0 a 2.
Výpis 5 – Metoda IsLocalUrl(), která je upravená pro použití s třídou kontroleru MVC
private bool IsLocalUrl(string url)
{
if (string.IsNullOrEmpty(url))
{
return false;
}
else
{
return ((url[0] == '/' && (url.Length == 1 ||
(url[1] != '/' && url[1] != '\\'))) || // "/" or "/foo" but not "//" or "/\"
(url.Length > 1 &&
url[0] == '~' && url[1] == '/')); // "~/" or "~/foo"
}
}
Teď, když je metoda IsLocalUrl() na místě, můžeme ji volat z akce LogOn a ověřit parametr returnUrl, jak je znázorněno v následujícím kódu.
Výpis 6 – aktualizovaná metoda LogOn, která ověřuje parametr returnUrl
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
if (IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("",
"The user name or password provided is incorrect.");
}
}
}
Teď můžeme otestovat útok s otevřeným přesměrováním tak, že se pokusíme přihlásit pomocí externí adresy URL pro vrácení. Použijeme /Account/LogOn? ReturnUrl=https://www.bing.com/ znovu.
Obrázek 04: Testování aktualizované akce LogOn
Po úspěšném přihlášení jsme přesměrováni na akci Kontroler domů nebo indexu místo na externí adresu URL.
Obrázek 05: Otevřený útok přesměrování byl poražen
Souhrn
K útokům s otevřeným přesměrováním může dojít, když se adresy URL přesměrování předávají jako parametry v adrese URL aplikace. Šablona ASP.NET MVC 3 obsahuje kód pro ochranu před útoky s otevřeným přesměrováním. Tento kód můžete přidat s určitými úpravami pro aplikace ASP.NET MVC 1.0 a 2. Pokud chcete chránit před otevřenými útoky přesměrování při přihlašování k aplikacím ASP.NET 1.0 a 2, přidejte metodu IsLocalUrl() a v akci LogOn ověřte parametr returnUrl.
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro