ASP.NET Core vs ataki typu Unvalidated Redirects and Forwards (OWASP Top 10 #1)

Czas czytania ~ 180 sekund

OWASP (Open Web Application Security Project) jest organizacją non-profit skupioną na poprawianiu bezpieczeństwa oprogramowania. Co kilka lat tworzą dokument OWASP Top 10 (Top Ten Most Critical Web Application Security Risks) zawierający klasyfikację najpopularniejszych ataków na aplikacje webowe wraz ze wskazówkami, jak się przed nimi bronić.

ASP.NET Core posiada wiele wbudowanych mechanizmów obrony przed takimi atakami. Ich dobra znajomość może nas uchronić przed niebezpieczeństwem. W tym i dwóch kolejnych postach postaramy się przyjrzeć wybranym atakom i możliwościom obrony przed nimi. Dziesiąte miejsce na liście z 2013 roku (najbardziej aktualna w momencie pisania posta) zajmuje rodzina ataków typu Unvalidated Redirects and Forwards.

Przekierowania serwerowe

Przez niezwalidowane serwerowe przekierowanie rozumiemy taką odpowiedź HTTP, która spowoduje, że użytkownik znajdzie się na innej domenie. Technicznie można to wykonać na kilka sposobów, poniżej trzy przykładowe odpowiedzi HTTP:

HTTP/1.1 301 Moved permanently (302 Moved Temporarily)
Location: http://badsite.com/

HTTP/1.1 200 OK
Refresh: 0; url=http://badsite.com/

HTTP/1.1 200 OK
Content-Length: 125
<html>
<head>
<meta http-equiv=“refresh” content= “0;url=http://badsite.com/”> </head>
</html>

Przekierowania klienckie

Bez udziału serwera użytkownik również możne na wiele sposobów zmienić adres. Mamy nawigację przez elementy HTML typu a i ich atrybut href, mamy kilka możliwości po stronie JS, takich jak:

  • document.location
  • document.URL
  • window.location.href
  • window.open
  • window.naviagate

Ryzyko

Problem pojawi się, gdy atakujący będzie mógł manipulować parametrem przekierowania, tak, by dało się z naszej strony wylądować na dowolnej innej stronie. Przykład otwartego, niezwalidowanego przekierowania:

Klient wysyła POST pod adres:

http://mydomain.com/api/Values?redirectUrl=https://mickl.net

A kod po stronie serwera wygląda tak:

[HttpPost]
public IActionResult Post([FromBody]string value, string redirectUrl)
{
    //do something
    return Redirect(redirectUrl);
}

Jednym z popularniejszych zastosowań tego typu mechanizmu może być strona logowania. Użytkownik wchodzi przez link na jakąś podstronę, jego sesja wygasła, więc serwer przekieruje go do strony logowania, a po pomyślnym logowaniu chcemy, żeby user wylądował od razu na tej stronie, którą chciał odwiedzić. Adres docelowej strony przekazujemy między requestami np. jako parametr URL.

Atakujący ma więc pole manewru i może podsyłać swoim ofiarom linki do naszej strony. Użytkownik otwiera link, widzi naszą dobrze znaną stronę logowania, ale po zalogowaniu ląduje na innej stronie. Niewielu użytkowników przed otwarciem przeanalizuje wszystkie znaki zawarte w linku (czasami nawet ich nie zobaczy). Dodatkowo każdy adres URL można ukryć za skracaczem linków, takim, jak tinyurl.com

Ataki takie mogą skompromitować naszą stronę (użytkownik zniechęci się, gdy zobaczy, że strona którą zna przekierowała go w jakieś dziwne miejsce z nie do końca interesującą go treścią). Z drugiej otwarcie linku może wymusić pobieranie plików .exe na dysk nieświadomego usera, a to dopiero początek do większych problemów. Wszystko firmowane marką naszej domeny.

Walidacja

Po przeczytaniu tego wszystkiego możemy wpaść na proste rozwiązanie z poziomu kodu. Chcemy tylko przekierowywać do naszej strony więc przechwyćmy wszystkie linki bezwzględne, np. takim wyrażeniem regularnym

^https?://

Oto kilka sposobów, jak obejść takie zabezpieczenie (każdy taki adres również przekieruje)

LocalRedirect

Dużo bardziej skutecznym rozwiązaniem wydaje się być whitelisting, a więc dopuszczenie tylko adresów z określonej listy. Dodatkowo jeśli chcemy udostępnić przekierowania tylko do własnej domeny (pewnie najczęściej tak będzie), możemy skorzystać z metody kontrolera o nazwie LocalRedirect.

[HttpPost]
public IActionResult Post([FromBody]string value, string redirectUrl)
{
    //do something
    return LocalRedirect(redirectUrl);
}

Różnica będzie polegała na tym, że przekierowanie nastąpi tylko jeśli adres jest adresem lokalnym, np. /Home. Przetestowałem wszystkie przykłady z listy powyżej i w każdym przypadku serwer rzuca błędem 500.

wwww

W przypadku adresów lokalnych serwer zwraca kod 302  / Found. Mamy również do dyspozycji metodę LocalRedirectPermanent, która zwraca kod 301 / Moved Permanently. Różnica będzie taka, że przeglądarka po przeczytaniu 301 zapamięta, by nie odpytywać już o zasób pod pierwszym adresem (został przeniesiony permanentnie).

A więc rozumiejąc ryzyko, które niosą za sobą przekierowania, pamiętajmy o tym, że możemy w prosty sposób uniknąć niepotrzebnych problemów.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s