HTTPS i Apache :: Kilka słów o znakach z poza ASCII w adresach URI :: Słów kilka o Unicode i kodowaniach :: PHP, CGI, suexec - czyli (nie)bezpieczny Apache

WWW

Do grupy najpopularniejszych usług serwerowych należy zaliczyć protokół HTTP służący do przesyłania stron www i plików. Jednym z najbardziej rozbudowanych i zarazem najpopularniejszych serwerów WWW jest Apache2.

Jego konfiguracja (przynajmniej w Debianie) odbywa się poprzez pliki i katalogi zgomadzone w /etc/apache2/. Głównym plikiem konfiguracyjnym jest apache2.conf, istotną rolę pełni także ports.conf służący do definiowania portów na których ma słuchać serwer (wydaje się też on stosownym miejscem na dyrektywy NameVirtualHost wskazujące na jakich adresach obsługiwane mają być hosty wirtualne identyfikowane nazwami). Kolejnymi ważnymi pozycjami są katalogi: mods-enabled (zawierający dowiązania do poleceń ładujących i konfigurujących moduły) oraz sites-enabled (zawierający konfiguracje udostępnianych stron, vhost'ów).

Listę modułów wraz z opisami można znależć w dokumentacji serwera. Należy pamiętać iż funkcjonalność taka jak: "userdir", "rewriting URL engine", kontrola dostępu (zarówno poprzez dyrektywy Deny, Alow jak i poprzez autoryzację HTTP), automatyczne indeksowanie katalogów, aliasy, obsługa CGI czy proxy zawarta jest w stosowanych modułach.

Przykładowa konfiguracja vhosta może wyglądać:

<VirtualHost *:80> # wcześniej musi wystąpić dyrektywa "NameVirtualHost *:80"
	ServerName www.nasza.domena:80
	ServerAlias nasza.domena *.specjalne.nasza.domena
	# *.specjalne.nasza.domena umożliwia współpracę z wpisami dnsowymi postaci:
	# 	*.specjalne       IN      A       ....
	# co zapewnia obsługę wszystkich adresów {teskt bez kropki}.test.n17.waw.pl przez ten vhost
	ServerAdmin admin@nasza.domena
	
	DocumentRoot /var/www/moja-stronka
	
	# kontrola dostępu oparta na zablokowaniu portu 98 na routerze
	# Order deny,allow
	# Deny from all
	# Allow from 127.0.0.1
	
	SSLEngine on
	
	SSLCertificateFile /etc/ssl/server.crt
	SSLCertificateKeyFile /etc/ssl/server.key
	
	<Location /status>
		SetHandler server-status
	</Location>
	<Location /info>
		SetHandler server-info
	</Location>
	
	Alias /mail  "/usr/share/squirrelmail/"
	Alias /mysql "/usr/share/phpmyadmin/"
	Alias /pgsql "/usr/share/phppgadmin/"
	
	RewriteEngine On
	RewriteRule /webmin https://%{SERVER_NAME}:10000/ [R=301,L]
	RewriteRule /usermin https://%{SERVER_NAME}:20000/ [R=301,L]
</VirtualHost>

W głównej konfiguracji apache warto rozważyć ustawienie logowania przez syslog (ErrorLog syslog:daemon), dostroić poziom głośności logowania (LogLevel ...). Warto wstawić także nastepujące ustawienia i zabezpieczenia zabezpieczenia:

UseCanonicalName Off
LogFormat   "%>s %b %T	%h %t %{Host}i \"%r\" 	\"%{Referer}i\" \"%{User-Agent}i\""
# jeżeli chemy przekazywać logi do sysloga:
TransferLog |/path-to/apache_syslog
# gdzie apache_syslog jest skryptem o następującej treści:
# /usr/bin/logger -t www -p local0.debug

AddHandler cgi-script .cgi
AddDefaultCharset UTF-8
DirectoryIndex index.html index.cgi index.pl index.php index.xhtml

ExtendedStatus On

<Directory />
	Options All
	AllowOverride All
</Directory>

<Location />
	<Limit CONNECT>
		Order deny,allow
		Deny from all
	</Limit>
</Location>

<Location http://*>
	Order deny,allow
	Deny from all
</Location>

A także ustawić zmniejszony timeout w domyślnym vhoście (identyfikowanym nazwą) - pierwszy w kolejności vhost łyka rządzania, których nagłówek Host: nie pasuje do rzadnego innego vhosta - np. TimeOut 10.

Niekiedy może zajść potrzeba dostrojenia (np. ustawień Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec ExecCGI) pewnych parametrów w konfiguracji udostępniania stron domowych użytkowników poprzez http://serwername/~user w /etc/apache2/mods-enabled/userdir.conf. Należy także pamiętać iż ograniczenie ilości danych które możemy przesłać z formularza www do php wynika z konfiguracji tego ostatniego - zmienna post_max_size = 50M w /etc/php5/apache2/php.ini.

W razie problemów ze startem serwera radzę zwrócić uwagę ściezki gdzie zapisywane są logi - najlepiej podawać od / i upewnić się że katalogi istnieją ... . Zamieszam też zestaw prostych skryptów CGI służących do testowania działania tego mechanizmu cgi-test.tar.gz

Niekiedy nie potrzeba nam tak rozbudowanego serwera jkim jest Apache - wtedy dobrym wyborem może się okazać mini-httpd (przykładowy konfig) lub nginx (przykładowe konfigi do ustawienia serwera z obsługą php na porcie 8888 i realizacji proxy do takiego serwera z maszyny bez php - proxy-php.nginx, proxy.nginx i proxy_params.nginx).

HTTPS i Apache

Aby uruchomić szyfrowany dostęp do stron WWW (poprzez Apache2) wystarczy uaktywnić moduł SSL, ustawić nasłuch na porcie 443 (standardowy port dla https) oraz skonfigurować vhosta na tym porcie z włączonym szyfrowaniem poprzez umieszczenie w jego konfiguracji:

SSLEngine on
SSLCertificateFile /etc/ssl/server.crt
SSLCertificateKeyFile /etc/ssl/server.key

Następnie musimy również przygotować odpowiednie certyfikaty - więcej na ten temat w części poświęconej szyfrowaniu z wykorzystaniem certyfikatów x509

Jeżeli wygenerowaliśmy niezależnie certyfikat CA warto w konfiguracji Apache także określić jego położenie:

SSLCACertificatePath /etc/ssl/ca/
SSLCACertificateFile /etc/ssl/ca/ca.crt

Należy zaznaczyć iż dawniej aby vhosty miały różne certyfikaty SSL należy je uruchamiać na rożnych portach lub adresach IP (jest to ograniczenie wynikłe z samego mechanizmu SSL). Posłużyć się należy dyrektywami: VirtualHost ip.ip.ip.ip:port oraz Listen port i opcjonalnie - gdy na danym ip i porcie ma być kilka vhostow rozróżnianych nazwami (ale z wspólnym certyfikatem !) NameVirtualHost ip.ip.ip.ip:port). Należy też zaznaczyć iż porty możemy użyć w redirectach z http:// (ale z https:// już trudniej - zmiana certyfikatu), najwygodniejsze są adresy IP.

