Translate: 
EnglishFrenchGermanItalianPolishPortugueseRussianSpanish

Nadpisywanie wbudowanych protokołów PHP własnym wrapperem strumienia

Po ostatnich, dosyć wyczerpujących artykułach na temat silnego typowania danych, oraz autentykacji NTLM poprzez atak man-in-the-middle, postanowiłem przedstawić coś znacznie lżejszego.

Nieraz podczas pisania aplikacji natrafiałem na zabezpieczenia w stylu „system plików jest w trybie: tylko do odczytu, PHP ma prawa zapisu wyłącznie w bazie danych”. Jest to oczywiście skrajny przypadek zabezpieczeń przed hackerami, jednak w moim przypadku zdarzający się dosyć nagminnie. W tak zabezpieczonym środowisku pojawiają się więc dosyć nietypowe problemy z funkcjami wbudowanymi w PHP, gdyż niektóre z nich wymagają praw zapisu do katalogów na dysku lokalnym (upload plików, cache WSDL, biblioteka cURL), lub odczytu z plików (np. SoapClient nie potrafi odczytać treści WSDL bezpośrednio ze zmiennej, lecz tylko poprzez protokół HTTP lub z pliku lokalnego).

Opiszę więc krótko mało znane i dosyć egzotyczne obejście tych problemów: nadpisywanie wbudowanych w PHP protokołów własnymi.

Jak to wygląda w praktyce

O ile funkcja stream_wrapper_register() jest opisana dosyć wyczerpująco, to niestety wpis o stream_wrapper_unregister() milczy na temat jednej, bardzo ważnej funkcjonalności. Wyrejestrowywać można nie tylko własne protokoły, lecz także te, które udostępnia rdzeń języka PHP.

Nic nie stoi na przeszkodzie by napisać taki skrypt:

// unregister built-in HTTP protocol
stream_wrapper_unregister('http');
 
// register custom stream wrapper, which will read data from memory, rather than from network
stream_wrapper_register('http', 'localFsInsteadOfInternetClass');
 
// SoapClient does not recognize protocols other than HTTP[S] and FILE
$x = new SoapClient('http://wsdlFileLoadedFromMemory');
 
// restore original protocol
stream_wrapper_restore('http');
 
// now HTTP protocol works like always
$y = file_get_contents('http://google.com');

Jedynym warunkiem by opisany przeze mnie algorytm zadziałał, jest pełne zaimplementowanie wszystkich funkcji opisanych przez interfejs WrapperInterface

interface WrapperInterface
{
 
	/**
	 * @return bool
	 */
	public function dir_closedir();
 
	/**
	 * @param string $path
	 * @param int $options
	 * @return bool
	 */
	public function dir_opendir($path , $options);
 
	/**
	 * @return string
	 */
	public function dir_readdir();
 
	/**
	 * @return bool
	 */
	public function dir_rewinddir();
 
	/**
	 * @param string $path
	 * @param int $mode
	 * @param int $options
	 * @return bool
	 */
	public function mkdir($path , $mode , $options);
 
	/**
	 * @param string $path_from
	 * @param string $path_to
	 * @return bool
	 */
	public function rename($path_from , $path_to);
 
	/**
	 * @param string $path
	 * @param int $options
	 * @return bool
	 */
	public function rmdir($path , $options);
 
	/**
	 * @param int $cast_as
	 * @return resource
	 */
	public function stream_cast($cast_as);
 
	/**
	 * @return bool
	 */
	public function stream_close();
 
	/**
	 * @return bool
	 */
	public function stream_eof();
 
	/**
	 * @return bool
	 */
	public function stream_flush();
 
	/**
	 * @param mode $operation
	 * @return bool
	 */
	public function stream_lock($operation);
 
	/**
	 * @param string $path
	 * @param string $mode
	 * @param int $options
	 * @param string &$opened_path
	 * @return bool
	 */
	public function stream_open($path , $mode , $options , &$opened_path);
 
	/**
	 * @param int $count
	 * @return string
	 */
	public function stream_read($count);
 
	/**
	 * @param int $offset
	 * @param int $whence = SEEK_SET
	 * @return bool
	 */
	public function stream_seek($offset , $whence = SEEK_SET);
 
	/**
	 * @param int $option
	 * @param int $arg1
	 * @param int $arg2
	 * @return bool
	 */
	public function stream_set_option($option , $arg1 , $arg2);
 
	/**
	 * @return array
	 */
	public function stream_stat();
 
	/**
	 * @return int
	 */
	public function stream_tell();
 
	/**
	 * @param string $data
	 * @return int
	 */
	public function stream_write($data);
 
	/**
	 * @param string $path
	 * @return bool
	 */
	public function unlink($path);
 
	/**
	 * @param string $path
	 * @param int $flags
	 * @return array
	 */
	public function url_stat($path , $flags);
}

W najbliższym czasie postaram się opublikować gotową bibliotekę, tworzącą tymczasowy system plików w pamięci RAM, którą używam we własnym frameworku.

Potencjalne problemy

Nasuwają się tylko dwa. Nie wszystkie moduły dostarczane razem z językiem PHP posiadają wsparcie dla wrapperów użytkownika, niechlubnym przykładem jest tutaj np. biblioteka cURL, która nie potrafi zapisywać danych tymczasowych inaczej, niż w katalogu na dysku lokalnym. Drugim problemem jest to, że wrappery napisane w języku PHP nie są tak szybkie, jak ich odpowiedniki C/C++ skompilowane w moduły *.so.

Potencjalne możliwości

Zależą w głównej mierze od pomysłowości programistów. Własne wrappery umożliwiają np. odcięcie internetu poprzez nadpisanie protokołu HTTP, operacje na bazach danych funkcją fopen(), czy też zasymulowanie serwera FTP przy pomocy lokalnych systemów plików.

Dodaj odpowiedź