Kurs języka HTML i CSS

Poradnik webmastera

  • Zwiększ rozmiar czcionki
  • Domyślny  rozmiar czcionki
  • Zmniejsz rozmiar czcionki

Bezpieczeństwo serwisów WWW

Email Drukuj PDF

Tematy: Uwierzytelnianie i autoryzacja | Rzecz o hasłach | Śledzenie sesji | Konfiguracja PHP pod kątem bezpieczeństwa | Podsumowanie


Jeden rozdział poświęcony bezpieczeństwu serwisów WWW to zdecydowanie za mało. Ba, nawet 300-stronicowa książka też może się okazać zbyt ogólna i pobieżna. Ale chcąc tworzyć serwisy WWW, od zagadnień bezpieczeństwa nie uciekniemy.

Bezpieczeństwo stron WWW możemy rozpatrywać na kilku płaszczyznach. Zacząć powinniśmy niewątpliwie od systemu operacyjnego realizującego połączenie z siecią komputerową — system bezpieczny to system stabilny, mający relatywnie mało tzw. dziur (czyli luk w zabezpieczeniach), z dobrze chronionym i filtrowanym dostępem do sieci (w obie strony). Dalej powinniśmy zwrócić uwagę na serwer WWW — czy używamy najnowszej, stabilnej wersji tego oprogramowania i czy jest dobrze skonfigurowane (tzn. czy konfiguracja nie otwiera różnych możliwości mogących zaowocować np. nieprzewidzianym wykorzystaniem serwisów WWW). Jeśli nasz serwis przechowuje dane w bazie danych, należy upewnić się, czy system zarządzania bazami danych „nie wpuści” użytkownika, który nie ma odpowiednich uprawnień, i czy baza danych jest dobrze zaprojektowana oraz czy uwzględniono role poszczególnych użytkowników. Na koniec mamy PHP — w tym przypadku musimy szczególną uwagę zwrócić przede wszystkim na konfigurację PHP (plik php.ini), a jeżeli nie mamy odpowiednich uprawnień, musimy się skupić na poprawnym pisaniu kodu.

Najważniejszym jednak ogniwem bezpieczeństwa serwisów WWW jest sam programista. To on musi uwzględniać bezpieczeństwo od początku do końca procesu projektowania dynamicznych stron WWW. Jeśli problem bezpieczeństwa zostanie zbagatelizowany, wtedy w momencie, gdy będziemy chcieli zabezpieczyć nasz serwis, może się okazać, że najlepiej będzie stworzyć go od nowa… Oznacza to, że system i konfiguracja oprogramowania to jedna, bardzo ważna rzecz, a dobrze zaprojektowana strona WWW to druga rzecz, równie istotna.

