9:55
18/8/2010

Podpisywanie kodu w PHP

Podpisywanie kodu zapewnia określenie źródła pochodzenia oprogramowania oraz jego integralność. Podpisując kod udostępniany innym zabezpieczasz się przed tym, że np. ktoś wrzuci do niego backdoora podszywając się pod ciebie. Jeśli ktoś korzysta z podpisanego kodu (np. sterownika, biblioteki) ma gwarancję, że został on utworzony przez osobę, której publiczny klucz posiada i że nie został on zmodyfikowany przez osoby trzecie.

Autorem tego artykułu jest Krzysztof Kotowicz.

Użycie certyfikatów do podpisywania kodu jest często (czasem obowiązkowo) stosowane przez programistów .NET, J2EE czy np. piszących aplikacje mobilne na Androida. W środowisku webowym z uwagi na całkiem odmienną metodę dystrybucji oprogramowania idea ta jest mniej popularna. Tym niemniej jest to sprawdzona metoda na rozwiązanie problemu bezpiecznych uaktualnień oprogramowania czy systemu wtyczek – dużo lepsza niż często stosowane publikowanie sum kontrolnych plików (włamywacz może podszyć się pod stronę z uaktualnieniami i opublikować złośliwy kod razem z nową sumą kontrolną).

Phar (Php ARchive) dla PHP

Dzięki rozszerzeniu phar (Php ARchive) dla PHP (od wersji 5.2.1) podpisywanie kodu jest możliwe również w środowisku PHP. Co więcej, przy użyciu narzędzi PharUtil (disclaimer -– PharUtil jest mojego autorstwa) jest to łatwiejsze niż kiedykolwiek. Przedstawiamy krótki przewodnik dzięki któremu dowiesz się jak podpisać dowolny kod w PHP i jak zweryfikować złożony podpis.

Potrzebne będą:

Instalacja

Jeśli masz PHP w wersji < 5.3.0, najprawdopodobniej nie masz phar (sprawdź to w php -m lub phpinfo()). Uaktualnij PHP do wersji 5.3.x lub zainstaluj phar z pecl.php.net. Poniżej krótka instrukcja instalacji (Ubuntu):

$ sudo apt-get install php5-dev
$ sudo pecl install pecl/phar
$ echo "extension=phar.so" | sudo tee /etc/php5/conf.d/phar.ini

Dla użytkowników Windows: OpenSSL w PHP trzeba skonfigurować zgodnie z tą instrukcją. PharUtil instalujemy korzystając z kanału PEAR: pear.kotowicz.net

$ sudo pear channel-discover pear.kotowicz.net
$ sudo pear install kotowicz/PharUtil-beta

Konfiguracja

Na początku musimy umożliwić pharowi tworzenie archiwów -– dodajemy wpis konfiguracyjny phar.readonly=0 do php.ini. W Ubuntu:

$ echo "phar.readonly=0" | sudo tee -a /etc/php5/conf.d/phar.ini

Jeśli wszystko przebiegło bez zarzutu, powinniśmy mieć w systemie dostępne kilka nowych poleceń. Sprawdźmy, czy wszystko działa:

$ phar-build -v
phar-build version 0.4.0.
$ php -m | grep Phar
Phar

Podpisywanie kodu

Wygenerujmy parę kluczy do podpisywania kodu (możemy mieć wiele takich par):

$ mkdir cert/
$ cd cert/
$ phar-generate-cert
$ cd ..

Klucz prywatny (cert/priv.pem) będzie używany do podpisania kodu (nie udostępniamy go nikomu) – klucz publiczny (cert/pub.pem) musi być udostępniony do sprawdzenia podpisu (weryfikacji).

Do stworzenia podpisanej paczki z kodem (pliku .phar) służy program phar-build. Program w domyślnym ustawieniu zbiera (prawie) całą zawartość katalogu ./src i podpisuje ją kluczem ./cert/priv.pem. Stwórzmy więc naszą pierwszą paczkę (~/projekty/hello-world-php not included!):

