Wzorce projektowe: Router – cz. 2

Informacje wstępne

W tej części chciałbym skupić się na prostej metodzie filtrowania niebezpiecznych znaków na wejściu.

Implementacja

Ostatni raz klasa miała następującą postać

[php]
class Router {

public static $current_uri = ”;

public static function get_uri()
{
if (!empty($_SERVER[’PATH_INFO’]))
{
self::$current_uri = $_SERVER[’PATH_INFO’];
}
}
}
[/php]

Dodajmy więc następne elementy. Zaznaczone na zielono.

[php highlight=”12,13,14,15,16,17,18,19,20,21″]
class Router {

public static $current_uri = ”;

public static function get_uri()
{
if (!empty($_SERVER[’PATH_INFO’]))
{
self::$current_uri = $_SERVER[’PATH_INFO’];
}

self::$current_uri = trim(self::$current_uri, '/ ’);

if (self::$current_uri !== ”)
{
self::$current_uri = preg_replace(’#//+#’, '/’, self::$current_uri);

self::$current_uri = str_replace(’ ’, '_’, self::$current_uri);

self::$current_uri = preg_replace("/[^a-z0-9\\-\\_\\/\\,]/i", ”, self::$current_uri);
}
}
}
[/php]

  1. W linii 12 dodaliśmy funkcję „trim”, która obcina po lewej i po prawej stronie tekstu znaki, które podamy w parametrze. Domyślnie usuwa białe znaki (spacja, enter, tabulator). My chcemy pozbyć się niepotrzebnych slashy i białych znaków.
  2. Następnie sprawdzamy czy cokolwiek po tej operacji zostało w zmiennej $current_uri (linia 14). Jeśli jest różne od pustego ciągu do wykonujemy dodatkowe zabezpieczenie
  3. W linii 14 wyrażenie regularne usuwa wielokrotne slashe, np: jeśli na wejściu będzie „konto///klient//zobacz” to zamieni to na „konto/klient/zobacz”
  4. W linii 18 spacje zamieniane są na podkreślniki. To jeden z najprostszych sposobów oddzielania wyrazów w adresach stron. Ewentualnie można jeszcze zastosować myślnik
  5. Wreszcie w linii 20 wyrażenie regularne usuwa z wejścia wszystko co nie jest znakiem dopuszczanym (nie znajduje się na liście). W tym przypadku wyrażenie regularne pozostawi tylko znaki alfanumeryczne, przecinek i slash. W zależności od potrzeb można ta listę wydłużyć/zmienić

Ostateczna postać klasy:

[php]
class Router {

public static $current_uri = ”;

public static function get_uri()
{
if (!empty($_SERVER[’PATH_INFO’]))
{
self::$current_uri = $_SERVER[’PATH_INFO’];
}

self::$current_uri = trim(self::$current_uri, '/ ’);

if (self::$current_uri !== ”)
{
self::$current_uri = preg_replace(’#//+#’, '/’, self::$current_uri);

self::$current_uri = str_replace(’ ’, '_’, self::$current_uri);

self::$current_uri = preg_replace("/[^a-z0-9\\-\\_\\/\\,]/i", ”, self::$current_uri);
}
}
}</pre>
[/php]

Przykładowe wywołanie kodu:

[php]
Router::get_uri();
echo Router::$current_uri;
[/php]

Kod wywołania nie zmienił się. Spróbuję jednak podać przykład „brzydkiego” wejścia.

http://moja.strona/stroąna[45],param1/zob&*acz

Efekt powinien być taki:

strona45,param1/zobacz

Jak widać wejście dobrze zabezpieczone – zezwala tylko na takie znaki jakie ma zdefiniowane

Podsumowanie

Starałem się krótko przedstawić proste zasady uniknięcia problemów i nieprzyjemności z dalszymi operacjami na wejściu. Można oczywiście mnożyć ilość przypadków, znaków i sytuacji które należy obsłużyć ale podane reguły sa na obecną chwilę całkowicie wystarczające.

Przedstawione zabezpieczenia nie mają na celu doprowadzić adresu z wejścia do czegoś konkretnego. Np jeśli nasza aplikacja posiada akcję „/klient/logowanie” a ktoś zamiast tego wpisze (np haker) /klient/logowanie<script>złośliwykod</script>” albo sql injection w stylu „/klient/’ or 1=1 '” itd to system niebezpieczne ciągu znaków odrzuci i zostawi tylko to co się nadaje i jest bezpieczne. W związku z tym dalej możemy szukać czy mimo wszystko coś sensownego z tego da się wyciągnąć. Tworząc linki w naszej aplikacji wiemy jakie one są: np na stronie głównej zrobimy „/klient/logowanie” ale to nie znaczy, że ktoś ręcznie w przeglądarce wpisze sobie coś innego (czego nie jesteśmy w stanie przewidzieć). Dzięki tym zabezpieczeniom system najwyżej takiej osobie, która próbuje manipulować linkami powie, że taka strona nie istnieje i nie wpłynie to na bezpieczeństwo samej aplikacji. Ustalmy, że to Router ma dostarczyć dane w sposób bezpieczny tak, aby reszta aplikacji nie musiała się już tym martwić

W następnym artykule przedstawię sposób rozbicia wejścia na użyteczne informacje