Domyślnie ASP.NET MVC blokuje metody zwracające JSON, które wywołuje się za pomocą HTTP GET. Przykład:
public ActionResult GetData() { return Json(new []{new Person("Piotr","Zielinski")}); }
Wykonanie zakończy się wyjątkiem:
This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.
Z tego względu, programiści często wywołują Json z parametrem AllowGet:
public ActionResult GetData() { return Json(new []{new Person("Piotr","Zielinsk")},JsonRequestBehavior.AllowGet); }
W starszych przeglądarkach powyższa metoda może stanowić ryzyko. Załóżmy, że kontroler powyższej metody jest opatrzony atrybutem Authorize, czyli dostęp do danych powinien być chroniony. Wyłącznie osoba zalogowana ma prawo wywołać GetData.
Załóżmy również, że złośliwa osoba stworzy następujący dokument HTML:
<html> <body> <script type="text/javascript"> Object.prototype.__defineSetter__('FirstName', function(obj){alert(obj);}); </script> <script src="http://localhost:62574/Home/GetData"></script> </body> </html>
Jako źródło skryptu wskazujemy JSON. Tak się składa, że tablica danych JSON to prawidłowy kod JavaScript:
[{"FirstName":"Piotr","LastName":"Zielinski"}]
Gdybyśmy zwrócili wyłącznie pojedynczy obiekt, wtedy ładowanie strony zakończyłoby się błędem. Wyżej mamy również metodę, która wykona się wyłącznie gdy ustawiana jest właściwość o nazwie FirstName. Metoda Object.prototype.__defineSetter__ umożliwia nam zdefiniowanie, co ma zdarzyć się w momencie ustawienia właściwości na jakimś obiekcie. W tej chwili wyświetlamy Alert z zawartością obiektu co stanowi po prostu dane, które były wcześniej chronione.
Oczywiście musimy przekonać najpierw ofiarę, aby wykonała kod w jej kontekście. Jest to tak naprawdę odmiana ataku CSRF – wykonanie kodu, w kontekście innej osoby. Łatwo sobie wyobrazić kod, który zamiast wyświetlenia alertu, zapisuje dane na serwerze.
Rozwiązanie problemu jest proste – umożliwiać wyłącznie wykonanie metody za pomocą HTTP POST a nie GET. Wtedy nie będzie możliwe tzw. cross-domain request.
[Authorize] [AcceptVerbs(HttpVerbs.Post)] public ActionResult GetData() { return Json(new []{new Person("Piotr","Zielinski")}); }
Nowe przeglądarki są bezpieczne na tą lukę i nie jest możliwe pobranie danych w czyimś kontekście. Z tego co wyczytałem to Firefox od wersji 21, Chrome od 27 i IE od 10 nie są podatne na opisany wyżej atak.
Powyższe rozważania mają sens wyłącznie gdy dane są poufne i stąd opatrzone atrybutem Authorize. W takim przypadku nie chcemy, aby ktoś wykorzystał kontekst osoby zalogowanej w kompletnie innej aplikacji.