kminek.pl

AJAX i bezpieczeństwo serwerowych skryptów Grzegorz Wójcik

8 May, 2009  |  AJAX, Artykuły, PHP

Jeśli Twoja publiczna aplikacja internetowa bardzo intensywnie korzysta z technologii AJAX powinieneś zadać sobie pytanie, czy serwerowe skrypty obsługujące takie requesty są należycie zabezpieczone – okazać się może bowiem, że z ich dobrodziejstw korzystasz nie tylko Ty…

Rozważmy następujący scenariusz: na stronie głównej naszego serwisu (index.php) wyświetlamy listę artykułów. Klikając w link „komentarze” przy danym artykule, za pośrednictwem technologii AJAX pytamy skrypt po stronie serwera (komentarze.php) o listę komentarzy dla danego artykułu.

Zakładając, że korzystamy np. z [1] MooTools takie zapytanie może mieć np. taką formę:

  1. var myRequest = new Request({
  2. url: 'komentarze.php',
  3. method: 'get',
  4. onSuccess: function(responseText) {}
  5. });
  6.  
  7. myRequest.send('artykul_id=7');

Rodzi się pytanie – jak zabezpieczyć skrypt komentarze.php przed niecnym wykorzystaniem przez osobę trzecią? Średnio rozgarnięty intruz odwiedzając naszą stronę i analizując requesty przeglądarki (np. za pomocą [2] Firebuga) bez problemu dowie się o jego istnieniu i akceptowanych parametrach.

Intruz na swojej stronie nie będzie w stanie wykonywać AJAX-owych zapytań bezpośrednio do naszego skryptu ze względu na założenia bezpieczeństwa tej technologii (ang. same domain policy – żądania mogą być wykonywane tylko w obrębie tej samej domeny). Ale nic nie stoi na przeszkodzie, aby skorzystał z biblioteki [3] cURL i napisał prosty skrypt pośredniczący (proxy), który będzie odpowiadał za interakcję z komentarze.php. Jak ustrzec się takiego zagrożenia?

Nagłówki HTTP

Wydawać by się mogło, że po stronie serwera wystarczy dokonać inspekcji nagłówków requesta i np. sprawdzić domenę w zmiennej $_SERVER["HTTP_REFERER"]. Nic bardziej mylnego – zabezpieczanie skryptów tylko w oparciu o nagłówki HTTP można porównać do antykoncepcji metodą kalendarzyka małżeńskiego (vel watykańskiej ruletki). Wynika to z prostego faktu, że wykazując odrobinę dobrej woli wartością nagłówków można dowolnie manipulować. Przykładowo ustawienie dowolnej wartości dla pola Referer: w cURL jest kwestią jednej linii kodu:

  1. curl_setopt($ch, CURLOPT_REFERER, "http://www.kminek.pl/");

Oczywiście nagłówki dla pewności można sprawdzić zawsze ale nigdy nie powinna być to jedyna metoda zabezpieczeń. Należy nadmienić, że różne biblioteki JavaScript często dodają do requestów AJAX-owych nagłówek X-Requested-With: XMLHttpRequest, który również nie zaszkodzi sprawdzić.

Baza danych

Skoro nagłówki HTTP nie gwarantują wystarczającego bezpieczeństwa potrzebna jest jakaś inna forma ochrony. Najlepszym wyjściem jest wygenerowanie dla AJAX-owego żądania tokena (np. losowego ciągu alfa-numerycznego), który następnie będzie sprawdzany przez skrypt po stronie serwera. Generowanie losowego ciągu znaków może wyglądać np. w ten sposób:

  1. $token = md5(time().'jakislosowyciagznakow');

Taki token może być zapisywany np. w tabeli w bazie danych. Dla naszego przykładu z artykułami kroki tej metody mogą wyglądać następująco:

  1. plik index.php generuje losowy token, zapisuje go w tabeli w bazie danych i „wypluwa” jako zmienną JavaScript, która jest „doklejana” do wszystkich AJAX-owych wywołań
  2. skrypt komentarze.php sprawdza, czy przesłany token znajduje się w tabeli w bazie danych – jeśli tak – skrypt zwraca wyniki zapytania i kasuje token z tabeli. Jeśli tokena nie ma w tabeli – następuje przerwanie działania (exit()).

Zmienne sesji

Token możemy też przekazać za pomocą zmiennych sesji. To najlepsze i sugerowane rozwiązanie. Przede wszystkim unikamy obciążania bazy danych kolejnymi zapytaniami. Przeglądarka, wysyłając AJAX-owe żądanie, wyśle przecież również ciasteczko z identyfikatorem sesji. Później wystarczy już tylko porównać zmienną sesji z przesłanym tokenem. Czyli – kontynuując nasz przykład z artykułami – w pliku index.php będziemy mieli np. coś takiego:

  1. <?php
  2. session_start();
  3. $token = md5(time().'jakislosowyciagznakow');
  4. $_SESSION['token'] = $token;
  5. ?>

a w pliku komentarze.php sprawdzimy przesłany w żądaniu hash z tym, który jest przechowywany w sesji:

  1. <?php
  2. session_start();
  3. $token = $_GET['token'];
  4.  
  5. if (!empty($_SESSION['token']) && $_SESSION['token'] == $token) {
  6.  
  7. // poprawny request
  8.  
  9. } else {
  10.  
  11. // niepoprawny request
  12.  
  13. }
  14. ?>

Jeśli znacie inne ciekawe sposoby na zabezpieczanie skryptów będę wdzięczny za sugestie w komentarzach.

-----

Wydrukowano z: https://www.kminek.pl/ajax-i-bezpieczenstwo-serwerowych-skryptow/

Lista adresów URL występujących w tekście:

© 2007-2024 kminek.pl