Współczenie rozwiązaniem tego problemu jest wykorzystanie mechanizmu SNI, który pozwala na używanie różnych certyfikatów dla różnych vhostów. Jest on obsługiwany przez wszystkie najpopularniejsze przeglądarki w współczesnych wersjach idomyślnie włączony w Apache.

Kilka słów o znakach z poza ASCII w adresach URI

Zagadnie skład się z dwóch pod-zagadnień:

Znaki z poza ASCII w nazwach domenowych są wynalazkiem stosunkowo nowym - przez to nie obciążonym mnogością kodowań narodowych. Zgodnie z dokumentami RFC specyfikującymi to rozwiązanie - Internationalizing Domain Names (RFC3490, RFC3491, RFC3492) człony (od kropki do kropki) takich domen w całości zapisywane są z wykorzystaniem Unicode kodowanego do postaci zbioru znaków ASCII reprezentowanego w nazwach domenowych z wykorzystaniem punycode. Standard wspierany jest przez m.in. przez przeglądarkę Firefox 3, ale np. Konqueror 3.5.9 (pomimo oficjalnej zgodności) ma problemy z obsługą tego standardu. W przypadku narzędzi nie potrafiących dekodować IDN wprowadzanego w formie czytelnej dla człowieka (Unicode) konieczne jest zastosowanie programu konwertującego nazwę domeny na punycode - np. w3m `idn --quiet http://www.żółw.pl/`.

