Apache i skrypty PHP

background image

1

Referat

Temat: Apache i skrypty PHP

Wykonali:

Zbigniew Kiliański
Paweł Ścipień
Artur Zając

Prowadzący zajęcia:

mgr inż. Bogusław Juza

Data:

28-05-2002

Rok studiów:

IV

Rok akademicki:

2001/2002

background image

2

Spis tresci:

1.

Wstęp

.................................................................................................................................................................. 3

2.

Metody uruchomienia programu napisanego w PHP

........................................................................................... 4

2.1.

Obsługa PHP w kompilowana w Apache statycznie,

.................................................................................. 4

2.2.

Moduł PHP ładowany dynamicznie do Apache,

......................................................................................... 5

2.3.

Skrypty PHP uruchamiane jako tzw. skrypty CGI,

...................................................................................... 7

2.4.

Skrypty PHP uruchamiane z wykorzystaniem biblioteki "fastcgi",

............................................................. 9

3.

Tryb safe mode

.................................................................................................................................................. 10

4.

Analiza kodu suexec

.......................................................................................................................................... 12

5.

Pomysły na rozbudowanie kodu suexec

............................................................................................................ 21

5.1.

Modyfikacja suexec tak, aby uruchamiać pliki PHP z uprawnieniami właściciela pliku:

......................... 21

5.2.

4.2 Obsługa limitów setrlimit().

............................................................................................................... 22

6.

Wnioski

............................................................................................................................................................. 28

7.

Literatura

........................................................................................................................................................... 29

background image

3

1. Wstęp

PHP jest szeroko stosowanym językiem, który służy do tworzenia witryn z dynamicznie,

zmieniającymi się stronami WWW. Język ten umożliwia generowanie stron po stronie serwera i

przekazywanie efektów działania programów w postaci kodu HTML interpretowanego przez

przeglądarki po stronie klienta. Swoją popularność język ten zawdzięcza swojej prostocie,

bogatym zbiorze funkcji operujących na danych, dużym możliwością podłączenia się z bazami

danych i doskonalej dokumentacji dostępnej na firmowej stronie WWW.

Strony WWW udostępniane są przez demona HTTPD działającego na serwerze.

Najpopularniejszym demonem świadczącym tego typu usługi jest demon Apache dostępny na

stronie (www.apache.org). Demon ten odpowiedzialny jest za udostępnianie stron klientom, oraz

za uruchamianie skryptów generujących te strony.

Ze wzglądów bezpieczeństwa serwer Apache nie jest uruchamiany z uprawnieniami

administratora systemu. Użytkownik, z którego uprawnieniami działa serwer Apache zwykle nie

posiada własnych plików i nie ma możliwości zapisu do plików, które nie są "zapisywalne" przez

każdego użytkownika. Taka konfiguracja systemu poprawia znacznie bezpieczeństwo aplikacji

działających na serwerze, ponieważ nawet złamanie demona nie daje możliwości zapisu lub

odczytu plików na serwerze, które maja zabezpieczony dostęp dla użytkowników innych niż

właściciel główny lub grupowy.

Czasami jednak istnieje konieczność uruchomienia skryptów, które posiadają uprawnienia

większe lub inne niż użytkownik, który jest właścicielem procesów Apache'a, ponieważ należy

np. zabezpieczyć dostęp do odczytu niektórych plików np. z hasłami do bazy danych. Problem

ten rozwiązuje tzw. wrapper o nazwie suexec. Plik programu suexec ma ustawiony bit suid dla

właściciela pliku i właścicielem pliku jest administrator systemu. Uruchomienie skryptów

wymagających specjalnych uprawnień polega na wywołaniu wrappera suexec przez proces

Apache, a następnie uruchomieniu przez wrappera właściwego skryptu. Dostęp do plików innych

użytkowników może być także zabezpieczony przez wykorzystanie trybu „safe mode” i

właściwej konfiguracji serwera.

Istnieje kilka sposobów uruchamiania skryptów PHP. Każdy ze sposobów ma swoje zalety

i wady, z którymi trzeba się pogodzić i w zależności od potrzeby należy wybrać odpowiednią

background image

4

metodę uruchamiania skryptów. Analiza problemów związanych z uruchomieniem skryptów

PHP zajmuje się ten referat.

2. Metody uruchomienia programu napisanego w PHP

Istnieje wiele sposobów uruchamiania skryptów PHP. Każdy z nich umożliwia uzyskanie

pewnych właściwości, zazwyczaj kosztem innych. Najważniejszymi metodami uruchamiania

skryptów PHP są:

2.1. Obsługa PHP w kompilowana w Apache statycznie,

Obsługę PHP można włączyć do kodów serwera Apache podczas kompilacji serwera (jest

to tzw. kompilacja statyczna). Zaleta takiej konfiguracji jest względnie bardzo duża szybkość

działania skryptów PHP. Serwer taki konfiguruje się w ten sposób, ze najpierw kompiluje się

PHP włączając informacje o ścieżce do kodów źródłowych Apache'a, a następnie kompiluje się

Apache'a.

Kompilacja statyczna Apache i PHP

$ gunzip -c apache_1.3.x.tar.gz | tar xf -

$ cd apache_1.3.x

$ ./configure

$ cd ..

$ gunzip -c php-4.2.x.tar.gz | tar xf -

$ cd php-4.2.x

$ ./configure --with-mysql --with-apache=../apache_1.3.x \

--enable-track-vars

$ make

$ make install

$ cd ../apache_1.3.x

$ ./configure --prefix=/www --activate-module=

src/modules/php4/libphp4.a

$ make

$ cd ../php-4.2.x

background image

5

$ cp php.ini-dist /usr/local/lib/php.ini

Konfiguracja php polega na edycji pliku /usr/local/lib/php.ini.

Następnie należy powiązać rozszerzenie pliku ze skryptem (np. .php) z interpreterem oraz

(opcjonalnie) rozszerzenie pliku (np. .phps) z interpreterem, który koloruje i wyświetla kod PHP

