Diversity w polskim IT
Michał Giza
Michał Giza Administrator systemów / DevOps

ionCube do zadbania o bezpieczeństwo kodu PHP

Sprawdź, dlaczego warto chronić kod napisany w PHP oraz jak wykorzystać do tego narzędzie ionCube.
9.03.20238 min
ionCube do zadbania o bezpieczeństwo kodu PHP

Jak wiadomo, PHP to język skryptowy, co oznacza, że napisane w nim skrypty są wykonywane przez odpowiedni interpreter. Aplikacje napisane w różnych językach skryptowych cechuje duża przenośność, z reguły można je łatwo uruchomić niezależnie od środowiska. Dlatego wiele programów narzędziowych, szczególnie tych działających wyłącznie z poziomu CLI, zostało napisanych właśnie jako skrypty.

Jest to w pewnym stopniu również wada języków skryptowych, ponieważ do działania takiego programu wymagany jest wybrany interpreter, który trzeba zainstalować. Rozwiązanie stanowi „kompilacja” skryptu (przykład to PyInstaller dla Python), chociaż nie zawsze będzie taka możliwość. PHP nadaje się raczej do tworzenia aplikacji internetowych i to jest jego główne zastosowanie, kod jest po prostu wykonywany na serwerze.

Opisywane podejście było stosowane „od zawsze” i w ogólnym znaczeniu jest poprawne. Natomiast w niektórych przypadkach zależy nam na solidnym zabezpieczeniu naszej aplikacji. W przypadku PHP kod aplikacji, a często także dostępy do bazy danych czy innych usług, zapisane są w jawnej postaci w plikach o rozszerzeniu php lub innych tekstowych. Dlatego sytuacja uzyskania do nich dostępu przez nieuprawnione osoby może być problematyczna.

Innym istotnym powodem, dla którego powinniśmy chronić napisany przez nas kod, jest wdrożenie aplikacji na infrastrukturze kontrolowanej przez klienta (do której posiada on dostęp). Raczej nie chcielibyśmy, aby klient stosował nieuczciwe praktyki i przykładowo udostępniał nasz komercyjny kod innym osobom czy korzystał z aplikacji po zakończeniu wsparcia technicznego.

Sensownym zabezpieczeniem jest zastosowanie sprawdzonego rozwiązania ionCube. Najprostsza forma ochrony kodu to jego zakodowanie. Jest ona dostępna w podstawowej wersji Basic. W wersjach Pro i Cerberus mamy dodatkowo opcję dodania restrykcji dla nazwy domeny czy adresu IP, a nawet adresu fizycznego MAC (tylko w wersji Cerberus). Encoder jest rozwiązaniem płatnym, natomiast konieczny do obsługi po stronie serwera ionCube loader możemy bezpłatnie pobrać i skonfigurować w ustawieniach PHP.

W celu zapoznania się z opisywanym rozwiązaniem warto użyć wersji trial. Na dedykowanej stronie zwyczajnie podajemy adres e-mail oraz wybieramy platformę, na której będziemy używać modułu Encoder. Wersja testowa posiada oczywiste ograniczenie — zakodowane pliki będą możliwe do zdekodowania wyłącznie przez 36 godzin.

Linux

W celu skorzystania z Encoder w systemie Linux wystarczy pobrać i wypakować archiwum, do którego link otrzymaliśmy w wiadomości e-mail. W katalogu znajduje się plik ioncube_encoder.sh, za pomocą którego można przeprowadzić operację kodowania plików lub całych katalogów z witrynami. Dostępnych jest kilka parametrów, z jakimi możemy uruchomić skrypt, ale podstawowe użycie stosowane do zakodowania całego katalogu sprowadza się do:

. ioncube_encoder.sh <katalog_ze_strona> -o <katalog_wynikowy>


Aktualnie ionCube obsługuje wszystkie wersje PHP od 4.x do 8.1, czyli jedynie najnowsza wersja 8.2 nie jest wspierana.

W podany niżej sposób zabezpieczymy kod znajdujący się w katalogu wordpress, a pliki wynikowe umieścimy bezpośrednio w katalogu, na który wskazuje document root przykładowej domeny:

. ioncube_encoder.sh wordpress -o public_html/phptest


Możemy określić użytą wersję PHP poprzez dodanie jednego argumentu, jest to jednak opcjonalne:

. ioncube_encode.sh [-4 | -5 | -53 | -54 | -55 | -56 | -71 | -72 | -74 | -81]


Określenia adresu IP, domeny lub adresu MAC hosta, z którego kod może być wykonywany, dokonamy poprzez parametr --allowed-server. Próba uruchomienia aplikacji na niezgodnym hoście zakończy się błędem. Warunki możemy zgrupować według schematu:

--allowed-server '<domena>@<adres_ip>{<adres_mac>}'


Domyślnie kodowane są tylko pliki *.php, można sprecyzować również inne pliki (np. .env czy pozostałe przechowujące dane uwierzytelniające) do zabezpieczenia poprzez --encode. Trzeba zwrócić uwagę, że serwer WWW prawdopodobnie jako kod PHP będzie traktował jedynie pliki o takim rozszerzeniu. Więc jeśli zakodujemy zwykły plik tekstowy, to w domyślnym ustawieniu po otwarciu go w przeglądarce zobaczymy kod PHP zamiast właściwej zawartości (co w przypadku tradycyjnych plików PHP jest bardzo niebezpieczne).

Rozwiązaniem może być dodanie do .htaccess:

AddType application/x-httpd-php .txt


Z kolei w NGINX można zmodyfikować istniejącą dla plików *.php sekcję location:

location ~ \.(php|txt)$ {
#pozostała konfiguracja }


Do kwestii kodowania wszystkich plików trzeba podejść rozsądnie, bo łatwo w tym miejscu o overengineering. Postęp trwającej operacji w Encoder uzyskamy parametrem -v.

Encoding PHP file /home/users/mgiza/wordpress/wp-admin/includes/user.php as /home/users/mgiza/public_html/phptest/wp-admin/includes/user.php
Encoding PHP file /home/users/mgiza/wordpress/wp-admin/includes/widgets.php as /home/users/mgiza/public_html/phptest/wp-admin/includes/widgets.php
Creating directory /home/users/mgiza/public_html/phptest/wp-admin/js
Creating directory /home/users/mgiza/public_html/phptest/wp-admin/js/widgets
Copy /home/users/mgiza/wordpress/wp-admin/js/widgets/custom-html-widgets.js => /home/users/mgiza/public_html/phptest/wp-admin/js/widgets/custom-html-widgets.js
Copy /home/users/mgiza/wordpress/wp-admin/js/widgets/custom-html-widgets.min.js => /home/users/mgiza/public_html/phptest/wp-admin/js/widgets/custom-html-widgets.min.js


Struktura zakodowanego pliku PHP składa się z poniższej instrukcji warunkowej oraz ciągu bytecode.

if(extension_loaded('ionCube Loader')){die('The file '.__FILE__." is corrupted.\n");}echo("\nScript error: the ".(($cli=(php_sapi_name()=='cli')) ?'ionCube':'<a href="https://www.ioncube.com">ionCube</a>')." Loader for PHP needs to be installed.\n\nThe ionCube Loader is the industry standard PHP extension for running protected PHP code,\nand can usually be added easily to a PHP installation.\n\nFor Loaders please visit".($cli?":\n\nhttps://get-loader.ioncube.com\n\nFor":' <a href="https://get-loader.ioncube.com">get-loader.ioncube.com</a> and for')." an instructional video please see".($cli?":\n\nhttp://ioncu.be/LV\n\n":' <a href="http://ioncu.be/LV">http://ioncu.be/LV</a> ')."\n\n");exit(199);

Windows

Aplikacja Encoder dla systemu Windows ma tę przewagę, że wszystkie czynności można wykonać z poziomu interfejsu graficznego. Po zainstalowaniu programu uruchamiamy Encoder GUI i z menu po lewej stronie wybieramy Create New Project… Wystarczy uzupełnić właściwie tylko pola w karcie Quickstart.

Projekt zostanie utworzony. Powinniśmy zapisać jego ustawienia np. z użyciem skrótu Ctrl+S. „Zbudowanie” aplikacji odbywa się po wybraniu ikony strzałki lub naciśnięciu F5. Zakodowany kod pojawi się w folderze ustawionym jako Project target.

Przesyłanie wielu małych plików na serwer to zwykle długotrwały proces. Można jednak automatycznie spakować wynikowe dane do archiwum. Przechodzimy do Projects Settings i w karcie Target zaznaczamy checkbox przy opcji Create package from target folder, po czym zaznaczamy interesujący nas format. Tak utworzone archiwum wystarczy wypakować. Jeśli do serwera mamy dostęp wyłącznie FTP, to polecam spróbować wykonać w przeglądarce analogiczny plik PHP:

<?php exec('tar -zxf app.tar.gz');


Dodatkowo do .htaccess powinno się wtedy dodać (wypakowanie archiwum może trwać dłużej niż domyślne 30 sekund):

php_value max_execution_time -1


Dzięki temu proces uruchomienia aplikacji będzie zdecydowanie szybszy, ponieważ polecenie tar (lub unzip) zostanie wykonane bezpośrednio w systemie operacyjnym serwera. Czasem spotyka się sytuację, że na hostingach funkcja exec() jest blokowana lub zostały ustawione limity czasu wykonywania procesów, ale zawsze dobrze jest znać podobne sposoby.

W tej samej karcie znajduje się również opcja pozwalająca na przesłanie wynikowych plików poprzez FTP na docelowy serwer. Ma to duży sens przy mniejszych projektach, gdy ilość danych nie jest znaczna i można je szybko przesłać.

Możliwe do dodania ograniczenia zamieszczono w karcie Restrictions. Jak poprzednio, możemy ustawić wykonywanie kodu jedynie z określonych domen, adresów IP czy MAC. Poniżej tego pola znajduje się także funkcjonalność dodania „wygasania” plików po pewnym czasie. W karcie Messages możemy ustawić własne komunikaty błędów.

Jeśli zainstalujemy ponadto ionCube Package Foundry uzyskamy możliwość zbudowania instalatora naszej aplikacji (EXE). Najprostszy sposób polega na standardowym utworzeniu folderu bądź archiwum z zakodowanym kodem w narzędziu Encoder. Następnie uruchamiamy tę drugą aplikację i uzupełniamy kartę Basic zgodnie ze zrzutem ekranu:

Po kliknięciu Build zostanie utworzony plik setup.exe, który możemy uruchomić na dowolnym hoście, ponieważ dane wskazane jako Package source zostały w nim zainkludowane. Podajemy dane do połączenia FTP lub SFTP oraz określamy ścieżkę i adres, pod którym aplikacja będzie dostępna. Następnie rozpocznie się proces przesyłania danych.

ionCube loader

Po umieszczeniu plików w katalogu root witryny będą one standardowo wykonywane przez interpreter na serwerze. Najważniejszy jest fakt, że dekodowanie odbywa się „w locie” i jest realizowane przez odpowiedni dla danej wersji PHP loader. Jak wspomniałem, loader musi być skonfigurowany na serwerze. W praktyce nie ma z tym większego problemu, wsparcie dla ionCube jest dostępne na wielu hostingach.

Konfigurację w naszym własnym środowisku możemy równie łatwo zrealizować. Należy pobrać archiwum zawierające pliki rozszerzeń dla konkretnych wersji PHP. Przykładowo dla wersji 8.1 są to:

ioncube_loader_lin_8.1.so
ioncube_loader_lin_8.1_ts.so


Szczegółów konfiguracji PHP dostarczy wykonanie funkcji phpinfo() w terminalu czy przeglądarce. Interesujące nas informacje znajdują się w wierszach PHP Extension Build i extension_dir:

php8.1 -i|grep -E "PHP Extension Build|extension_dir"
PHP Extension Build => API20210902,NTS
extension_dir => /usr/lib/php/20210902 => /usr/lib/php/20210902


Jak widać, używana jest wersja NTS (non-thread-safe), a rozszerzenia są przechowywane w katalogu /usr/lib/php/20210902. W związku z tym do tej lokalizacji należy skopiować plik ioncube_loader_lin_8.1.so (bez „ts” w nazwie):

sudo cp ioncube/ioncube_loader_lin_8.1.so /usr/lib/php/20210902


Do pliku php.ini dopisujemy:

zend_extension = ioncube_loader_lin_8.1.so


Na koniec restartujemy serwer WWW lub usługę PHP-FPM. Warto dodać, że zwykle stosowany na hostingach współdzielonych serwer Apache może działać w trzech „trybach” MPM: prefork, worker i event. Tryby worker i event do działania wymagają PHP-FPM, prefork obsługuje zarówno tradycyjny mod_php (apache2handler), jak i PHP-FPM. Inna kluczowa różnica między nimi z perspektywy użytkownika to wydajność — aplikacja potrafi działać zdecydowanie szybciej, jeśli Apache jest uruchomiony wraz z mod_event czy mod_worker, wykorzystanie pamięci operacyjnej także jest wyraźnie mniejsze.

Powyższy sposób został pokazany na przykładzie systemu Linux, ale z powodzeniem zadziała również w innych konfiguracjach. Zasadnicza czynność to sprawdzenie wartości PHP Extension Build i extension_dir oraz wybranie na tej podstawie odpowiedniego pliku z pobranego archiwum. Można też użyć skryptu, który po wykonaniu w przeglądarce wyjaśni, jakie konkretnie czynności należy wykonać w naszym środowisku. Loader działa poprawnie również w kontenerach Docker.

Podsumowanie

ionCube jest znanym rozwiązaniem służącym do ochrony kodu PHP poprzez kodowanie i ograniczenia środowiska wykonania. Jeśli zależy nam na zabezpieczeniu naszej własności intelektualnej czy przeciwdziałaniu nielegalnej dystrybucji kodu powinniśmy wdrażać podobne zabezpieczenia. Należy też pamiętać o repozytoriach i środowiskach deweloperskich, upewniać się, czy dostęp mają wyłącznie autoryzowane osoby.

<p>Loading...</p>