W przypadku znaków z poza ASCII w pozostałych częściach URL sprawa jest bardziej skomplikowana gdyż:

Dla ścisłości należy zaznaczyć że nie wszystkie znaki ASCII są obsługiwane bezpośrednio (w sposób niekodowany) w URL'ach - niektóre z nich mają znaczenie specjalne w ramach URL, a inne nie są znakami alfanumerycznymi.

Słów kilka o Unicode i kodowaniach

Unicode sam w sobie przypisuje tylko jednoznaczny kod numeryczny znakom. Nie określa on sposobu zapisu tego kodu numerycznego - za to odpowiadają standardy tupu UCS i UTF:

Kodowania o długości słowa powyżej 8 bitów (UTF-16, UCS-2, UTF-32/UCS-4) wymagają stosowania znacznika kolejności bajtów BOM (kodowania te - w odróżnieniu od np. protokołów sieciowych - nie ustalają stałej kolejności bajtów w słowach wielobajtowych). Znacznik ten jest dopuszczany także w UTF-8, jednak jego używanie przy starszych implementacjach jest problematyczne.

Ponadto istnieje kilka metod kodowania znaków 8bitowych (bez względu na to czy są znakami UTF-8 czy jakiegoś kodowania narodowego) do 7-bitowego (lub mniejszego) podzbioru ASCII:

Poniżej zamieszczam skrypt pokazujący różnice pomiędzy poszczególnymi kodowaniami:

NAPIS="ą_ś_ _ź"
echo "ORYGINALNY HEX:"
echo "$NAPIS" | od -t x1

echo -e "\nUTF-32/UCS-4 HEX:"
echo "$NAPIS" | iconv -t utf32 | od -t x4

echo -e "\nUCS2 HEX:"
echo "$NAPIS" | iconv -t ucs2 | od -t x2

echo -e "\nUTF-16 HEX:"
echo "$NAPIS" | iconv -t utf16 | od -t x2

echo -e "\nUTF-8 HEX:"
echo "$NAPIS" | iconv -t utf8 | od -t x1

echo -e "\nUTF-7:"
echo "$NAPIS" | iconv -t utf7

echo -e "\nPunycode:"
echo "$NAPIS" | idn --quiet

echo -e "\nkodowanie Quoted-Printable:"
echo "$NAPIS" | konwert 8bit-qp

echo -e "\nkodowanie URL:"
echo "$NAPIS" | php -r '$stdin=fopen("php://stdin","r"); while($line=fgets($stdin)) {echo rawurlencode(substr($line,0,strlen($line)-1)) . "\n";}'

echo -e "\nkodowanie BASE64:"
echo "$NAPIS" | mewencode -b

Zobacz też: email2text.sh - prosta implementacja konwersji e-maila na zwykły tekst z próbą dekodowania znaków non-ASCII, Comparison of Unicode encodings

PHP, CGI, suexec - czyli (nie)bezpieczny Apache

Powierzanie dostępu (prawa do odczytu) do poufnych plików (w szczególności plików haseł) użytkownikowi/grupie na prawach której chodzi serwer www (np. apache) jest pomysłem dość kontrowersyjnym, zwłaszcza w systemach wieloużytkownikowych z dostępem do PHP. Problem polega na tym że standardowo PHP działa z tymi samymi prawami co serwer www, zatem jeżeli jakiś plik ukryjemy przed innymi użytkownikami a damy do niego dostęp serwerowi www, to każdy kto może wykonać skrypt PHP przez WWW na tym serwerze będzie miał dostęp do naszego poufnego pliku. Problem ten w szczególności dotyczy też stron zabezpieczanych hasłem przez autoryzację Apache (umieszczaną np. w plikach .htaccess) gdyż nawet gdy plik będzie dostępny dla właściciela i grupy, a będzie maił zabroniony dostęp dla innych użytkowników musi mieć do niego dostęp serwer www (najczęściej przez prawa grupy) a zatem i inni użytkownicy przez PHP.