w przeglądarce. W tym celu należy zmodyfikować plik konfiguracyjny Apache httpd.conf lub

srm.conf i dodać:

AddType application/x-httpd-php .php

AddType application/x-httpd-php-source .phps

W wyniku kompilacji Apache'a tworzony zostaje serwer, który posiada "zaszytą" w sobie

obsługę PHP. Korzystając z tej metody można uzyskać najszybsza z możliwych obsługę

skryptów PHP. Szybkość ta wynika z tego, że podczas wykonywania skryptów PHP nie trzeba

doładowywać bibliotek dynamicznych, ani tworzyć procesów, które uruchamiają się długo w

stosunku do całkowitego czasu działania skryptów.

Wadą tej metody jest bardzo mała elastyczność konfiguracji. Chcąc zmienić wersje

interpretera PHP musimy przekompilować cały serwer Apache. Wada także jest fakt, ze skrypty

PHP uruchamiane tą drogą muszą dziedziczyć uprawnienia procesu, który je wywołuje, a w tym

wypadku musi to być Apache. Nie ma więc możliwości zmiany uprawnień skryptów.

Ograniczenie to można ominąć uruchamiając oddzielny serwer Apache na innym porcie

przez innego użytkownika i przekierowując niektóre zapytania na ten serwer (wykorzystując

dyrektywę Redirect).

2.2. Moduł PHP ładowany dynamicznie do Apache,

Druga prezentowana metoda jest kompilacja obsługi PHP jako moduł dynamicznie

ładowany podczas uruchamiania skryptów PHP. Najpierw kompiluje się serwer Apache,

włączając opcje obsługi dynamicznie ładowanych modułów. W tym celu podczas

konfigurowania źródeł Apache'a należy dodać do polecenia ./configure opcje

--enable_module=so

.

Następnie należy dokonać kompilacji modułu PHP z wykorzystaniem programu apxs,

który umożliwia automatyczną konfiguracje modułu w zależności od wykorzystywanej

konfiguracji Apache (np. opcja -DEAPI, gdy korzystamy z modułu modssl).

background image

6

Jeżeli mamy już zainstalowany serwer Apache wraz z obsługa dynamicznych modułów

PHP kompiluje się w nastepujący sposób:

$ gunzip -c php-4.2.x.tar.gz | tar xf -

$ cd php-4.2.x

$ ./configure --with-mysql --with-apxs

$ make

$ make install

Po skompilowaniu modułu PHP program instalacyjny umieszcza go w stosownym miejscu

(tj. w katalogu libexec/ pod nazwą libphp4.so).

Nast

ępnie należy skopiować i z edytować plik php.ini

$ cd ../php-4.2.x

$ cp php.ini-dist /usr/local/lib/php.ini

Następnie należy zmodyfikować plik httpd.conf.

Modyfikacja pliku httpd.conf polega na umieszczeniu dyrektyw ładujących moduł

PHP do Apacha oraz umieszczeniu dyrektyw informujących serwer, ze pliki o zadanym

rozszerzeniu powinny być interpretowane przez PHP.

Wiązanie plików z rozszerzeniem .php i .phps (rozszerzenia mogą być dowolne) z

interpreterem:

AddType application/x-httpd-php .php

AddType application/x-httpd-php-source .phps

Ładowanie modułu PHP do serwera Apache (wiersze te powinny być automatycznie

dodane, przez apxs).

LoadModule php4_module libexec/libphp4.so

AddModule mod_php4.c

W przeciwieństwie do poprzedniego rozwiązania, to daje możliwość zmiany ustawień

modułu PHP (np. dodanie obsługi GD) bez konieczności ponownej kompilacji kodów serwera

Apache. Zaleta ta rekompensuje bardzo niewielką (około 5%) utratę szybkości interpretacji

skryptów. Poważną wadą rozwiązania jest brak możliwości uruchamiania skryptów z

uprawnieniami właściciela, ponieważ skrypty PHP są uruchamiane przez użytkownika, z

uprawnieniami serwera Apache.

background image

7

Wadę tą da się ominąć uruchamiając serwer Apache na innym porcie przez odpowiedniego

użytkownika (podobnie jak miało to miejsce w poprzednio opisanej metodzie).

2.3. Skrypty PHP uruchamiane jako tzw. skrypty CGI,

Jedna z najczęściej używanych metod uruchamiania skryptów po stronie serwera jest

metoda zwana CGI. CGI jest specyfikacja interfejsu wymiany informacji pomiędzy aplikacją

serwera np. Apache, a programem/ skryptem napisanym przez użytkownika, którego głównym

(najczęstszym) zadaniem jest wygenerowanie strony HTML, która jest następnie przekazywana

użytkownikowi. Jedynym wymaganiem stawianym przez specyfikację CGI odnośnie języków

programowania jest to, ze program napisany w stosownym języku musi umieć odczytać dane ze

standardowego wejścia i musi umieć zapisywać dane na standardowym wyjściu. Ponieważ

praktycznie wszystkie języki spełniają te wymagania, istnieje możliwość napisania skryptów CGI

w dowolnym języku.

Główną wada skryptów CGI jest czas inicjacji programu i obciążenie systemu tym

spowodowane. Skrypt CGI, który jest uruchamiany przez program wymaga utworzenia tzw.

ciężkiego procesu. Narzut czasowy związany z tą czynnością jest niejednokrotnie większy niż

całkowity czas pracy skryptu, dlatego rozwiązanie to nie jest rozwiązaniem optymalnym w

przypadku skryptów PHP.

Czasem istnieje jednak konieczność uruchamiania skryptów PHP jako procesy CGI.

Najważniejsza zaleta tego rozwiązania jest możliwość uruchamiania skryptów z uprawnieniami

właściciela przy wykorzystaniu programu suexec.

Kompilacja kodów źródłowych PHP jest prostsza niż w przypadku wersji statycznie

wkompilowanej w kody Apacha i wersji dynamicznie ładowanego modułu, ponieważ

standardowo PHP kompiluje się jako interpreter, który powinien być uruchamiany jako CGI.

Kompilacje PHP jako interpreter CGI i 'uruchamiany z shella' przeprowadza się

następująco:

1) ./configure (parametry dołączanych bibliotek do PHP)

2) make

3) make install

background image

8

W wyniku tych komend zostanie utworzony plik php, który jest interpreterem skryptów

PHP. Następnie kopiuje się go do katalogu, który się znajduje np. w zmiennej PATH lub

katalogu, który przechowuje skrypty CGI (np. jeden z aliasów /cgi-bin/).

Istnieje także wiele sposobów skonfigurowania serwera Apache, tak aby uruchamiał

skrypty PHP jako CGI.

Najprostszym z nich jest nazywanie skryptów z użyciem rozszerzenia .cgi lub innego

wyspecyfikowanego przez następujacą dyrektywę w httpd.conf;

AddHandler cgi-script .cgi

Zawartość plików uruchamianych w ten sposób powinna wyglądać na przykład

następująco:

#!/usr/local/bin/php

<? phpinfo(); ?>

Wadą tego rozwiązania jest konieczność umieszczania ścieżki w pierwszym wierszu

skryptu. Jest to pewnym kłopotem, jeśli chcemy zmienić uprawnienia napisanych już skryptów

ponieważ zmuszeni jesteśmy wtedy do modyfikacji każdego skryptu. W jednym z następnych

punktów opisano modyfikację programu suexec, która omija tą wadę.

Pewnym rozwiązaniem jest dodanie nowego handlera do rozszerzenia pliku i powiązania z

nim akcji:

AddHandler php-script php

Action php-script /cgi-bin/php

Niestety sposób ten, nie jest obsługiwany przez suexec. Wiec bez modyfikacji kodów

programu suexec i Apache nie jest uruchomienie skryptów z uprawnieniami właściciela ( w ten

sposób).

Istnieje możliwość zmiany właściciela wykonywanych skryptów na wirtualnych domenach.

Konfiguracja tego sposobu zmiany użytkownika polega na zadeklarowaniu użytkownika i grupy

(który jest właścicielem skryptów).

<VirtualHost 192.168.1.1:80>

background image

9

ServerName www.test.com.pl

DocumentRoot /www/testcom

ErrorLog /www/log/testcom_error

TransferLog /www/log/testcom_transfer

User usrtest

Group grptest

</VirtualHost>

2.4. Skrypty PHP uruchamiane z wykorzystaniem biblioteki "fastcgi",

FastCGI jest biblioteka i modułem, który umożliwia przyspieszenie uruchamiania skryptów

CGI na serwerze. Więc przy użyciu FastCGI można częściowo rozwiązać problem z dużym

narzutem związanym z nakładem mocy obliczeniowej przeznaczonej na tworzenie procesu.

Przyspieszenie osiągnięte dzięki FastCgi wynika z tego, że moduł ten wczytuje z dysku i

inicjuje skrypty raz i przetrzymuje je w pamięci. Dzięki temu nie ma potrzeby tworzenia nowego

procesu dla każdego zapytania oddzielnie. Minimalizuje to w znaczący sposób obciążenie

komputera i przyspiesza czas reakcji na zapytanie.

Konfiguracja PHP różni się od wersji CGI jedynie opcja dodawana podczas

konfigurowania źródeł PHP. (moduł i biblioteka FastCgi powinna być już wcześniej

zainstalowana).

./configure --with-fastcgi

FastCGI jako moduł jest włączany do Apacha poprzez stosowne dyrektywy w httpd.conf.

LoadModule fastcgi_module libexec/mod_fastcgi.so

AddModule mod_fastcgi.c

Obsługę PHP jako FastCGI konfiguruje się wewnątrz pliku httpd.conf poprzez dodanie

dyrektyw:

Alias /fcgi-bin/ /www/fcgi-bin/

FastCgiSuexec /www/bin/suexec

AddType application/x-httpd-fcgi .fcgi

AppClass /www/htdocs/fcgi-bin/php.fcgi -processes 4

AddType application/x-httpd-fphp .fhtml

background image

10

Action application/x-httpd-fphp /www/htdocs/fcgi-bin/php.fcgi

Taka konfiguracja powoduje, ze skrypty są uruchamiane o wiele szybciej niż przez CGI.

Niestety skrypty są jednak uruchamiane wolniej niż przy pomocy modułu wkompilowanego w

Apache lub modułu dynamicznie dołączanego do Apache. Wadą rozwiązania jest także fakt, ze

podczas normalnej pracy serwera uruchomionych jest wiele interpreterów php (wersji dla fast-

cgi), które niekoniecznie są w danej chwili potrzebne. Powodują one jednak to, ze przy obsłudze

zapytania nie ma konieczności natychmiastowego uruchamiania (ładowania, tworzenia procesu)

interpretera i przez to obciążenie systemu jest mniejsze i czas reakcji szybszy.

3. Tryb safe mode

W przypadku administrowania serwerem z wieloma użytkownikami, którym nie można w

pełni zaufać istnieje konieczność ograniczenia pewnych możliwości jakie daje korzystanie z

PHP. Najważniejszymi potencjalnie niebezpiecznymi możliwościami są udostępnianie plików

leżących poza drzewem Apache’a na stronach WWW, uruchamianie programów (niezależnie

od tego gdzie leżą), zmiana zmiennych środowiskowych, korzystanie z potencjalnie

niebezpiecznych funkcji.

Aby ograniczyć niektóre prawa użytkownika korzystającego z PHP administrator może

wykorzystać tryb safe mode uruchamiania skryptów PHP. Tryb ten włączany jest przy pomocy

dyrektywy safe_mode w pliku konfiguracyjnym php.ini lub w pliku httpd.conf.

Przykład php.ini:

safe_mode = On

Przykład httpd.conf:

php_flag safe_mode on

Włączenie trybu safe mode powoduje zmodyfikowanie działania niektórych funkcji. W

większości są to funkcje wejścia/wyjścia. Modyfikacja ich polega w większości na tym, że przed

wykorzystaniem ich właściwej opcji np. na pliku sprawdzane jest czy plik należy do tej samej

osoby co właściciel. W przypadku funkcji uruchamiających inne skrypty lub programy

sprawdzane jest czy znajdują się one we właściwym miejscu.

Na działanie skryptu PHP w trybie safe mode wpływają następujące dyrektywy:

background image

11

safe_mode

Dyrektywa ta włącza lub wyłącza tryb safe mode oraz wpływ innych dyrektyw trybu safe

mode na działanie programu.


safe_mode_gid

Podczas operacji plikowych sprawdzane jest czy właściciel pliku, na którym wykonywana

jest operacja jest taki sam jak właściciel skryptu. W przypadku niezgodności właściciela pliku i

skryptów wykonanie operacji zostaje zabronione.

Jeżeli dyrektywa safe_mode_gid jest włączona w przypadku niezgodności właścicieli

pliku i skryptów sprawdzane jest także czy właściciel grupowy pliku i skryptów są takie same.

Jeżeli właściciel grupowy jest ten sam to operacja wejścia/wyjścia jest wykonywana.


open_basedir

Dyrektywa, ta specyfikuje katalogi, z których użytkownikowi wolno otwierać pliki. Przy

pomocy tej dyrektywy możemy ograniczyć użytkownikowi prawa do otwierania plików poza

wybranym drzewem katalogów.


safe_mode_include_dir

Dyrektywa ta specyfikuje katalogi (wraz z ich podkatalogami), z których użytkownikowi

wolno includować pliki, które nie są jego własnością. Pliki includowane położone poza tym

drzewem muszą mieć tego samego właściciela co uruchamiany skrypt .


safe_mode_exec_dir

Dyrektywa ta specyfikuje katalogi (wraz z ich podkatalogami), z których użytkownikowi

wolno uruchamiać zewnętrzne programy np. przy użyciu funkcji system.



safe_mode_allowed_env_vars

Przy pomocy tej dyrektywy możemy wyszczególnić prefixy zmiennych środowiskowych,

które mogą być zmieniane przez użytkownika wykorzystującego funkcję putenv(). Można podać

więcej prefixów oddzielając je przecinkami.


safe_mode_protected_env_vars

Przy pomocy tej dyrektywy można podać listę zmiennych środowiskowych, które nie mogą

być modyfikowane przez użytkownika funkcją putenv(). Zmienne takie nie będą mogły być

modyfikowane nawet jeżeli objęte zostały dyrektywą safe_mode_allowed_env_vars.

disable_functions

background image

12

Ta dyrektywa umożliwia podanie nam listy funkcji, które zostają zablokowane i

użytkownik nie może z nich skorzystać. Nazwy funkcji są oddzielone przecinkami. Nie można

korzystać z tej dyrektywy wyłącznie wewnątrz pliku php.ini.

Większość dyrektyw tych można definiować także wewnątrz pliku httpd.conf (w

konfiguracji Apache’a) . Daje nam to możliwość zmiany właściwości trybu safe mode dla

poszczególnych wirtualnych domen, a także dla poszczególnych katalogów.

Przykład takiej definicji w pliku httpd.conf wygląda następująco:

php_flag safe_mode on

<Directory /docroot>
php_admin_value open_basedir /docroot
</Directory>

4. Analiza kodu suexec

Poniżej zamieszczono dokładny opis kodu programu suexec, który służy jako pośrednik do

uruchamiania skryptów CGI.

Deklaracja bezpiecznych zmiennych środowiskowych, które mogą zostać przekazane

skryptowi uruchamianemu, przy pomocy suexec.

char *safe_env_lst[] =

{

"AUTH_TYPE",

"CONTENT_LENGTH",

"CONTENT_TYPE",

"DATE_GMT",

"DATE_LOCAL",

"DOCUMENT_NAME",

"DOCUMENT_PATH_INFO",

"DOCUMENT_ROOT",

"DOCUMENT_URI",

"FILEPATH_INFO",

background image

13

"GATEWAY_INTERFACE",

"LAST_MODIFIED",

"PATH_INFO",

"PATH_TRANSLATED",

"QUERY_STRING",

"QUERY_STRING_UNESCAPED",

"REMOTE_ADDR",

"REMOTE_HOST",

"REMOTE_IDENT",

"REMOTE_PORT",

"REMOTE_USER",

"REDIRECT_QUERY_STRING",

"REDIRECT_STATUS",

"REDIRECT_URL",

"REQUEST_METHOD",

"REQUEST_URI",

"SCRIPT_FILENAME",

"SCRIPT_NAME",

"SCRIPT_URI",

"SCRIPT_URL",

"SERVER_ADMIN",

"SERVER_NAME",

"SERVER_ADDR",

"SERVER_PORT",

"SERVER_PROTOCOL",

"SERVER_SOFTWARE",

"UNIQUE_ID",

"USER_NAME",

"TZ",

NULL

};

Funkcja wyprowadzająca komunikaty diagnostyczne do pliku logów. Plik ten jest

zdefiniowany

podczas

kompilacji

Apacha

przez

ustawienie

--suexec-

logfile=NAZWAPLIKU

.

static void err_output(const char *fmt, va_list ap)

background image

14

Funkcja przetwarzająca zmienną liczbę argumentów i wywołująca funkcje

err_output()

w celu wypisania komunikatu do plików logów.

static void log_err(const char *fmt,...)

Funkcja oczyszcza środowisko ze zmiennych, niebezpiecznych. Za zmienne bezpieczne

uznawane są tylko zmienne, których nazwy zostały umieszczone w tablicy safe_env_lst[]

oraz zmienne zaczynające się prefiksem HTTP_. Maksymalna ilość zmiennych jest umieszczona

w stałej AP_ENVBUF zdefiniowanej na początku pliku.

Funkcja main(int argc, char *argv[]) uruchamia polecenie ze zmienionymi uprawnieniami.

Działanie funkcji:

Sprawdza czy użytkownik, który wywołuje program istnieje w pliku /etc/passwd

uid = getuid();

if ((pw = getpwuid(uid)) == NULL) {

log_err("crit: invalid uid: (%ld)\n", uid);

exit(102);

}

Sprawdza czy można wyświetlić info. Info jest wyświetlane wtedy jeżeli: liczba

argumentów jest większa niż 1, parametr przekazany do funkcji jest równy "-V" oraz osoba

wywołującą program jest root lub użytkownik HTTPD_USER (czyli użytkownik, z

uprawnieniami którego działa Apache i uprawniony jest do uruchamiania programu suexec). Po

wyświetleniu informacji program jest zatrzymywany.

Jeżeli liczba parametrów jest mniejsza od 4, program jest kończony z błędem i

komunikatem do logów.

if (argc < 4) {

log_err("alert: too few arguments\n");

exit(101);

}

background image

15

Następnie pobierana jest z argumentów wejściowych nazwa użytkownika, grupy i

programu uruchamianego przez tego użytkownika.

target_uname = argv[1];

target_gname = argv[2];

cmd = argv[3];

Sprawdza czy użytkownik, który próbuje uruchomić program ma do tego uprawnienia.

Użytkownik, który wywołuje program definiowany jest w trakcie kompilacji lub w pliku

suexec.h

.

if (strcmp(HTTPD_USER, pw->pw_name)) {

log_err("crit: calling user mismatch (%s instead of %s)\n",

pw->pw_name, HTTPD_USER);

exit(103);

}

Sprawdza czy łańcuch komendy wykonywanej zaczyna się "/" lub "../" lub zawiera "/../'.

Zabezpieczenie to ma na celu wyeliminowanie możliwości uruchamiania programu z korzenia

systemu plików lub "cofania" się w hierarchii systemu plików.

if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3))

|| (strstr(cmd, "/../") != NULL)) {

log_err("error: invalid command (%s)\n", cmd);

exit(104);

}

Sprawdza czy suexec ma do czynienia z wywołaniem programu z konta użytkownika.

Jeżeli tak to usuwa z nazwy użytkownika pierwszy znak tj. "~" (tyldê).

if (!strncmp("~", target_uname, 1)) {

target_uname++;

userdir = 1;

}

Sprawdza czy docelowy użytkownik istnieje w systemie.

if ((pw = getpwnam(target_uname)) == NULL) {

background image

16

log_err("crit: invalid target user name: (%s)\n",

target_uname);

exit(105);

}

Sprawdza poprawność grupy docelowej (numeru, ewentualnie nazwy).

if (strspn(target_gname, "1234567890") != strlen(target_gname))

{

if ((gr = getgrnam(target_gname)) == NULL) {

log_err("crit: invalid target group name: (%s)\n",

target_gname);

exit(106);

}

gid = gr->gr_gid;

actual_gname = strdup(gr->gr_name);

}

else {

gid = atoi(target_gname);

actual_gname = strdup(target_gname);

}

Zapisanie id użytkownika, nazwy użytkownika i docelowego katalogu w zmiennych

pomocniczych.

uid = pw->pw_uid;

actual_uname = strdup(pw->pw_name);

target_homedir = strdup(pw->pw_dir);

Otwarcie pliku logów i zalogowanie o zaistniałym zdarzeniu:

log_err("info: (target/actual) uid: (%s/%s) gid: (%s/%s) cmd:

%s\n",

target_uname, actual_uname,

target_gname, actual_gname,

cmd);

background image

17

Sprawdzenie czy użytkownik docelowy nie jest rootem i czy jego uid nie jest mniejszy od

zmiennej UID_MIN, która jest ustawiana w pliku suexec.h lub w trakcie konfigurowania

Apache'a.

if ((uid == 0) || (uid < UID_MIN)) {

log_err("crit: cannot run as forbidden uid (%d/%s)\n",

uid, cmd);

exit(107);

}

To samo dla grupy docelowej.

if ((gid == 0) || (gid < GID_MIN)) {

log_err("crit: cannot run as forbidden gid (%d/%s)\n", gid,

cmd);

exit(108);

}

Zmienia uprawnienia procesu na uprawnienia użytkownika docelowego. Najpierw setgid(),

a pózniej setuid().

if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) !=

0)) {

log_err("emerg: failed to setgid (%ld: %s)\n", gid, cmd);

exit(109);

}

if ((setuid(uid)) != 0) {

log_err("emerg: failed to setuid (%ld: %s)\n", uid, cmd);

exit(110);

}

Pobranie nazwy aktualnego katalogu.

if (getcwd(cwd, AP_MAXPATH) == NULL) {

log_err("emerg: cannot get current working directory\n");

exit(111);

}

background image

18

Sprawdzenie czy osiągalny jest docelowy katalog roboczy dla programu uruchamianego.

Dla programu uruchamianego w normalnym trybie sprawdzana jest także czy katalog

docelowy jest w poddrzewie katalogu zawartego w stałej DOC_ROOT ustawianej podczas

konfiguracji lub w pliku suexec.h.

if (userdir) {

if (((chdir(target_homedir)) != 0) ||

((chdir(USERDIR_SUFFIX)) != 0) ||

((getcwd(dwd, AP_MAXPATH)) == NULL) ||

((chdir(cwd)) != 0)) {

log_err("emerg: cannot get docroot information

(%s)\n",target_homedir);

exit(112);

}

}

else {

if (((chdir(DOC_ROOT)) != 0) ||

((getcwd(dwd, AP_MAXPATH)) == NULL) ||

((chdir(cwd)) != 0)) {

log_err("emerg: cannot get docroot information (%s)\n",

DOC_ROOT);

exit(113);

}

}

if ((strncmp(cwd, dwd, strlen(dwd))) != 0) {

log_err("error: command not in docroot (%s/%s)\n", cwd,

cmd);

exit(114);

}

Pobranie informacji o katalogu aktualnym, w którym będzie uruchamiany program

docelowy.

if (((lstat(cwd, &dir_info)) != 0) ||

!(S_ISDIR(dir_info.st_mode))) {

background image

19

log_err("error: cannot stat directory: (%s)\n", cwd);

exit(115);

}

Sprawdzenie czy katalog może być zapisywany przez innych użytkowników lub

użytkowników w tej samej grupie (jeśli tak to błąd).

if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode &

S_IWGRP)) {

log_err("error: directory is writable by others: (%s)\n",

cwd);

exit(116);

}

Pobranie informacji o programie.

if (((lstat(cmd, &prg_info)) != 0) ||

(S_ISLNK(prg_info.st_mode))) {

log_err("error: cannot stat program: (%s)\n", cmd);

exit(117);

}

Sprawdza czy program uruchamiany, może być zapisywany przez innych (jeśli tak to błąd).

if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode &

S_IWGRP)) {

log_err("error: file is writable by others: (%s/%s)\n", cwd,

cmd);

exit(118);

}

Błąd jeżeli program ma ustawiony bit SGID lub SUID.

if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode &

S_ISGID)) {

log_err("error: file is either setuid or setgid: (%s/%s)\n",

cwd, cmd);

exit(119);

}

background image

20

Sprawdza czy docelowa grupa i użytkownik są właścicielami programu uruchamianego i

katalogu, w którym ten program jest uruchamiany.

if ((uid != dir_info.st_uid) ||

(gid != dir_info.st_gid) ||

(uid != prg_info.st_uid) ||

(gid != prg_info.st_gid)) {

log_err("error: target uid/gid (%ld/%ld) mismatch "

"with directory (%ld/%ld) or program (%ld/%ld)\n",

uid, gid,

dir_info.st_uid, dir_info.st_gid,

prg_info.st_uid, prg_info.st_gid);

exit(120);

}

Sprawdza, czy program jest uruchamialny przez właściciela

if (!(prg_info.st_mode & S_IXUSR)) {

log_err("error: file has no execute permission: (%s/%s)\n",

cwd, cmd);

exit(121);

}

Sprawdza poprawne ustawienie maski dla tworzonych plików tak, aby pliki utworzone

przez wywoływany program nie mogły byś zapisywane przez innych użytkowników systemu.

if ((~SUEXEC_UMASK) & 0022) {

log_err("notice: SUEXEC_UMASK of %03o allows "

"write permission to group and/or other\n",

SUEXEC_UMASK);

}

umask(SUEXEC_UMASK);

Czyści zmienne środowiskowe uruchamianego programu.

clean_env();

Zamyka plik logów.

background image

21

if (log != NULL) {

fclose(log);

log = NULL;

}

Uruchomienie programu.

#ifdef NEED_HASHBANG_EMUL

/* We need the #! emulation when we want to execute scripts */

{

extern char **environ;

ap_execve(cmd, &argv[3], environ);

}

#else /*NEED_HASHBANG_EMUL*/

execv(cmd, &argv[3]);

#endif /*NEED_HASHBANG_EMUL*/

Zapisanie informacji do logów w przypadku nieprawid

łowego

uruchomienia skryptu.

log_err("emerg: (%d)%s: exec failed (%s)\n", errno,

strerror(errno), cmd);

exit(255);

5. Pomysły na rozbudowanie kodu suexec

5.1. Modyfikacja suexec tak, aby uruchamiać pliki PHP z uprawnieniami

właściciela pliku:

Z jednym ze wcześniejszych punktów zasygnalizowano możliwość uruchamiania skryptów

z wykorzystaniem suexec. Celem modyfikacji jest umożliwienie uruchamiania plików PHP bez

konieczności wpisywania w pierwszym wierszu każdego skryptu ścieżki do interpretera PHP tak

aby uruchamiały się one z uprawnieniami właściciela pliku.

Modyfikacja ta polega na rozpoznawaniu po rozszerzeniu czy jest to plik PHP (oraz wersji)

oraz uruchomieniu odpowiedniego interpretera skryptów.

background image

22

Najważniejsza modyfikacja źródeł suexe.c jest następująca:

if (czy_php(cmd))

if ((newargv[0] = getPhpInterpreter(cmd)) == NULL) {

fprintf(stderr, "interpreter nie znaleziony\n");

exit(130);

}

newargv[1] = cmd;

newargv[2] = NULL;

Powyższy kod sprawdza na postawie rozszerzenia czy polecenie jest plikiem PHP. W

zależności od tego czy plik ma rozszerzenie .php, .php3 lub .php4. dobiera odpowiedni

interpreter. Oraz buduje tablice zawierającą parametry do uruchomienia interpretera wraz z

odpowiednim skryptem.

Tablica newargv[] przekazywana jest następnie do funkcji ap_execve(), która odpowiedzialna

jest za uruchomienia skryptu.

5.2. 4.2 Obsługa limitów setrlimit().

Obecnie

serwer

Apache

nie

udostępnia

możliwości

ograniczenia

zasobów

wykorzystywanych przez programy CGI. Zasoby te mogą być ograniczone poprzez standardowe

ustawienia w systemie, które są stosowane dla każdego programu. Ograniczenia te można

zmieniać wykorzystując polecenie ulimit/limit (w zależności od shella). Administrator nie ma

jednak możliwości ograniczenia w ten sposób tylko do skryptów CGI i dlatego potrzebne wydaje

się zmodyfikowanie serwera tak, aby mógł wpływać na ograniczenia w przydzielaniu zasobów

systemowych. Aby zrealizować ten cel można zmodyfikować kod programu suexec, który jest

odpowiedzialny za uruchamianie skryptów CGI. Modyfikacja programu będzie polegać na

odczytaniu pliku konfiguracyjnego, zinterpretowaniu jego zawartości i ustawieniu limitów dla

niego samego, a takze dla wszystkich innych programów, które suexec uruchomi.

Ponieważ w naszym rozwiązaniu korzystamy z funkcji systemowej setrlimit() dlatego