$ mkdir src/
$ cp -r ~/projekty/hello-world-php/* src/
$ phar-build
phar-build 0.4.0

Building Phar archive from ./src...

adding ./src/test.php
adding ./src/index.php
Signing the archive with './cert/priv.pem'.
./output.phar created, exiting.

Weryfikacja podpisu

$ ls *.phar*
output.phar output.phar.pubkey

W trakcie podpisywania archiwum oprócz output.phar stworzony został również output.phar.pubkey – plik z kluczem publicznym, którym możemy zweryfikować poprawność podpisu. Phar wymaga, aby ten plik zawsze towarzyszył archiwum. PharUtil znosi to ograniczenie (np. phar-verify pozwala na podanie ścieżki do klucza), ale w ustawieniu domyślnym działa tak, jak oczekuje tego .phar.

$ phar-verify output.phar
phar-verify 0.4.0
Verifying Phar archive: output.phar...
Phar archive successfully verified.
All done, exiting.

Jakakolwiek modyfikacja paczki .phar (lub np. podmiana klucza publicznego) spowoduje, że podpis nie zostanie zweryfikowany:

$ sed -i 's/index/andex/' output.phar
$ phar-verify output.phar
phar-verify 0.4.0

Verifying Phar archive: output.phar...

Error: phar "/tmp/34433-output.phar" openssl signature could not be verified: openssl signature could not be verified

Używanie archiwum

Arhiwa Phar mają tę zaletę nad innymi sposobami dystrubucji kodu PHP, że dzięki wrapperowi strumienia phar:// są dla języka „przezroczyste” i są łatwo dostępne z poziomu skryptu – można stosować m.in. następujące konstrukcje:

include_once 'phar:///path/to/phar.phar';
include_once 'phar:///path/to/phar.phar/some/file/within.php';
echo file_get_contents('phar:///path/to/phar.phar/readme.txt');

Możemy również rozpakować paczkę phar do wybranego katalogu – służy do tego program phar-extract (błąd w obecnej wersji phar powoduje, że przy rozpakowaniu ignorowane są puste katalogi w archiwum).

Komendy phar-* to zwykłe skrypty php korzystające z funkcji phar/openssl. Tworzenie archiwów i inne funkcjonalności można oczywiście zrealizować samodzielnie z poziomu języka PHP – polecamy skorzystanie z dokumentacji rozszerzenia. Niestety, phar ma kilka „kruczków” , także związanych z bezpieczeństwem, którymi zajął się PharUtil. Zanim zaczniesz programować wg przykładów z dokumentacji, zajrzyj również do źródeł PharUtil

Bezpieczna dystrybucja kodu PHP

Przy użyciu podpisywania kodu i archiwów Phar możemy pokusić się o stworzenie architektury bezpiecznego ściągania i wykonywania kodu PHP z zewnętrznego serwera..

W takiej architekturze serwer – dostawca oprogramowania podpisuje paczki swoim kluczem prywatnym. Klient – posiadający klucz publiczny serwera, ściąga paczki .phar z określonego serwerowego URL, sprawdza podpis paczki oraz, jeśli weryfikacja się powiodła (co dowodzi, że paczka została utworzona przez zaufany serwer), kopiuje ją do określonej lokalizacji i używa dalej. Sam phar nie udostępnia takiej funkcjonalności – jest to możliwe dzięki klasie PharUtil_RemotePharVerifier. Odpowiednie przykłady i szczegółowa dyskusja na temat takiej architektury znajduje się w dokumentacji biblioteki oraz na moim blogu.

Przeczytaj także:



14 komentarzy

Dodaj komentarz
  1. Wszystko fajnie, ale czy ma większy sens dystrybucja podpisanego archiwum wraz z kluczem publicznym? Jeśli ktoś wstrzyknie malware, to może przecież stworzyć podobnie wyglądający klucz, podpisać nowe archiwum i będzie ono dystrybuowane z jego kluczem publicznym, który nadal będzie weryfikować archiwum poprawnie… Zakładam że jest możliwość weryfikacji z zewn. kluczem, ale moim zdaniem to powinien być default, a zamiast dystrybucji z kluczem publicznym, powinien być tylko URL do niego. A sam klucz publiczny (via http) najlepiej powinien być na innym serwerze niż kod, żeby było trudniej zmienić oba.

  2. @BartOwl: bez klucza publicznego nie zweryfikujesz podpisu. Powinieneś tylko się zastanowić, czy ufasz takiemu kluczowi. Phar używa foramtu OpenSSL. Jednak bez użycia kluczy z CA, model zaufania jest bliższy raczej Web Of Trust: http://pl.wikipedia.org/wiki/Sie%C4%87_zaufania

  3. > BartOwl

    To prawda – phar zastosował domyślnie dziwną metodę – klucz musi znaleźć się obok archiwum z kodem (ten sam katalog). Dlatego w ostatnim akapicie przedstawiam schemat innej architektury, gdzie klucz publiczny trzymasz u siebie lokalnie – odpowiednie rozwiązanie (biblioteka z kodem) już jest gotowe, przetestowane i podlinkowane.

  4. dodajemy wpis konfiguracyjny phar.readonly=0 do php.ini. W Ubuntu:
    $ echo “phar.readonly=0” | sudo tee -a /etc/php5/conf.d/phar.ini

    pliki w opisie i listingu sie roznia.

  5. “Jeśli ktoś korzysta z podpisanego kodu (np. sterownika, biblioteki) ma gwarancję, że został on utworzony przez osobę, której publiczny klucz posiada i że nie został on zmodyfikowany przez osoby trzecie.”
    vs
    https://niebezpiecznik.pl/post/nowy-sposob-ataku-na-windows-7/
    Co ciekawe, oba podpisane cyfrowo przez Realtek Semiconductor Corp. Sterowniki służą ukryciu plików rootkita na dysku USB.

    wiec jak to jest z tym podpisywaniem ? :D
    Realtek wypuscil rootkita ?

  6. @fwe Realtek nie pilnuje swojego klucza (lub zatrudnia rootkitowca)

  7. @fwe: albo to nie ten Realtek o którym myślisz :-). Klucz o dowolnej nazwie może wygenerować sobie każdy, a w txt nie było napisane, czy ten podpis był złożony kwalifikowanym kluczem.

  8. Tfu, miało być “kwalifikowanym certyfikatem”. Z góry uprzedzam, jako że głownie siedzę przy pingwinach to nie jestem pewny jakie klucze/certyfikaty dopuszcza m$ do podpisywania driverów windowych.

    • To był oryginalny klucz prywatny który im gwizdneli (co prawda przeterminiwany, ale gui windowsa w przypadku weryfikacji podpisanego softu nie jest bardzo hmmm przejrzyste…)

  9. Może ktoś napisać coś więcej jak to wygląda od strony kryptograficznej? Na jakich funkcjach skrótu to Phar się opiera?

  10. @ukash
    W obecnej wersji Phar wspiera różne metody sprawdzania integralności archiwum – poza sumami kontrolnymi w kilku wariantach (MD5/SHA1/SHA256/SHA512) jest (opisana w artykule) możliwośc podpisania kodu przy użyciu OpenSSL.

    Przy OpenSSL generowany jest podpis SHA1 – Phar wewnętrznie używa funkcji openssl_verify ( http://php.net/manual/en/function.openssl-verify.php ), dla której jest to domyślny algorytm. Nie da się tego obecnie zmienić w userland. Konkretne wywołanie znajdziesz w źródłach – http://svn.php.net/viewvc/pecl/phar/trunk/util.c?view=markup

  11. […] Szkolenie poprowadzi Krzysiek Kotowicz, security researcher i webdeveloper z wieloletnim doświadczeniem w projektowaniu i wdrażaniu aplikacji intra- i internetowych. Mogliście go już poznać na spotkaniach OWASP, gdzie prezentował techniki tworzenia i analizy malware’u w Javascript oraz metody zabezpieczenia przed SQL injection. Zaproponował również bezpieczną metodę podpisywania kodu w PHP.  […]

  12. […] Szkolenie poprowadzi Krzysiek Kotowicz, security researcher i webdeveloper z wieloletnim doświadczeniem w projektowaniu i wdrażaniu aplikacji intra- i internetowych. Mogliście go już poznać na spotkaniach OWASP, gdzie prezentował techniki tworzenia i analizy malware’u w Javascript oraz metody zabezpieczenia przed SQL injection. Zaproponował również bezpieczną metodę podpisywania kodu w PHP.  […]

  13. […] oraz metody zabezpieczenia przed SQL injection. Zaproponował również bezpieczną metodę podpisywania kodu w PHP, a niedawno pokazał ataki na rozszerzenia do przeglądarki Chrome. Obecnie interesuje się […]

Twój komentarz

Zamieszczając komentarz akceptujesz regulamin dodawania komentarzy. Przez moderację nie przejdą: wycieczki osobiste, komentarze nie na temat, wulgaryzmy.

RSS dla komentarzy: