Wprowadzenie do klas w języku PHP
- Dla kogo przeznaczony jest ten artykuł
- Wprowadzenie
- Cele kursu
- Informacje podstawowe
- Wskazówki wstępne
- Jak to działa
- Definiowanie klas
- Weryfikacja adresu strony
- Mechanizm obsługi nieprawidłowych adresów
- Sprawdzanie adresu strony, z której nastąpiło wejście
- Wykrywanie powracających złośliwych użytkowników
- Sprawdzanie linków referencyjnych i powracających złośliwych użytkowników
- Funkcje sesji
- Podsumowanie funkcji bezpieczeństwa
- Konstruktory
- Zmiana strony docelowej dla użytkowników wchodzących z nieprawidłowego adresu
- Kompletny kod
- Przydatne adresy
1. Dla kogo przeznaczony jest ten artykuł
Artykuł ten przeznaczony jest dla średniozaawansowanych i zaawansowanych programistów PHP.
2. Wprowadzenie
Dzięki temu artykułowi dowiesz się jak pisać i używać klas w celu stworzenia elastycznego i łatwego w utrzymaniu kodu.
Czytając ten tutorial dowiesz się jak utworzyć prostą klasę, zaopatrzoną w kilka podstawowych funkcji zwiększających bezpieczeństwo: sprawdzanie logowań użytkowników. Mimo że klasa ta odpowiada funkcjonalnością najnowszym modelom bezpieczeństwa, to nie zapewnia go ona w 100%.
Przykład przedstawiony w tym artykule opisuje bardzo prosty ale skuteczny sposób hakowania stron internetowych: ręczne modyfikowanie adresów URL z poziomu okna przeglądarki. Spośród wielu rodzajów ataków na strony WWW, ten jest jednym z najprostszych do przeprowadzenia i zarazem jednym z tych, przed którymi najtrudniej się obronić. Złośliwy użytkownik może na przykład zmienić ceny w koszyku lub uzyskać dostęp do miejsc, które wymagają autoryzacji. Dokonać tego można modyfikując parametry przechowywane w adresie URL lub zapisując stronę na dysku i modyfikując jej ukryte zmienne. Szczególną uwagę należy poświęcić stronom, na których wypełniane są wszelkiego rodzaju zestawy formularzy, następujących po sobie w określonej kolejności, np. sklepy internetowe, formularze zamówienia czy rejestracja użytkowników.
Przykładowa klasa jest odporna na podmianę parametrów adresu URL, dzięki wykorzystaniu
funkcji wbudowanych PHP. Pierwszą z nich jest funkcja getenv(), która
służy do odczytywania wartości nagłówków HTTP. Odczytany nagłówek jest przechowywany
w zmiennej HTTP_REFERER, która przechowuje pełną ścieżkę do strony, z której
przyszedł użytkownik. Nasza klasa będzie sprawdzała czy nagłówek posiada odpowiednią
wartość dzięki czemu możliwe będzie odróżnienie czy użytkownik wchodzi
z właściwej strony, czy też nie.
Opisywana klasa ma budowę modularną, tzn. składa się z kilku funkcji, z których każda ma wykonywać określone zadanie. Pierwsza z nich została już opisana w poprzednim akapicie. Druga funkcja będzie odpowiedzialna za wyświetlanie informacji o nieprawidłowej stronie, z której przyszedł użytkownik oraz zapisywanie adresu IP tego użytkownika do pliku, trzecia zaś, będzie sprawdzać czy i ile razy określony użytkownik został już wcześniej zapisany. Czwarta funkcja będzie odpowiedzialna za kontrolę sesji.
Głównym celem tego artykułu, jak już było wspomniane, jest nauka wykorzystania klas jako podstawy tworzonych aplikacji. Nauczysz się jak dobrze rozplanować pisanie poszczególnych modułów, tak aby kod był elegancki i przejrzysty oraz by można go było zaimplementować do innych projektów PHP. Artykuł ten ukazuje wszechstronność użycia klas, które pozwalają na ponowne wykorzystanie kodu i na wykonywanie nawet bardzo skomplikowanych zadań w sposób prosty i efektywny.
Mimo że przykładowa klasa stworzona w tym tutorialu jest w pełni funkcjonalna, to służy ona tylko jako wstęp do programowania obiektowego, jako że nie pokazuje w pełni dostępnych możliwości. Na przykład, klasy mogą komunikować się i manipulować danymi zawartymi w bazach danych, a także mogą być rozszerzane o nowe funkcje, które nie były początkowo planowane.
Z założenia głównym przeznaczeniem klas było ich zastosowanie w dużych i skomplikowanych aplikacjach. Mimo to, mogą być one także bardzo użyteczne w mniejszych aplikacjach webowych, w szczególności jeśli planujemy ponowne użycie kodu. Wystarczy poświęcić trochę czasu na utworzenie wyspecjalizowanej klasy, wykonującej jakieś skomplikowane zadanie, aby zaoszczędzić sobie niepotrzebnego pisania tego samego lub podobnego kodu w przyszłości.
3. Cele kursu
Czytając ten tutorial, nauczysz się:
- Czym są klasy i kiedy ich używać
- Definiować klasy
- Definiować i używać funkcji wewnątrz klas
- Definiować i używać zmiennych wewnątrz klas
- Definiować i używać konstruktorów wewnątrz klas
- Tworzyć kompletne i funkcjonalne klasy
- Implementować instancje klas
- Ponownego użycia kodu
- Zmieniać wartość zmiennych klasy
- Używać następujących funkcji PHP:
- Używać następujących operatorów PHP
list()new
- Używać następującej zmiennej sesyjnej:
PHPSESSID
- Używać następujących zmiennych serwera
HTTP_REFERERREMOTE_ADDR
4. Informacje podstawowe
Co to jest klasa?
Klasa to zbiór powiązanych ze sobą zmiennych i funkcji (zwanych też atrybutami lub metodami). Ich zadaniem jest opisywanie i przetwarzanie danych pochodzących od określonego obiektu (lub klasy). Obiektem może być prawie wszystko co jest częścią kodu. Na przykład, klasa może służyć do opisywania pewnej porcji danych lub wykonywać operacje związane z obsługą systemu pocztowego serwera.
Dobra klasa jest dobrze zaprojektowanym systemem. przewiduje wszystkie możliwe zdarzenia, dzięki czemu dostajemy właściwą odpowiedź bez względu na rodzaj i ilość wprowadzonych danych lub parametrów. Dobre klasy działają jak potężny podzespół poleceń, działający niewidocznie ale wykonujący swoją pracę bezbłędnie.
Jako przykład użycia klasy rozpatrzymy następujący przypadek: system rezerwacji linii lotniczych. System posiada następujące funkcje: zarządzanie lotami, ustalanie rozkładu lotów dla załogi oraz rezerwację biletów na samoloty. Każda funkcja jest częścią klasy. Jako pasażer, nie masz dostępu do samych funkcji, ale możesz zobaczyć ich atrybuty: numer lotu, czas odlotu i przylotu, cena biletu oraz czy są miejsca przy oknie. Wywołanie jednej z funkcji, np. rezerwacja biletu, powoduje zmiany kilku atrybutów klasy: informacja o częstości lotów pasażera, potrzebna ilość posiłków w czasie lotów oraz informacja o tym, czy są jeszcze wolne miejsca.
Mając utworzoną taką klasę, można ją wykorzystać ponownie przy tworzeniu systemu rezerwacji dla innych linii lotniczych. Nie ma potrzeby pisać nowego systemu rezerwacji. Tak właśnie działają klasy w programowaniu. Pozwalają one na tworzenie ogólnych funkcji, których można użyć w różnych aplikacjach. Aby użyć funkcji zdefiniowanych w określonej klasie wystarczy tylko zadeklarować jej instancję ("utworzyć nową kopię").
W pewnym sensie, już od dawna używasz klas programując w PHP, np. tworząc
uchwyt do pliku przy jego otwieraniu, tworzysz obiekt. Odbierając dane z bazy
danych SQL, otrzymujesz obiekt i zestaw jego atrybutów, np. przy użyciu funkcji
numrows. Pomyśl o tym, jak te informacje odnoszą się do tego,
co powiedzieliśmy wcześniej, a zrozumiesz jak bardzo klasy ułatwiają ci życie.
5. Wskazówki wstępne
Zawsze bądź zorganizowany. Kod PHP zawsze przechowuj w plikach z rozszerzeniem .php. Klasy przechowuj w plikach o rozszerzeniu .inc i dołączaj je tylko wtedy, gdy są ci potrzebne.
Kod naszej przykładowej klasy został napisany z myślą o dobrym stylu programowania. Klasy i funkcje mają z reguły budowę modularną. Każda funkcja jest krótka i zwięzła oraz wykonuje najwyżej dwa zadania.
W miarę jak aplikacje rozrastają się i kod staje się coraz bardziej skomplikowany, klasy pomogają utrzymać porządek i uniknąć dublowania nazw zmiennych i funkcji. Dzięki klasom unikniesz błędów związanych z nazewnictwem, jeśli pracujesz w zespole. Zmienne i funkcje w klasach nie są widoczne poza nimi, dzięki czemu nie ma problemów z nadawaniem im nazw.
Klasy są bardzo dobrym narzędziem służacym do optymalizowania czasu pracy. Solidnie napisana klasa pozwala na wielokrotne wykorzystanie kodu, dzięki czemu oszczedzasz na czasie i pracy.
6. Jak to działa
Każdy krok ilustrowany jest odpowiednim przykładem. Skrypty PHP umieszczamy pomiędzy
znacznikami <?php oraz ?>
Definiowanie klasy
Poniższy przykład pokazuje sposób definiowania klasy poprzez nadanie jej nazwy. Zauważ, że cała treść klasy powinna znajdować się pomiędzy nawiasami klamrowymi { oraz }.
Definiowanie klasy przy pomocy deklaracji jej nazwy.
class Security {
...
}
Nazwą klasy może być każde słowo za wyjątkiem słów kluczowych PHP.
Weryfikacja adresu strony
- Deklaracja funkcji klasy o nazwie
verifyReferer - Akceptacja wartości zmiennej
$ref, która wskazuje na stronę, z której powinien wejść odwiedzający - Sprawdzenie rzeczywistej strony, z której nastąpiło wejście, przy pomocy odczytania jej nagłówka (HTTP_REFERER).
- Jeśli wejście nastąpiło z niewłaściwej strony, nastąpi wywołanie funkcji printbadRef (funkcja zdefiniowana jest w dalszej części kodu).
class Security {
...
function verifyReferer ($ref) {
$headerref =
getenv("HTTP_REFERER");
if ($headerref == $ref) {
return true;
} else {
$this -> printbadRef ();
}
}
}
Nagłówek HTTP_REFERER może zawierać więcej informacji niż znajduje się
w zmiennej $ref, dzięki czemu porównywane wartości mogą nie być identyczne.
Nie oznacza to wcale, że użytkownik, który wszedł na stronę jest na pewno hakerem. Sposób radzenia sobie z
tym problemem został przedstawiony na końcu artykułu. Tutaj skupiliśmy się tylko na najważniejszych sprawach w celu zachowania przejrzystości.
Funkcje w klasach deklaruje się dokładnie tak samo jak poza nimi. Różnica między funkcją klasy i zwykłą funkcją polega na tym, że ta pierwsza może zostać wywołana tylko przy pomocy instancji danej klasy.
Wewnątrz klasy, zapis w postaci $this -> oznacza
"ten obiekt", czyli egzemplarz klasy, z której został on wywołany.
Pozwala to funkcjom obiektu na dostęp do jego zmiennych i innych funkcji.
Wywoływanie funkcji wewnątrz klasy dokonuje się przy pomocy następującej składni
$this->printbadRef();.
Zauważ, że przy pomocy jednej metody (verifyReferer)można wywołać inną metodę
tej samej klasy (printbadRef), zanim ta druga została zdefiniowana.
Zasada ta odnosi się także do zwykłych funkcji w PHP 4, ale dla tych, którzy korzystali
do tej pory z PHP 3 może wydawać się nieco dziwna.
Mechanizm obsługi nieprawidłowych adresów
Struktura klasy
-
Deklaracja metody klasy o nazwie
printbadRef. - Wyświetlenie strony z informacją o błędzie (jeśli wejście nastąpiło z niewłaściwej strony).
-
Odczytanie adresu IP odwiedzającego (
REMOTE_ADDR) przy pomocy nagłówka wysyłanego razem z żądaniem wyświetlenia strony. - Zapisanie do pliku "badips.txt" adresów IP użytkowników, którzy weszli na stronę w niewłaściwy sposób, w celu wykrycia powtarzających się ataków.
class security {
...
function printbadRef {
include ("badref.html");
$myfile = fopen ("badips.txt", "a");
fputs($myfile, getenv("REMOTE_ADDR")."\n");
fclose($myfile);
exit;
}
}
Wskazówki
Zwróć uwagę na modularną budowę klasy. Funkcja verifyReferrer sprawdza,
czy wejście nastąpiło z właściwej strony. Jeśli tak, to wywoływana jest funkcja printbadRef,
której zadaniem jest wyświetlenie informacji o błędzie oraz zapisanie adresu IP użytkownika do pliku. Następnie funkcja
verifyIP sprawdza czy określony adres IP sie nie powtórzył. Każda z tych funkcji
jest krótka i wykonuje tylko jedno zadanie.
Sprawdzanie adresu strony, z której nastąpiło wejście
Mamy już zadeklarowaną klasę Security. Aby skorzystać z jej funkcjonalności,
musimy utworzyć jej nowy obiekt. Kod w poniższym przykładzie sprawdza czy dany użytkownik
wszedł z odpowiedniej strony page1.phtml.
Zadania do wykonania
- Utworzenie egzemplarza klasy o nazwie
Security - Blokowanie użytkowników wchodzących ze stron innych niż
page1.phtml
$sec = new Security;
$sec->verifyReferer("http://www.here.com/page1.phtm");
Wskazówki
Aby utworzyć nowy egzemplarz klasy, należy użyć konstruktora new
przy zastosowaniu następującej składni:
$sec = new Security;
Powyższy przykład tworzy nowy egzemplarz (obiekt) klasy o nazwie Security.
Obiektami operuje się w taki sam sposób jak zwykłymi zmiennymi przechowującymi liczby lub ciągi znaków. Dzięki temu można bez problemu modyfikować jeden obiekt klasy bez obawy, że naruszymy inne jej obiekty. W tym samym czasie może być aktywnych wiele różnych egzemplarzy jednej lub wielu klas. Oczywiście nazwy obiektów muszą być unikalne.
W celu wywołania funkcji obiektu klasy, należy zastosować następującą składnię:
$sec->verifyReferer();
Tej samej składni użyliśmy do wywoływania funkcji wewnątrz klasy w przykładzie powyżej:
$this->printbadRef();
A więc, aby odwołać się do funkcji wewnątrz klasy, należy zastosować składnię:
"obiekt->funkcja"
Można także zastosować alternatywną metodę wywoływania funkcji w postaci: Class::function($vars).
Przy zastosowaniu tej metody zmienna $this nie jest deklarowana. Zapis $vars
oznacza parametry funkcji.
Wykrywanie powracających złośliwych użytkowników
Poniższy kod przedstawia funkcję o nazwie verifyIP,
która sprawdza, czy dany użytkownik próbował już wcześniej wejść na stronę w niewłaściwy sposób.
Jeśli adres IP takiego użytkownika figuruje już na "czarnej" liście, to następuje
wyświetlenie strony z błędem nawet gdy wchodzi on tym razem z dobrej lokalizacji.
Jest to dość brutalne traktowanie użytkownika, który mógł popełnić po prostu błąd lub powziąć postanowienie poprawy. Na samym końcu tego artykułu znajduje się trochę mniej radykalna wersja kodu. Tutaj opisane są tylko najważniejsze części funkcji.
Struktura
- Deklaracja funkcji o nazwie
verifyIP - Odczytanie adresu IP użytkownika
- Jeżeli w pliku
badips.txtznajdują się jakieś zapisy, to jego zawartość zostaje załadowana jako tablica do zmiennej $myfile - Sprawdzanie czy dany adres IP figuruje już w bazie danych
- Jeśli adres użytkownika znajduje się na "czarnej" liście to następuje wywołanie
funkcji
printbadRef
class Security {
...
function verifyIP () {
$IP = getenv("REMOTE_ADDR");
if (file_exists("badips.txt")) {
$myfile = file("badips.txt");
}
for ($index = 0; $index < count($myfile); $index++) {
if ($IP == chop($myfile[$index])) {
$this->printbadRef();
}
}
}
...
}
Wskazówki
Zmienna, np. $IP, która jest zdefiniowana wewnątrz funkcji klasy,
dostępna jest w każdym miejscu tej funkcji. Zachowanie to jest podobne do zwykłych funkcji.
Sprawdzanie linku referencyjnego oraz wykrywanie powracających "złośliwców".
Rozszerzymy teraz trochę funkcjonalność naszego poprzedniego kodu. Poza sprawdzaniem czy wejście nastąpiło z odpowiedniej strony, będziemy wykrywać czy dany użytkownik próbował już dokonać tego wcześniej czy nie.
Struktura
- Zdefiniowanie nowej instancji klasy
Security - Zezwolenie użytkownikowi na odwiedzenie strony tylko w przypadku gdy wejście nastąpiło
ze strony o nazwie
page1.phtml - Sprawdzenie czy złośliwy użytkownik już kiedyś wchodził na stronę
$sec = new Security;
$sec -> verifyReferer("http://www.here.com/page1.phtml");
$sec->verifyIP();
Wskazówki
Należy być bardzo ostrożnym implementując taką funkcję do sprawdzania adresu IP. Może się zdarzyć, że niesłusznie zabronimy dostępu niektórym użytkownikom, którzy na stronę weszli przypadkowo (np. stary adres z ulubionych). Może się też zdarzyć, że przy dynamicznym przydzielaniu adresów, adres zapisany na "czarnej liście" należy już do innej osoby. Rozwiązaniem mogłoby być określenie jakiegoś przedziału czasu, w którym użytkownik z pod danego adresu IP nie może wejść na stronę.
Funkcje sesji
Nasza następna funkcja, służąca do obsługi sesji, będzie zezwalała na poruszanie się po stronie tylko tym użytkownikom, których sesja jest aktywna. W wielu przypadkach ważne jest, aby nie blokować użytkownikom dostępu do jakichkolwiek podstron, ale zezwalać im na poruszanie się po stronie tylko gdy aktywna jest aktualna sesja. Taka kontrola sesji może być użyteczna w aplikacjach służących do odbioru poczty email. Dzięki niej odchodząc od monitora na dłuższy czas, użytkownik nie musi się obawiać, że ktoś przeczyta jego korespondencję ponieważ sesja wygasa po określonym czasie.
Struktura
-
Deklaracja funkcji
verifySession - Sprawdzanie czy istnieje już domyślna zmienna sesji
$PHPSESSID - Wyświetlanie strony z błędem jeśli nie istnieje aktywna sesja
class Security {
...
function verifySession() {
if (!isset($PHPSESSID)) {
$this->printbadRef();
}
}
...
}
Wskazówki
Funkcja verifySession może być wywołana tylko po uprzednim
rozpoczęciu lub zarejestrowaniu sesji. Dodatkowo, chcąc zmienić nazwę sesji, należy dokonać
także zmian w kodzie funkcji.
Podsumowanie funkcji bezpieczeństwa
Na koniec utworzymy funkcję, w której zsumowane będą możliwości wszystkich funkcji bezpieczeństwa. Użytkownik, który nie przeszedł nawet tylko jednego testu, nie będzie mógł obejrzeć strony.
Struktura:
- Deklaracja funkcji
verifySecurity - Sprawdzanie czy użytkownik wchodzi z właściwego adresu
- Sprawdzanie czy istnieje aktywna sesja
- Sprawdzanie czy użytkownik ma już na swoim koncie jakieś nielegalne działania
class Security {
...
function verifySecurity($expectedpage) {
$this->verifyReferrer($expectedpage);
$this->verifySession();
$this->verifyIP();
}
...
}
Wskazówki
Zauważ w jaki sposób funkcja verifySecurity przekazuje parametr
zawierający właściwy link referencyjny ($expectedpage)). Parametr
przekazywany jest bezpośrednio do funkcji verifyReferrer.
Konstruktory
Inną właściwością klas są konstruktory. Konstruktory to funkcje, które
wywoływane są podczas tworzenia nowych obiektów klasy. Konstruktor może służyć do określania
domyślnych wartości i atrybutów. Dobrym przykładem użycia konstruktorów w klasie
Security byłoby zezwolenie projektantowi na stworzenie jego własnej
wersji strony badref.html. Dzięki temu możliwe jest stworzenie
różnych stron dla każdego działu witryny, zamiast tworzenia jednego, bardzo dużego pliku dla
wszystkich.
Aby utworzyć konstruktor, który uruchamia się automatycznie podczas tworzenia klasy,
należy nadać tę samą nazwę zarówno funkcji jak i klasie. W poniższym przykładzie,
za każdym razem kiedy tworzona jest nowa instancja klasy Security,
automatycznie uruchamiany jest konstruktor o tej samej nazwie, który nadaje domyślną wartość
zmiennej $badref.
Struktura
- Deklaracja zmiennej
$badref. - Deklaracja konstruktora
Security. - Nadanie domyślnej wartośći zmiennej
$badref.
class Security {
...
var $badref;
function Security {
$this -> badref = "badref.html";
}
...
}
Wskazówki
Wszystkie zmienne wykorzystywane przez klasę muszą być zadeklarowane przed jakąkolwiek funkcją lub konstruktorem.
Aby odwołać się do zmiennej $badref wewnątrz kodu klasy
należy użyć składni: $this->badref
Podobnie odnosimy się do zmiennej klasowej $badref
wewnątrz kodu aplikacji (gdzie zdeklarowana jest instancja klasy o nazwie
sec). Składnia jest następująca: $sec ->badref.
Jak widać powyżej, nazwa zmiennej badref nie występuje samodzielnie
i nie ma przed nią znaku $. Dzieje się tak dlatego, że aby skorzystać ze zmiennej należy posłużyć się
składnią "$obiekt->nazwazmiennej", a jak wiadmomo klasa jest obiektem. Identyfikatorem
zmiennej jest zapis w postaci "obiekt->nazwazmiennej" z poprzedzającym znakiem "$".
W naszym przykładzie nazwa zmiennej to obiekt->badref a nie
samo badref. Dzieje się tak dlatego, że zmienna badref
jest własnością obiektu (zmienną klasy Security).
Należy zauważyć, że zapis "$object->nazwazmiennej" to nie jest to samo co
"$object ->$nazwazmiennej". Drugi zapis spowoduje podstawienie zawartości zmiennej
$nazwazmiennej w jej miejsce a jej nazwa zostanie zmieniona na "$object->zawartosc
zmiennej $nazwazmiennej".
W języku PHP 4 nie ma destruktorów, które znane są programistom C++, ponieważ PHP automatycznie zwalnia nieużywaną pamięć. Więcej informacji na ten temat można przeczytać w artykule Reference Counting, którego autorem jest Andy Gutmans.
Zmiana strony nieprawidłowego odwołania
Funkcja o nazwieset_badref pozwala aplikacji na zmianę
wartości zmiennej $badref przy pomocy klasy
Security. To znaczy, zmienia ona stronę docelową
w przypadku gdy użytkownik okaże się potencjalnie niebezpieczny.
Struktura
class Security { ... function set_badref($badref_page) { $this ->badref = $badref_page; } ... }
Wskazówki
Pamiętaj: Elastyczność i możliwość wprowadzania zmian wartości domyślnych są najważniejszymi cechami wyróżniającymi dobrze napisaną klasę. Klasa powinna nie tylko być prosta w użyciu, ale także pozwalać na dokonywanie zmian według indywidualnych potrzeb użytkowników.
Zmiana strony docelowej dla użytkowników wchodzących z nieprawidłowego adresu
Aby zmienić lokację $badref w skrypcie, należy
wywołać set_badref natychmiast po utworzeniu
nowej instancji klasy. Wtedy odwiedzający zostanie przekierowany do
nowej strony, zamiast do domyślnej badref.html.
Lokalizację można zmieniać wielokrotnie, w zależności od tego, co zrobił
użytkownik.
Struktura
$sec = new Security; $sec->set_badref("5yearsand50000dollars.html"); $sec->verifyReferer("http://www.here.com/page1.phtml");
Wskazówki
Nie ma potrzeby korzystać z wszystkich możliwości klasy. Nie wywołuj set_badref
jeśli nie ma ku temu wyraźnej potrzeby.
Jeśli sytuacja wymaga użycia różnych wartości zmiennej $badref,
to ich zmiany należy dokonywać przy użyciu set_badref.
7. Kompletny kod
Poniżej znajduje się pełny kod aplikacji, opatrzony wieloma komentarzami.
Klasa security została wzbogacona o dodatkowe
linijki kodu, aby podnieść jej niezawodność.
Ważne:
Przykładowy kod umieszczony jest w dwóch plikach (.inc oraz .php), tak jak radziłem we "Wskazówkach wstępnych".
Komentarze poprzedzone są znakami "//". Kod PHP zawarty jest pomiędzy znacznikami "<?php" i "?>".
Plik 1: Klasa Security
<?php // Deklaracja klasy poprzez nadanie jej nazwy class Security { // zmienne klasy muszą zsotać zadeklarowane przed funkcjami var $badref; // Ten konstruktor klasy ma taką samą nazwę jak sama klasa, // dzięki czemu jest on wywoływany automatycznie za każdym razem, // gdy tworzona jest nowa instancja klasy. Konstruktor ten ustawia // wartość domyślną dla "strony nieprawdiłowego odwołania". function Security() { $this->badref = "badref.html"; } // Funkcja ta sprawdza skąd przyszedł dany użytkownik, // przy pomocy funkcji wbudowanej PHP "getenv" oraz // zmiennej HTML HTTP_REFERER function verifyReferer($ref) { $headerref = getenv("HTTP_REFERER"); // Funkcja wbudowana PHP "ereg" przeszukuje łańcuchy znaków // w poszukiwaniu wyrażeń regularnych. W tym przypadku wyrażeń, // w których znakiem rozdzielającym jest znak "?". if(ereg("\?" ,$headerref)) { // Funkcja wbudowana PHP "split" rozbija łańcuch na dwie // części. W tym przypadku podział następuje w miejscu, gdzie // pojawia się pierwszy znak "?". Pierwsza część // rozbitego łańcucha przechowywana jest jako parametr // operatora "list", a druga część jako jego drugi // parametr. Strona, z której nastąpiło wejście, lub jej // adres, jest reprezentowana przez tę część łańcucha // HTTP_REFERER, która znajduje się przed pierwszym "?". list($url,$getstuff) = split("\?",$headerref); $headerref = $url; } if ($headerref==$ref) { return true; } else { // Użytkownik wszedł z niewłaściwej strony, // wyświetli się więc strona błędu $this->printbadRef(); } } // Poniższa funkcja sprawdza, czy adres IP danego użytkownika nie // znajduje się na liście użytkowników wchodzących z niewłaściwej // lokalizacji, nawet jeżeli tym razem wchodzi on ze strony // prawidłowej. function verifyIP () { $count = 0; // Sprawdzanie IP użytkownika przy pomocy funkcji PHP "getenv" // oraz zmiennej HTML REMOTE_ADDR $IP = getenv("REMOTE_ADDR"); if (file_exists("badips.txt")); // Wczytaj zawartość pliku do tablicy, której każdy element // zawiera jedną linijkę zawartości pliku // (linijki oddzielone są od siebie znakiem nowego wiersza) $myfile = file("badips.txt"); } for ($index=0; $index < count($myfile); $index++) { // Użycie funkcji "chop" w celu usunięcia znaku nowej linii if ($IP == chop($myfile[$index])) { $count++; } } // reaguj tylko w sytuacji, gdy dany adres IP powtórzył się // conajmniej trzy razy, zamiast używania stałej liczby (3). // Przydatne mogłyby się okazać następujące kroki: // 1) deklaracja zmiennej klasy, np. $badcount; // 2) ustawienie wartości domyślnej w // konstruktorze Security, np. 3 // napisanie funkcji klasy, np. set_badcount, aby // umożliwić zmianę zmiennej $badcount przez aplikację. // Podobną procedurę zastosowano w przypadku zmiennej $badref if($count > 3) { $this->printbadRef(); } } // Poniższa funkcja sprawdza czy nie ma aktualnie otwartej sesji // sprawdzając zawartość zmiennej PHP $PHPSESSID function verifySession() { if (!isset($PHPSESSID)) { $this->printbadRef(); } } // Jedynym zadaniem tej funkcji jest połączenie funkcjonalności // wszystkich trzech funkcji, które wywołuje. Parametr zawierający // właściwy adres odwołania ($expectedpage) jest przekazywany wprost // do funkcji verifyReferer. function verifySecurity($expectedpage) { $this->verifyReferer($expectedpage); $this->verifySession(); $this->verifyIP(); } // Funkcja ta służy do wyświetlania strony błędu użytkownikowi, // który zrobił coś niedozwolonego. function set_badref($badref_page) { $this->badref = $badref_page; // Poniższa funkcja jest wywoływana, // gdy użytkownik wchodzi z niewłaściwej strony function printbadRef() { // Użycie funkcji PHP "include" do wyświetlenia // użytkownikowi strony błędu include($this->badref); // Wczytanie zawartości pliku do tablicy, której każdy // element (oddzielony znakiem nowej linii) // zawiera jedną linijkę zawartości pliku $myfile = fopen("badips.txt", "a"); // Dodawanie adresu IP użytkownika do listy użytkowników // wchodzących z niewłaściwej lokalizacji fputs($myfile, getenv("REMOTE_ADDR")."\n"); fclose($myfile); exit; } }
Plik 2: Segmenty skryptu PHP, który wykorzystuje funkcjonalność klasy Security
<?php ... // Deklaracja nowej instancji ("sec") klasy Security. // Do każdej funkcji w klasie (wewnątrz instancji "sec"), // należy odwoływać się przy pomocy następującej składni: // "$sec->function()" $sec = new Security; // Zmiana strony nieprawidłowego odwołania. // Funkcja ta jest opcjonalna, jako że strona nieprawidłowego odwołania // ma ustawioną wartośćdomyślną, który automatycznie jest ustawiana // przez konstruktor dla każdej nowej instancji klasy Security $sec->set_badref("5yearsand50000dollars.html"); // Porównaj stronę, z której wszedł użytkownik z adresem // podanym do funkcji jako parametr $sec->verifyReferer("http://www.here.com/page1.phtml"); // Sprawdź czy istnieje aktywna sesja. Należy pamiętać, że funkcja // verifySession może być użyta tylko w przypadku, gdy została // przed jej wywołaniem uruchomiona lub zarejestrowana sesja. // Dodatkowo, należy wnieść poprawki do kodu jeśli zmienia się // nazwę sesji (używając session_name). $sec->verifySession(); // Sprawdź czy adres IP danego użytkownika znajduje się // na liście użytkowników wykonujących nielegalne działania, // nawet jeśli tym razem nie robi nic podejrzanego $sec->verifyIP(); ... // Jedynym zadaniem tej funkcji jest wywołanie funkcji: // verifyReferer, verifySession oraz verifyIP. Link strony, // z której wchodzi użytkownik porównywany jest z adresem // podanym jako parametr funkcji. $sec->verifySecurity("http://www.here.com/page1.phtml"); ... ?>
8. Przydatne adresy
Informacje na temat klas w PHP
http://www.zend.com/manual/language.oop.php
Informacje na temat funkcji PHP "getenv()"
http://www.zend.com/manual/function.getenv.php
Informacje na temat zmiennych CGI/Header
http://hoohoo.ncsa.uiuc.edu/cgi/env.html
"Klasy i PHP"
http://www.phpbuilder.com/columns/rod19990601.php3
"Dostęp do baz danych przy zastosowaniu klas"
http://www.devshed.com/Server_Side/PHP/Class/
Autor: Duncan Lamb przy współpracy z Zend.
Data: 28.03.2000
Tłumaczenie: Łukasz Piwko
Oryginalna wersja artykułu znajduje się pod adresem
http://www.zend.com/zend/tut/class-intro.php