mamy możliwość nadania programowi następujących ograniczeń.


RLIMIT_CPU - czas procesora
RLIMIT_FSIZE – maksymalny rozmiar pliku

RLIMIT_DATA - maksymalny rozmiar segmentu danych
RLIMIT_STACK - maksymalny rozmiar segmentu stosu
RLIMIT_CORE – maksymalny rozmiar pliku core (tworzonego gdy w skrypcie

wyst

ąpi błąd)

RLIMIT_RSS maksymalna wielko

ść pamięci rezydentnej

background image

23

RLIMIT_NPROC - maksymalna ilo

ść towrzonych procesów

RLIMIT_NOFILE – maksymalna ilo

ść otworzonych plików

RLIMIT_MEMLOCK – maksymalna wielkoœæ obszaru locked-in-memory

Ponieważ jest to zadanie modelowe plik konfiguracyjny ma postać możliwie najprostsza.

Dane są w pliku oddzielone przecinkami i znajdują się w jednym wierszu. Wartość ujemna

oznacza brak limitu.

Kolejne wartości w pliku oznaczają:

RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_DATA, RLIMIT_STACK, RLIMIT_CORE, RLIMIT_RSS,
RLIMIT_NPROC, RLIMIT_NOFILE,RLIMIT_MEMLOCK

Przykład:

5,1223423,-1,1324214,2224313,14242223,12234233,13242234,-1

Modyfikacja kodów programu suexec sprowadza się do dodania dwóch dyrektyw define

oznaczających ścieżkę do pliku konfiguracyjnego i maksymalny jego rozmiar (który powinien

być niewielki z powodu niewielu informacji w nim trzymanych), funkcji odczytującej plik i

interpretującej jego zawartość oraz kilka wierszy sprawdzających uprawnienia do pliku

konfiguracyjnego i ustawiających limity. Ponadto trzeba „zaincludować” stosowne pliki.

Wiersze definiujące maksymalny rozmiar pliku konfiguracyjnego oraz ścieżkę do tego

pliku:

#define MAXCONFBUF 1000

#define MAXCONFPATH "/etc/param.conf"

Funkcja odczytująca i interpretująca zawartość pliku konfiguracyjnego. W przypadku błędu

funkcja ta zwraca –1 i wypisuje komunikat do pliku logów.

int odczytaj_limit(char *nazwa_pliku,struct rlimit *rl_CPU, struct rlimit

*rl_FSIZE,struct rlimit *rl_DATA,struct rlimit *rl_STACK,struct rlimit

*rl_CORE,struct rlimit *rl_RSS,struct rlimit *rl_NPROC,struct rlimit

*rl_NOFILE,struct rlimit *rl_MEMLOCK){

int fd,odczytano;

char bufor[MAXCONFBUF];

char *poz,*first=bufor;

background image

24

fd=open(nazwa_pliku,O_RDONLY);

if(fd>0){

odczytano=read(fd,(void *)bufor,MAXCONFBUF);

if(odczytano>0){

poz=bufor;

if(atoi(poz)=>0){

rl_CPU->rlim_cur=atoi(poz);

rl_CPU->rlim_max=rl_CPU->rlim_cur;

}

else rl_CPU->rlim_max=rl_CPU->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_FSIZE->rlim_cur=atoi(poz);

rl_FSIZE->rlim_max=rl_FSIZE->rlim_cur;

}

else rl_FSIZE->rlim_max=rl_FSIZE->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_DATA->rlim_cur=atoi(poz);

rl_DATA->rlim_max=rl_DATA->rlim_cur;

}

else rl_DATA->rlim_max=rl_DATA->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_STACK->rlim_cur=atoi(poz);

rl_STACK->rlim_max=rl_STACK->rlim_cur;

}

else rl_STACK->rlim_max=rl_STACK->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_CORE->rlim_cur=atoi(poz);

rl_CORE->rlim_max=rl_CORE->rlim_cur;

}

else rl_CORE->rlim_max=rl_CORE->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_RSS->rlim_cur=atoi(poz);

background image

25

rl_RSS->rlim_max=rl_RSS->rlim_cur;

}

else rl_RSS->rlim_max=rl_RSS->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_NPROC->rlim_cur=atoi(poz);

rl_NPROC->rlim_max=rl_NPROC->rlim_cur;

}

else rl_NPROC->rlim_max=rl_NPROC->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_NOFILE->rlim_cur=atoi(poz);

rl_NOFILE->rlim_max=rl_NOFILE->rlim_cur;

}

else rl_NOFILE->rlim_max=rl_NOFILE->rlim_cur=RLIM_INFINITY;

poz=index(poz,',');if(!poz) return -1; poz++;

if(atoi(poz)=>0){

rl_MEMLOCK->rlim_cur=atoi(poz);

rl_MEMLOCK->rlim_max=rl_MEMLOCK->rlim_cur;

}

else rl_MEMLOCK->rlim_max=rl_MEMLOCK->rlim_cur=RLIM_INFINITY;

return 1;

}

else{ log_err("blad odczytu informacji\n"); return -1; }

close(fd);

}

else{

log_err("crit: nie mozna znalezc pliku konfiguracyjnego\n");

return -1;

}

}

Deklaracja zmiennych przetrzymujących informacje o pliku konfiguracyjnym i informacje

o limitach odczytanych z tego pliku.

struct stat prg_info1; /* konfiguracja - limit info holder */

struct rlimit rl_CPU, rl_FSIZE, rl_DATA, rl_STACK, rl_CORE, rl_RSS, rl_NPROC,

rl_NOFILE, rl_MEMLOCK; /* Limity programu */

background image

26

Sprawdzenie czy pliku konfiguracyjny limitów nie jest zapisywany przez użytkowników

innych niż właściciel:

if (((lstat(M

AXCONFPATH, &prg_info1)) != 0) ||

(S_ISLNK(prg_info1.st_mode))) {

log_err("error: Nie moge uzyskac informacji o pliku : (%s)\n",MAXCONFPATH);

exit(127);

}