Na rynku dostępnych jest kilka ciekawych opracowań na temat bezpieczeństwa stron WWW — każdy, kto planuje stworzenie dynamicznej strony WWW, powinien zapoznać się z którąś z tych pozycji (wiele dostępnych jest w formie elektronicznej na stronach WWW). Warto też regularnie odwiedzać stronę The Open Web Application Security Project (http://www.owasp.org).

W rozdziale przedstawiono wybrane podstawowe zagadnienia związane z bezpieczeństwem serwisów WWW (uwierzytelnienie i autoryzacja, konfigurowanie PHP, zabezpieczanie haseł) oraz wyjaśniono, jak obejść wady bezstanowego protokołu HTTP.

Uwierzytelnianie i autoryzacja

Nie będzie w tym punkcie prezentacji jedynie słusznej i jedynie bezpiecznej metody uwierzytelniania i autoryzacji w serwisach tworzonych w PHP, bo takiej po prostu nie ma. Wyjaśnione zostaną natomiast oba tytułowe pojęcia, ponieważ często bywają ze sobą mylone lub traktowane jako synonimy.

Uwierzytelnianie (ang. authentication) polega na stwierdzeniu wiarygodności użytkownika próbującego zarejestrować się w danym systemie. Wiarygodność stwierdza się w oparciu o nazwę użytkownika (identyfikator, login) i hasło. W danym systemie zwykle istnieje tylko jeden użytkownik o określonym identyfikatorze, natomiast hasła mogą się powtarzać dla różnych użytkowników. Jeżeli system uzna, że para identyfikator-hasło jest poprawna (dany użytkownik istnieje w systemie), użytkownik otrzymuje prawo dostępu do systemu. Czasem uwierzytelnianie tłumaczone jest z języka angielskiego jako „autentykacja”… No cóż, niewiele to mówi o istocie tego procesu.

W przeciwieństwie np. do ASP.NET PHP nie udostępnia zintegrowanego systemu uwierzytelniania (i autoryzacji), stąd użytkownicy (programiści) są zmuszeni do rozwiązywania tego problemu samodzielnie, przez formularze HTML lub — rzadziej — wykorzystując uwierzytelnianie HTTP. Proces uwierzytelniania należy — w miarę możliwości — prowadzić w bezpiecznym połączeniu (wykorzystującym SSL), co pozwoli na utrudnienie przechwycenia hasła wysyłanego do serwera przez sieć. Utrudnienie, a nie uniemożliwienie — gdyby się postarać, można zdobyć tak przesyłane hasło, choćby stosując strategię man-in-the-middle. Ewentualnie można wykorzystać technologię AJAX (lub po prostu skrypty JavaScript albo aplety Javy) do wstępnego zaszyfrowania hasła przed wysłaniem przez sieć (np. internet).

Autoryzacja (ang. authorization) to proces, który można by określić jako następny krok po uwierzytelnianiu. Otóż uwierzytelnianie użytkownika to tylko stwierdzenie, czy jest poprawnym użytkownikiem danego systemu. Autoryzacja daje nam coś więcej: możemy decydować, co dany użytkownik może, czyli jakie ma uprawnienia w stosunku do określonych zasobów, jakie operacje może wykonywać, a jakich nie. Autoryzacja jest ściśle związana z podziałem użytkowników na grupy, którym przydzielane są odpowiednie role. Tak więc są administratorzy systemu, użytkownicy mogący modyfikować dane (ale niemogący zmieniać np. struktury bazy danych), zwykli użytkownicy, którzy mogą jedynie przeglądać dane itp.

Proces autoryzacji i uwierzytelniania trzeba zawsze traktować bardzo poważnie. Zaleca się zwykle, aby unieważniać sesję (o sesjach więcej w dalszej części rozdziału) użytkownika, który zbyt długo pozostaje zalogowany i bezczynny (przykładem są banki elektroniczne), jak również wymuszać ponowne uwierzytelnianie w przypadku próby dostępu do szczególnie chronionych obszarów serwisu WWW. Wiadomo, że użytkownik może próbować się odwołać bez logowania do strony, do której dostęp jest możliwy tylko po pozytywnym uwierzytelnieniu — przecież można taką nazwę strony zapamiętać i spróbować wpisać jej adres w przeglądarce. W takim przypadku najlepszym rozwiązaniem jest natychmiastowe przekierowanie użytkownika na stronę logowania (można w tym celu wykorzystać funkcję header()), ewentualnie można go poinformować o tym fakcie. Często zdarzają się serwisy WWW, które nie unieważniają sesji raz zalogowanego użytkownika — daje to możliwość dostępu do strony np. za tydzień i korzystania z jej zasobów bez potrzeby rejestracji. A co, jeśli za ten tydzień skorzysta z tej strony całkiem inna osoba, która akurat usiadła przy tym komputerze? Wniosek: starajmy się zawsze używać opcji Logout lub Wyloguj — będziemy mieć wtedy pewność (choć też nie do końca), że następnym razem znów będziemy musieli się na daną stronę zalogować.

Bardzo ważnym elementem, od którego zaczyna się poniekąd mowa o bezpieczeństwie i z którym bezpieczeństwo najczęściej się kojarzy, jest hasło.

Rzecz o hasłach

Jedna z zasad używania haseł mówi, że hasło powinno być łatwe do zapamiętania. I większość użytkowników na tym poprzestaje — mamy więc imiona własne z datą urodzenia (właściwie to już jest lepsze hasło), nazwy miejscowości, imiona sympatii czy dzieci, ulubione kolory, imiona ulubionych zwierzaków itp. Najbardziej kuriozalnym przypadkiem jest chyba użycie loginu jako hasła. Na szczęście coraz więcej systemów nie pozwala na tego typu niefrasobliwość.

Dlaczego hasło powinno być „mocne”? Owszem, hasło powinno być dla nas łatwe do zapamiętania. Ale powinno też być na tyle skomplikowane, żeby nie było łatwe do odgadnięcia. Jeżeli hasło składa się z wyrazu, który można znaleźć metodą słownikową (przez dopasowywanie wyrazów ze słownika danego języka), to takie hasło jest bardzo słabe. Najlepiej, gdy hasło składa się z wyrazów (skrótów, skrótowców, anagramów itp.) poprzeplatanych cyframi. Dodatkowo warto używać dużych liter wraz z małymi. Mimo że niektóre podręczniki zalecają wplatanie do haseł znaków niebędących literami ani cyframi, lepiej ostrożnie podchodzić do tych znaków (niektóre systemy mogą pozwolić na wprowadzenie hasła zawierającego znaki spoza alfabetu i zbioru cyfr, a potem może się okazać, że użytkownik nie może się zalogować).

Haseł nie zapisujemy! Nie przyklejamy listy haseł na monitorze, nie nosimy w portfelu ani w organizerze. Musimy zaufać naszej pamięci.

W zasadzie nawet system nie powinien przechowywać jawnie hasła (w przypadku systemów operacyjnych tak jest w istocie). Najbardziej zalecaną metodą przechowywania hasła jest obliczenie dla niego wyniku funkcji mieszającej (funkcji skrótu, w PHP najczęściej stosuje się gotowe funkcje md5() lub sha1()) i zapamiętanie tego właśnie wyniku. Wynik obliczony dla danego hasła za pomocą funkcji mieszającej ma duży współczynnik unikalności — w zasadzie małe jest prawdopodobieństwo, że dwa hasła będą generowały taki sam wynik danej funkcji mieszającej.

Przykład użycia funkcji md5() i sha1() został pokazany na listingu 12.1, natomiast efekt przedstawiony został na rysunku 12.1.

Listing 12.1. Zastosowanie funkcji mieszających

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Usługa sieciowa</title>

</head>

<body>

<div>

<?php

  $haslo1 = "P@ssw0rD";

  $haslo2 = "P@ssw0rd"; // różnica tylko w ostatnim znaku

 

  print("$haslo1:<br />md5: ");

  $haslo1_md5 = md5($haslo1);

  print("$haslo1_md5<br />sha1: ");

  $haslo1_sha1 = sha1($haslo1);

  print("$haslo1_sha1<br /><br />");

 

  print("$haslo2:<br />md5: ");

  $haslo2_md5 = md5($haslo2);

  print("$haslo2_md5<br />sha1: ");

  $haslo2_sha1 = sha1($haslo2);

  print("$haslo2_sha1<br />");

?>

</div>

</body>

</html>

Rysunek 12.1. Wynik funkcji mieszających dla dwóch haseł

Z tego, co widzimy na rysunku 12.1, nawet dla dwóch bardzo podobnych do siebie haseł wyniki funkcji mieszających md5() i sha1() znacznie się od siebie różnią. Zauważmy też, że na podstawie długości wyniku funkcji mieszającej możemy stwierdzić, że sha1() jest „mocniejsza”, tzn. używając tej funkcji, zmniejszamy ryzyko wystąpienia dwóch podobnych wyników dla różnych haseł (choć to prawdopodobieństwo i tak jest bliskie zeru).

Algorytm użycia funkcji mieszającej dla haseł jest dość prosty: przy zakładaniu konta użytkownika (nie można zapomnieć o nakazaniu użytkownikowi potwierdzenia wpisanego hasła) podane przez niego hasło „przepuszczamy” przez funkcję mieszającą i wynik wprowadzamy do bazy danych użytkowników — oznacza to, że w rzeczywistości baza danych nie przechowuje haseł, tylko wynik funkcji mieszającej. Gdy do systemu loguje się istniejący użytkownik, hasło przez niego podane przechodzi przez funkcję mieszającą i porównywane jest z wynikiem przechowywanym w bazie — jeżeli porównanie wypadnie pozytywnie, użytkownik jest uwierzytelniony.

Śledzenie sesji

HTTP jest protokołem bezstanowym — oznacza to m.in., że każde żądanie strony jest osobną transakcją, niemającą nic wspólnego z poprzednią, nawet gdyby żądania dotyczyły stron wchodzących w skład tej samej aplikacji. Jednak tworząc współczesne aplikacje WWW, które są dynamiczne i interaktywne, musimy mieć do dyspozycji jakiś system czy sposób na zapamiętywanie przy kolejnych przejściach przez strony serwisu czynności wykonywanych przez użytkownika (lub ich wyników). Sposoby takie istnieją. W rozdziale tym opisano dwa sposoby przechowywania tego typu danych wyłącznie po stronie klienta oraz jeden pozwalający na przechowywanie danych po stronie serwera lub klienta (lub zarówno po stronie serwera, jak i klienta).

Dlaczego chcemy zapamiętywać stan strony dla danego użytkownika? Odpowiedź wydaje się być oczywista: żeby wiedzieć przy wejściu na następną podstronę (nowe żądanie HTTP), czy użytkownik jest zalogowany, czy ma odpowiednie uprawnienia, jakich zakupów dokonał itp.

Ukryte pola formularza

Najprostszym sposobem na przechowywanie stanu sesji (jako sesję rozumiemy tutaj proces wymiany danych pomiędzy klientem a serwerem od momentu zalogowania się użytkownika, do momentu jego wylogowania) są ukryte pola formularza. W tych polach możemy przechowywać różnego typu wartości (napisy, liczby). Ponieważ formularze HTML są obsługiwane przez wszystkie przeglądarki, sposób ten wydaje się być niezawodny. Jest tylko jedno „ale”: każdy użytkownik może bez problemu podejrzeć dane, które zostały „zaszyte” w ukrytych polach formularza, wystarczy, że przejrzy źródło strony (a każda przeglądarka daje taką możliwość — w niektórych przeglądarkach da się podgląd źródła wyłączyć, ale zawsze można użyć innej). Mimo to jednak spróbujemy stworzyć przykładową stronę, wykorzystującą ukryte pola formularza, żeby wyjaśnić zasadę działania tego mechanizmu.

Nasza strona będzie prosta: użytkownik zostanie zapytany o imię, a po przejściu na następną stronę imię to zostanie zapamiętane w ukrytym polu formularza w celu dalszego wykorzystania. Listingi 12.2 – 12.4 zawierają kody skryptów naszej aplikacji (index.html, welcome.php i go_on.php).

Listing 12.2. Skrypty prostej aplikacji WWW wykorzystującej zapamiętywanie stanu sesji w ukrytych polach formularza (plik index.html)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Rejestracja</title>

</head>

<body>

  <form action="welcome.php" method="post">

  <div>

  Podaj swoje imię:<br />

  <input name="imie" /><br />

  <input type="submit" value="Wyślij" name="submit" />

  </div>

  </form>

</body>

</html>

Listing 12.3. Skrypty prostej aplikacji WWW wykorzystującej zapamiętywanie stanu sesji w ukrytych polach formularza (plik welcome.php)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Powitanie</title>

</head>

<body>

<div>

<?php

  if (isset($_POST['submit'])) {

    $imie = trim($_POST['imie']);

    print("<b>Witaj $imie!</b><br />");

    // Wstawiamy formularz z ukrytym polem

?>

<form action="go_on.php" method="post">

<div>

<input type="hidden" name="imie" value="<?= $imie; ?>"/>

<input type="submit" value="Kontynuuj" name="submit" />

</div>

</form>

<?php

  } else {

    header("Location: index.html");

  }

?>

</div>

</body>

</html>

Listing 12.4. Skrypty prostej aplikacji WWW wykorzystującej zapamiętywanie stanu sesji w ukrytych polach formularza (plik go_on.php)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Kontynuacja</title>

</head>

<body>

<p>

<?php

  if (isset($_POST['submit'])) {

    $imie = trim($_POST['imie']);

    print("Użytkownik $imie kontynuuje pracę.<br />");

  } else {

    header("Location: index.html");

  }

?>

</p>

</body>

</html>

Użytkownik domyślnie kierowany jest na stronę, której kod znajduje się w pliku index.php. Zostaje zapytany o imię i — po kliknięciu przycisku Wyślij — skierowany na stronę wygenerowaną w oparciu o welcome.php. Na tej stronie zostaje powitany (po imieniu) i może udać się na następną stronę (tym razem nie podając imienia), klikając przycisk Kontynuuj. Poszczególne fazy działania tej aplikacji pokazują rysunki 12.2 – 12.4.

Rysunek 12.2. Faza pierwsza aplikacji — pytanie o imię

Rysunek 12.3. Faza druga aplikacji — powitanie

Rysunek 12.4. Faza trzecia aplikacji — wykorzystanie danych z ukrytych pól formularza

Na rysunku 12.5 pokazany został widok źródła strony z zaznaczonym ukrytym polem formularza przechowującym zapamiętaną wartość.

Rysunek 12.5. Widok źródła strony z ukrytym polem formularza

Ciasteczka

Ciasteczka (ang. cookies) to mechanizm pozwalający na zapamiętywanie po stronie klienta (przez przeglądarkę) danych wysłanych przez stronę WWW. Ciasteczka charakteryzują się różnym czasem trwałości — można tworzyć ciasteczka, których ważność wygasa wraz z sesją (po opuszczeniu danej strony, która generowała ciasteczko, lub po zamknięciu przeglądarki) lub których ważność wygasa po pewnym zdefiniowanym czasie. Ciasteczka są wysyłane do przeglądarki wraz z nagłówkiem żądanej strony, natomiast w drugą stronę, czyli od przeglądarki do serwera, ciasteczka są przesyłane wraz z żądaniem strony, dla której ciasteczko zostało ustawione. W przeciwieństwie do ukrytych pól formularza ciasteczka nie są tak łatwe do podejrzenia, choć niektóre przeglądarki udostępniają taką możliwość. Podstawową wadą ciasteczek jako mechanizmu śledzenia sesji jest możliwość ich zablokowania przez wszystkie popularne przeglądarki WWW. Blokowanie ciasteczek polega po prostu na ich nieprzyjmowaniu przez przeglądarkę.

W PHP ciasteczka wysyła się za pomocą funkcji setcookie(), której wywołanie ma schematyczną postać:

setcookie(nazwa[, wartość[, czas_życia[, ścieżka_dost[, domena[, bezpieczne]]]]])

Poszczególne argumenty mają następujące znaczenie:

  • nazwa — nazwa identyfikująca cookie.
  • wartość — wartość cookie.
  • czas_życia — określany również jako czas ważności znacznik czasu Uniksa określający, kiedy wygasa ważność cookie. Po tym czasie zostanie ono usunięte przez przeglądarkę.
  • ścieżka_dost — ścieżka dostępu na serwerze, do której zostanie ograniczona dostępność cookie. Na przykład ustawienie tego parametru na / powoduje, że cookie dostępne jest w całej domenie, a ustawienie /www/example/, że będzie dostępne jedynie przy odwołaniu do katalogu /www/example/ i katalogów podrzędnych w stosunku do /www/example/.
  • domena — określa domenę, w której cookie będzie dostępne. Na przykład ustawienie na .domain.com powoduje, że cookie będzie dostępne we wszystkich poddomenach domeny domain.com, natomiast ustawienie na www.domain.com powoduje, że będzie dostępne jedynie w obrębie domeny www.domain.com.
  • bezpieczne — ustawienie na 1 powoduje, że cookie będzie przesyłane jedynie przez bezpieczne połączenia HTTPS; 0 — że będzie przesyłane również przez zwykłe połączenia HTTP. Ustawieniem domyślnym jest 0.

Jedynym argumentem obligatoryjnym jest nazwa, wszystkie pozostałe, tak jak zostało to oznaczone nawiasami kwadratowymi, są opcjonalne.

Najczęściej korzysta się z trzech pierwszych parametrów: pierwszy z nich to nazwa ciasteczka, drugi to wartość ciasteczka, a trzeci to czas ważności ciasteczka. O ile dwa pierwsze parametry nie wymagają wyjaśnień, o tyle ostatniemu parametrowi należy poświęcić więcej uwagi. Wydawałoby się, że wystarczy jako trzeci parametr podać czas w sekundach — owszem, ale musimy wyliczyć sobie ten czas od początku „ery Uniksa”, czyli od 1 stycznia 1970 roku. Na szczęście funkcja time() zwraca liczbę sekund, które upłynęły od 1 stycznia 1970 roku do teraz — wystarczy do wyniku zwróconego przez tę funkcję dodać liczbę sekund, po upływie których ciastko ma wygasnąć. Przykład:

setcookie("imie", "Rafał", time() + 10 * 60);

pokazuje sposób na ustawienie ciasteczka o nazwie imie i wartości Rafał wygasającego po 10 minutach.

W przypadku, jeśli zostanie pominięty argument czas_życia, cookie będzie ważne jedynie do końca sesji, czyli aż do zamknięcia przeglądarki, po czym zostanie usunięte.

Należy pamiętać, że ponieważ funkcja setCookie wysyła nagłówek HTTP, powinna być wywołana przed wysłaniem jakichkolwiek innych danych, w tym wszelkich danych będących składnikami strony.

Aby usunąć ciasteczko, należy albo ustawić czas ważności, który już upłynął, albo ustawiać jako wartość cookie pusty ciąg znaków lub wartość false. Co jednak ważne, pozostałe argumenty wywołania funkcji w celu usunięcia cookie muszą być takie same, jak argumenty użyte do jego ustawienia. Przykładowo w celu usunięcia cookie o nazwie imie z poprzedniej sekcji można użyć jednej z konstrukcji:

setCookie("imie", "Rafał", time() - 100);

setCookie("Rafał", "");

setCookie("Rafał", false);

Dostęp do ciasteczek w PHP następuje poprzez superglobalną tablicę $_COOKIES. Jej indeksami są nazwy, czyli dostęp do cookie można uzyskać za pomocą konstrukcji o schematycznej postaci:

$_COOKIE['nazwa']

Sesje

PHP udostępnia mechanizm śledzenia sesji, zwany popularnie po prostu sesjami. To, w jaki sposób przechowywane są dane sesji (czy po stronie serwera, czy w przeglądarce), uzależnione jest od konfiguracji PHP. Zwykle jednak pozostawia się konfigurację domyślną (identyfikator sesji przesyłany jest w miarę możliwości jako ciasteczko lub doklejany do adresów w łączach, natomiast dane sesji przechowywane są na dysku serwera). Z sesją związany jest jej unikalny identyfikator, generowany za pomocą algorytmu MD5 lub SHA1 (zależnie od konfiguracji). Zwykle przesyłany jest jako ciasteczko (chyba że zablokujemy pobieranie ciasteczek) o nazwie PHPSESSID (nazwa możliwa do zmiany w pliku konfiguracyjnym PHP).

Aby rozpocząć sesję, należy na stronie początkowej serwisu użyć funkcji session_start(). Od tej pory, aby dane bieżącej sesji były widoczne na pozostałych stronach serwisu, należy na początku każdej strony wywoływać tę funkcję, ponieważ funkcja ta nie tylko rozpoczyna sesję, ale też odtwarza jej stan. Zmienne sesji przechowywane są w tablicy superglobalnej $_SESSION. Utworzenie nowej zmiennej sesji polega na dodaniu zmiennej do tej tablicy.

Po zakończeniu pracy z serwisem (np. po wylogowaniu się) należy zamknąć sesję — najlepiej w tym celu najpierw usunąć zmienne sesji (za pomocą funkcji unset()), a następnie zniszczyć sesję (funkcja session_destroy()). Jeśli identyfikator sesji przekazany został przez ciasteczko, należy to ciasteczko usunąć, a potem dopiero wywołać funkcję session_destroy().

Listingi 12.5 – 12.6 zawierają modyfikację skryptów z listingów 12.2 – 12.4 — tym razem dane sesji przechowywane są z wykorzystaniem udostępnianego przez PHP mechanizmu sesji (plik index.html pozostaje bez zmian).

Listing 12.5. Skrypty prostej aplikacji WWW korzystającej z zapamiętywania stanu sesji z użyciem mechanizmów sesji PHP (plik welcome.php)

<?php

  session_start();

  if (isset($_POST['submit'])) {

    $_SESSION['imie'] = $_POST['imie'];

  } else {

    header("Location: index.html");

  }

?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Powitanie</title>

</head>

<body>

<div>

<?php

  $imie = trim($_SESSION['imie']);

  print("<b>Witaj $imie!</b><br />");

?>

 <a href="go_on.php">Kontynuuj</a>

</div>

</body>

</html>

Listing 12.6. Skrypty prostej aplikacji WWW korzystającej z zapamiętywania stanu sesji z użyciem mechanizmów sesji PHP (plik go_on.php)

<?php

  session_start();

?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

  "http://www.w3.org/TR/html4/strict.dtd">

<html lang="pl">

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

  <title>Kontynuacja</title>

</head>

<body>

<p>

<?php

  if (isset($_SESSION['imie'])) {

    $imie = trim($_SESSION['imie']);

    print("Użytkownik $imie kontynuuje pracę.<br />");

    // Usuwanie zmiennej sesji

    unset($_SESSION['imie']);

    // Niszczenie sesji - przeładowanie w przeglądarce strony go_on.php

    // spowoduje wykonanie klauzuli "else"

    session_destroy();

  } else {

    header("Location: index.html");

  }

?>

</p>

</body>

</html>

W pliku welcome.php tworzona jest sesja (wywołanie na początku skryptu funkcji session_start()) i ustawiana jest zmienna sesji imie — wartość tej zmiennej została przekazana z poprzedniej strony jako wartość pola tekstowego formularza. Plik go_on.php rozpoczyna się wywołaniem funkcji session_start() (kontynuacja sesji) i jeśli ustawiona została zmienna sesji imie, wyświetlany jest napis, a następnie sesja jest niszczona: najpierw usuwana jest zmienna imie, a potem wywoływana jest funkcja session_destroy().

Ponieważ faza pierwsza wykonania aplikacji (plik index.html) jest dokładnie taka sama jak poprzednio, rysunki 12.6 – 12.7 przedstawiają fazę drugą i trzecią wykonania aplikacji (strony generowane przez welcome.php i go_on.php).

Rysunek 12.6. Faza druga aplikacji — wyświetlenie i zapamiętanie imienia

Rysunek 12.7. Faza trzecia aplikacji — wyświetlenie informacji i zniszczenie sesji

Warto przejrzeć dokumentację PHP poświęconą sesjom (http://pl.php.net/manual/pl/ref.session.php) — można tam znaleźć wiele dodatkowych informacji związanych z mechanizmem sesji.

Konfiguracja PHP pod kątem bezpieczeństwa

Oprogramowanie PHP, niezależnie od tego, czy w wersji źródłowej, czy binarnej, dostarczane jest wraz z przygotowanym do wykorzystania w warunkach produkcyjnych plikiem konfiguracyjnym. W większości zastosowań domyślna konfiguracja wystarczy, ale z czasem, kiedy liczba użytkowników serwera tworzących i publikujących własne dynamiczne strony WWW rośnie, warto zagłębić się w plik konfiguracyjny php.ini — można tam znaleźć kilka ciekawych rzeczy związanych z bezpieczeństwem zarówno strony WWW, jak i serwera.

Zmienne globalne

Do wersji 4.2 PHP rejestrował wszystkie zmienne niebędące zmiennymi lokalnymi jako globalne — niezależnie od tego, czy były one tworzone poprzez metody GET, czy POST protokołu HTTP, sesje, środowisko czy serwer. Owszem, taka sytuacja ułatwiała zdecydowanie pracę, ale niosła ze sobą wiele niebezpieczeństw — wystarczy tylko wspomnieć o jednym, najważniejszym: wprowadzeniu błędnej wartości dowolnej zmiennej globalnej przez URL (metoda GET). Jeśli przy okazji taka wartość nie była odpowiednio filtrowana, to przy odrobinie szczęścia (dla włamywacza) czy też pecha (dla nas) można było nieźle namieszać w systemie.

Za rejestrację wszystkich zmiennych jako globalnych odpowiada dyrektywa register_globals, obecnie domyślnie ustawiana na wartość off. Gdyby jednak przypisać tej dyrektywie wartość on, po restarcie serwera znów moglibyśmy używać wszystkich zmiennych dostępnych bezpośrednio w zasięgu globalnym. Najlepszym sposobem zabezpieczenia się przez zgubnymi wpływami dyrektywy register_globals jest zapomnienie o niej i stosowanie tablic superglobalnych. Tablice te są tablicami asocjacyjnymi, gromadzącymi różne zmienne z podziałem na kategorie. Tabela 12.1 zawiera listę tablic superglobalnych. Z tablic tych korzystaliśmy już niejednokrotnie, np. podczas odczytywania wartości parametrów przekazywanych za pomocą metody POST, odczytywania i zapisywania zmiennych sesji itp.

Tabela 12.1. Tablice superglobalne

Tablica

Zawartość

$GLOBALS

Zmienne dostępne w zasięgu globalnym skryptu.

$_SERVER

Zmienne utworzone przez serwer i związane ze środowiskiem uruchomieniowym skryptu.

$_GET

Zmienne utworzone za pomocą metody GET protokołu HTTP.

$_POST

Zmienne utworzone za pomocą metody POST protokołu HTTP.

$_COOKIE

Zmienne utworzone przez mechanizm ciasteczek.

$_FILES

Zmienne utworzone podczas przesyłania plików na serwer (upload).

$_ENV

Zmienne dostarczone do skryptu przez środowisko serwera.

$_REQUEST

Zmienne dostarczone do skryptu przez mechanizmy wejścia.

$_SESSION

Zmienne zarejestrowane jako zmienne sesji.

Izolowanie skryptów PHP

W sytuacji, gdy z serwera korzysta wielu użytkowników, warto zadbać o kontrolę nad PHP. Jak wiemy, PHP jest potężnym i wszechstronnym narzędziem, które niewłaściwie wykorzystane może spowodować wiele szkód. Na szczęście istnieje możliwość ograniczenia ryzyka związanego z niewłaściwym wykorzystywaniem skryptów PHP. Możliwość ta to izolowanie skryptów. Izolowanie wiąże się przede wszystkim z uniemożliwieniem skryptom „wychodzenia” poza określone drzewo katalogów oraz uniemożliwienie im dostępu do plików, które mają innego właściciela niż skrypty. Dodatkowo istnieje możliwość ograniczenia lub wyłączenia niektórych funkcji mogących mieć wpływ na osłabienie bezpieczeństwa serwera.

W systemach, w których wielu użytkowników może publikować dynamiczne strony WWW, warto ustawić w pliku php.ini wartość on dla dyrektywy safe_mode. Jeśli tak zrobimy, PHP będzie pracować w tzw. trybie bezpiecznym. Tryb ten pozwala skryptom dokonywać operacji wyłącznie na plikach, które mają tego samego właściciela co skrypt, oraz ogranicza działanie niektórych funkcji (czasem całkowicie) — listę tych funkcji można znaleźć w dokumentacji PHP. Jako uzupełnienie trybu bezpiecznego można przyjąć dyrektywę disable_functions, dzięki której możemy dodatkowo zabronić wykonywania funkcji, które naszym zdaniem mogą stanowić zagrożenie dla bezpieczeństwa systemu.

Na koniec warto wspomnieć o dyrektywie open_basedir. Dyrektywa ta pozwala na skonfigurowanie PHP tak, aby uniemożliwiał skryptom wykonywanie operacji plikowych powyżej określonego dyrektywą katalogu.

Podsumowanie

Ten bardzo krótki jak na rzeczywisty zakres tematyki bezpieczeństwa stron WWW rozdział stanowi wprowadzenie w zagadnienia bezpieczeństwa tworzenia stron WWW. Z przyczyn oczywistych zagadnienie bezpieczeństwa nie zostało tutaj potraktowane wyczerpująco. Jednak nie należy lekceważyć zagrożeń związanych z serwisami WWW, a w zasadzie z ich niewłaściwym wykorzystaniem, ponieważ skutki zaniedbań i beztroski mogą się okazać przeogromne. Jeśli jest się administratorem serwera, na którym inni użytkownicy również korzystają z PHP, trzeba zadbać o poprawną konfigurację tego środowiska — nie można dopuścić do sytuacji, kiedy prosty skrypt spowoduje, że jakiś spryciarz „wjedzie” na serwer i narobi szkód, czasem bardzo kosztownych.

Warto drążyć dalej tematykę bezpieczeństwa. Pamiętać jednak należy, że tak jak nie istnieją bezpieczne i w 100% bezbłędne programy, tak nie będą nigdy istnieć strony WWW zabezpieczone w 100%. Rzecz w tym, żeby zminimalizować wszelkie niebezpieczeństwa.

 

PHP i HTML. Tworzenie dynamicznych stron WWW

PHP i HTML.
Tworzenie dynamicznych stron WWW

PHP 5. Narzędzia dla ekspertów

PHP 5. Narzędzia dla ekspertów

PHP. Praktyczne projekty

PHP. Praktyczne projekty

Nowości Helionu

Statystyki

Użytkowników : 766
Artykułów : 513
Zakładki : 28
Odsłon : 15449242