Pewnym rozwiązaniem tych problemów jest użytkowanie PHP jako CGI (w pierwszej linijce #!/usr/bin/php-cgi oraz prawo wykonywania pliku) wraz z korzystaniem z modułu suexec, wykonującego skrypty CGI z prawami innych użytkowników (dla vhostów ustawianych dyrektywą: SuexecUserGroup nazwa_uzytkownika nazwa_grupy oraz dla stron użytkowników dostępnych przez tyldą z prawami danego użytkownika). Wykorzystywany jest wtedy program /usr/lib/apache2/suexec, który musi mieć właściciela root, grupę www-data (w Debianie) oraz prawa 4750. Oczywiście skrypty cgi są bardziej obciążające dla serwera niż php jako moduł dlatego można stosować równolegle te dwie metody - dla skryptów wymagających poufnych danych odpalamy jako cgi z użytkownikiem mającym dostęp do tych danych (ale nie root ;) dla pozostałych normalnie. Nie rozwiązuje to oczywiście powyższego problemu stron chronionych hasłem (ale stanowi pewne rozwiązanie zabezpieczenia np. haseł do baz danych czy bazy shadow gdy chcemy używać w skryptach autoryzacji PAM (do php można dodać odpowiednie funkcje)), rozwiązanie tamtego problemu wymaga aby nie było w ogóle skryptów php czy cgi chodzących z prawami serwera www.

Na koniec warto zwrócić uwagę że program suexec ma wkompilowaną na stałe w siebie ścieżkę na której musza być uruchamiane z prawami innego użytkownika skrypty (niestety nie jest ona ustawiana żadną dyrektywa konfiguracyjną), jest to ścieżka położenia pliku a nie dojścia do niego więc symlinki z ścieżki do katalogów z root vhostow nie pomogą (w razie wielkich trudności można ustawić przy kompilacji / ale nie jest to zalecane). Warto też zaznaczyć że php przeznaczone do pracy z linii komend w odróżnieniu od wersji kompilowanej dla cgi ma problemy z nagłówkami - nagłówki trzeba wysyłać na samym początku skryptu przez ich wypisywanie (np. echo), a nie przez funkcję header(); wersja cgi domyślnie wysyła też nagłówek określający że jest to html.

Kolejnym problemem jest że skrypty chodzące na prawach serwera www mogą wywołać suexec i odpalić inny skrypt na większych prawach niż posiada serwer www (suexec chodzi z SUID root), zatem zabawa z suexec traci sens gdy pozostaje możliwość odpalenia jakiegokolwiek polecenia z prawamui użytkownika serwera www. Można nawet zaryzykować stwierdzenie że sama obecność suxec w takich systemach stanowi więcej zagrożenia niż pożytku ... .



Copyright (c) 1999-2015, Robert Paciorek (http://www.opcode.eu.org/), BSD/MIT-type license


Redystrybucja wersji źródłowych i wynikowych, po lub bez dokonywania modyfikacji JEST DOZWOLONA, pod warunkiem zachowania niniejszej informacji o prawach autorskich. Autor NIE ponosi JAKIEJKOLWIEK odpowiedzialności za skutki użytkowania tego dokumentu/programu oraz za wykorzystanie zawartych tu informacji.

This text/program is free document/software. Redistribution and use in source and binary forms, with or without modification, ARE PERMITTED provided save this copyright notice. This document/program is distributed WITHOUT any warranty, use at YOUR own risk.

Valid XHTML 1.1 Dokument ten (URL: http://www.opcode.eu.org/usage_and_config/network_services/www) należy do serwisu OpCode. Autorem tej strony jest Robert Paciorek, wszelkie uwagi proszę kierować na adres e-mail serwisu: webmaster@opcode.eu.org.
Data ostatniej modyfikacji artykulu: '2015-10-10 11:33:56 (UTC)' (data ta może być zafałszowana niemerytorycznymi modyfikacjami artykułu).