if ((prg_info1.st_mode & S_IWOTH) || (prg_info1.st_mode & S_IWGRP)) {

log_err("error: plik jest zapisywalny przez innych niz

wlasciciel\n",MAXCONFPATH);

exit(128);

}

Odczytanie pliku konfiguracyjnego i ustawienie limitów. W przypadku błędu wypisywany

jest komunikat o błędzie do logów i kończony jest program (nie sprawdzana jest poprawność

wykonania funkcji setrlimit() – rozbudowując kod można dodać obsługę błędów, którym

źródłem jest funkcja setrlimit()).

if(odczytaj_limit(MAXCONFPATH, &rl_CPU, &rl_FSIZE, &rl_DATA, &rl_STACK,

&rl_CORE, &rl_RSS, &rl_NPROC, &rl_NOFILE, &rl_MEMLOCK)>0){

//ustawianie limitow

setrlimit(RLIMIT_CPU,&rl_CPU);

setrlimit(RLIMIT_FSIZE,&rl_FSIZE);

setrlimit(RLIMIT_DATA,&rl_DATA);

setrlimit(RLIMIT_STACK,&rl_STACK);

setrlimit(RLIMIT_CORE,&rl_CORE);

setrlimit(RLIMIT_RSS,&rl_RSS);

setrlimit(RLIMIT_NPROC,&rl_NPROC);

setrlimit(RLIMIT_NOFILE,&rl_NOFILE);

setrlimit(RLIMIT_MEMLOCK,&rl_MEMLOCK);

}

else{

log_err("crit: blad w konfiguracji limitow\n");

exit(126);

}

background image

27

background image

28

6. Wnioski

Niestety nie ma uniwersalnej metody uruchamiania skryptów PHP, która nie posiadała by

wad. Wybierając metodę uruchamiania skryptów PHP należy dokonać oceny jak bardzo zależy

nam na uruchamianiu skryptów z uprawnieniami właściciela oraz jak duże i jakie koszty

jesteśmy w stanie poświęcić chcąc rozwiązać ten problem. Uruchamianie programów z

wykorzystaniem suexec wiąże się z narzutami związanymi z uruchamianiem nowego procesu

wady tej nie mają skrypty uruchamiane przez moduł. Ten jednak sposób uniemożliwia bez

uruchamiania nowego serwera na innym porcie uruchomienia skryptów z uprawnieniami

właściciela. Pewne problemy z narzutem związanym uruchamianiem procesów rozwiązuje

wykorzystanie modułu i biblioteki FastCGI, ale ta przechowuje w pamięci dużo procesów, które

cały czas zajmują pamięć.

Analiza kodu suexec daje nam do zrozumienia jak bardzo ważną częścią w

oprogramowaniu serwera są kody poświęcone bezpieczeństwu. W zasadzie cały kod programu

suexec służy do sprawdzenia czy został on wywołany w prawidłowy sposób nie dając

możliwości włamania się do systemu.

Tryb safe mode daje nam pewną możliwość poprawy bezpieczeństwa pracy na serwerze z

wieloma użytkownikami, którym nie można zaufać. Tryb safe mode staje się cennym narzędziem

w przypadku, gdy użytkownicy piszący skrypty nie mają dostępu do całego systemu plików

serwera (np. dostęp tylko po przez ftp z chrootem) oraz możliwości wykorzystania innych

narzędzi np. skryptów CGI napisanych w Perlu, które pozwolą na dostęp do obszarów serwera

poza swoim katalogiem domowym (lub wyznaczonym obszarem systemu plików). Zabezpiecza

to np. przed dostępem do skryptów lub plików oraz np. haseł w nich zawartych innego

użytkownika. Trudno jest tego dokonać inną metodą np. modyfikując uprawnienia w systemie

plików ponieważ skrypty muszą być dostępne do odczytu dla serwera Apache (czyli praktycznie

dla każdego użytkownika). Można oczywiście wykorzystać skrypty CGI oraz suexec i zapisywać

hasła w plikach tylko do odczytu dla właściciela, ale wiąże się to z narzutami na tworzenie

nowych procesów.

Podsumowując. Aby wybrać właściwy sposób uruchamiania skryptów PHP i co za tym

idzie konfiguracji serwera należy nie tylko znać konfigurowane oprogramowanie, ale także cel,

zasadę działania oprogramowania, które będzie uruchamiane przez serwer.

background image

29

7. Literatura

8. Dokumentacja serwera Apache: http://httpd.apache.org/docs/

9. Dokumentacja PHP: http://www.php.net/manual/en/

10. Dokumentacja FastCGI http://www.fastcgi.com/devkit/doc/fcgi-perf.htm

11. Ben, Peter Laurie „Apache przewodnik encyklopedyczny” - Helion 2000

12. Listy dyskusyjne


Wyszukiwarka

Podobne podstrony:
Skrypty PHP
PHP Praktyczne wprowadzenie R 4 Wstęp do programowania Proste skrypty PHP
jak za pomoca skryptu php umiescic w?zie kopie?nych utworzona wczesniej przez mysqldump
Skrypt z PHP Szczaniecki
Apache SSL PHP fp
sql - opis pol w bazie, ★skrypty php★, ➤ Skrypty PHP i HTML i INNE
Jak wyłączyć pokazywanie komunikatów o błędach w skryptach PHP, PHP Skrypty
Linux Apache MySQL i PHP Zaawansowane programowanie lapzap
Linux Apache MySQL i PHP Zaawansowane programowanie
Linux Apache MySQL i PHP Zaawansowane programowanie lapzap
Linux, Apache, MySQL i PHP Zaawansowane programowanie
ebook Jason Gerner, Morgan L Owens, Elizabeth Naramore, Matt Warden Linux, Apache, MySQL i PHP Zaaw
Linux Apache MySQL i PHP Zaawansowane programowanie 2
Linux Apache MySQL i PHP Zaawansowane programowanie lapzap

więcej podobnych podstron