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]
- 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.
- 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
- 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”
- 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
- 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