Co siedzi w działającej aplikacji? – manhole

Zdjęcie, które ilustruje ten wpis wymaga słowa komentarza. Przedstawia właz do ulicznego kanału, po angielsku – manhole.

manhole – a covered opening in a road that a worker can enter in order to reach underground pipes, wires, or drains that need to be examined or repaired – Cambridge Dictionary

Jak to się ma zaglądania w bebechy działającej aplikacji przekonasz się czytając kolejne akapity.

Podejrzenie zawartości zmiennych w działającym programie bywa nieocenioną pomocą, gdy debuggujemy błędy – czytaj niepożądane feature’y. W środowisku developerskim są w zasadzie trzy możliwe podejścia. Po pierwsze tak zwany dupa debugging, gdzie wypisujemy nazwę miejsca, w którym plecy tracą swoją szlachetną nazwę razem z danymi, które chcemy poznać. Dupa, najczęściej z dołączonym numerkiem pozwala łatwo namierzyć miejsce w programie, które badamy – szczególnie, gdy robi się ich kilkanaście.

O wiele czystszą metodą będą testy, jednakże warunkiem koniecznym jest posiadanie ładnego, testowalnego kodu (wszyscy taki mamy, prawda? 😀 ). Ostatnią z technik jest skorzystanie z programistycznej zabawki, debuggera. Wymaga odpowiednich narzędzi, ale bez problemu znajdą się darmowe odpowiedniki. W skrócie – oznaczamy miejsce w kodzie, które nas interesuje zastawiając tak zwaną pułapkę. W chwili gdy egzekucja kodu przechodzi przez oznaczoną linię program zostaje wstrzymany, a my możemy podglądać i ingerować w zawartość zmiennych.

To już coś. Swoją drogą, prezentowane zrzuty pochodzą z Pycharm’a, a podstawy podstaw obsługi debuggera można opanować dzięki temu filmikowi.

Debugger to niezwykle potężne narzędzie, które świetnie się sprawdza w środowisku developerskim. W ŚRODOWISKU DEVELOPERSKIM. Natomiast wszystkie fajne problemy dzieją się w wiadomym miejscu – na produkcji!

Można wprawdzie salwować się zdalnym debuggerem (PyCharm też to ma!), ale takie podejście ma dwie poważne wady:

  • aplikacja pod debuggerem działa wolniej
  • wejście w pułapkę (ang. breakpoint) spowoduje zatrzymanie programu. Najczęściej nie można na to pozwolić.

Nie wspominając o tym, że trzeba przecież aplikację zrestartować i liczyć, że znowu wpadnie w niepożądany stan. Z tej perspektywy dupa debugging zaczyna wyglądać jak niezłe rozwiązanie…

Właz konserwacyjny dla programistów

Wracając do samego początku artykułu – istnieje jeszcze inne rozwiązanie znane pod nazwą ‚manhole’ i dosłownie oznacza właz konserwacyjny – tylko że w tym przypadku dla programisty. Odpowiednia paczka pod tą samą nazwą jest dostępna na PyPI, zainstalować można ją standardowo pip’em:

Nie działa na Windowsie.

Ten właz prowadzi do interaktywnej konsoli Pythona działającej w kontekście naszego programu i umożliwia nam podglądanie i manipulację udostępnionych mu danych. Manhole ‚wciska się’ w program jako osobny wątek. Aby zademonstrować działanie, posłużę się przykładem składającym się z prostego serwera HTTP:

Serwer HTTP nasłuchuje na porcie 8000 [linia 6.]. Kolejne odświeżenia strony powodują zwiększenie o 1 licznika odwiedzin, przechowywanego jako pole klasy Counter [9] o nazwie value [10] poprzez metodę klasy – next_value [13].

Wykorzystanie manhole jest w linii 32 – tutaj instruujemy bibliotekę, że ma się aktywować po otrzymaniu przez program sygnału SIGUSR1. Jeżeli pominiemy ten argument, to nasz właz zostanie otwarty razem z programem (w miejscu wywołania manhole.install()). Po otwarciu dostępu, możemy dobić się do konsoli poprzez unix socket – na przykład programem netcat:

Domyślnie unix socket jest umieszczany w katalogu /tmp z nazwą manhole-<pid procesu>. Jest to oczywiście konfigurowalne zachowanie (jeden z argumentów manhole.install() – socket_path odpowiada za nazwę). Potrzebny jest jeszcze pid procesu z odpalonym programem i można już wysłać rozkaz otwarcia włazu:

Drugi z wykorzystywanych w przykładzie nazwanych argumentów – locals określa słownik z nazwami, które będą dostępne w otwartej konsoli. W tym przypadku udostępniamy tylko klasę Counter pod tą samą nazwą. To wystarczy, by podglądać i wprowadzać modyfikacje do działającego kodu tego prostego serwerka.

Oczywiście podejrzenie wartości pola w klasie to pikuś, podobnie z podmianą. Korzystając z dynamicznej natury Pythona, można w ten sam sposób modyfikować zachowanie programu:

Od tej chwili kolejne odświeżenia strony będą zmniejszać licznik, zamiast go zwiększać. Z kreatywnych zastosowań do głowy przychodzi mi jeszcze wymuszenie przeładowania modułu poprzez usunięcie konkretnego wpisu z sys.modules, zresztą dokumentacja wspomina o takim zastosowaniu:

sys.modules: This is a dictionary that maps module names to modules which have already been loaded. This can be manipulated to force reloading of modules and other tricks.

A bezpieczeństwo?

Artykuł byłby niekompletny, gdyby nie poruszał kwestii związanych z bezpieczeństwem takiej zabawy. Po pierwsze, to dostęp do unix socket’u utworzonego przez manhole umożliwia egzekucję w zasadzie dowolnego kodu. Jeżeli proces działa z uprawnieniami użytkownika uprzywilejowanego, oznacza to potencjalnie poważne kłopoty.

Na szczęście systemy operacyjne umożliwiają wysyłanie sygnałów tylko do procesów, które należą do nas (działają z uprawnieniami naszego użytkownika) lub gdy to my działamy z roota. Tak więc bez uzyskania specjalnych uprawnień atakujący nie może otworzyć włazu. Widzę tu jednak potencjalny problem, gdy manhole zostanie otwarty i uprawnienia do unix socketu zostaną zmienione na bardziej liberalne, jak 0777. Wtedy, o zgrozo, każdy atakujący będzie mógł wykorzystać nasz program do wykonania kodu z uprawnieniami, jakich nie powinien dostać.

Pewnym zabezpieczeniem jest też filozofia dostępu przez unix socket – czyli możliwe tylko z tego samego serwera.

Ostatnie kwestie dotyczą samego programu. Po pierwsze, nie każda aplikacja może strawić to, że w jej ramach uruchomiliśmy dodatkowy wątek. Są rozwiązania, które używając monkey patchingu robią brzydkie rzeczy z modułem wątków w bibliotece standardowej. Tej samej techniki używa zresztą manhole.

Operacje modyfikujące dane z interaktywnej konsoli mogą być potencjalnie niebezpieczne i dawać nieoczekiwane rezultaty, szczególnie gdy dotykane zasoby będą zmieniane przez inne jednostki egzekucji (tj wątki). Jedyną w miarę bezpieczną operacją jest podglądanie zawartości zmiennych.

Zakończenie

Powyższy artykuł to tylko wierzchołek góry lodowej – najwięcej informacji można znaleźć na stronie pypi manhole, polecam lekturę szczególnie ze względu na listę podobnych rozwiązań oraz omówienie opcji konfiguracyjnych.

photo credit: visually_conscious Manhole in Dumbo via photopin (license)

 

Jedna odpowiedź do “Co siedzi w działającej aplikacji? – manhole”

  1. Z tym debuggerem jest jeszcze taki problem że fakt iż on spowalnia działanie aplikacji uniemożliwia powtórzenie pewnych sytuacji typu race-condition. Co do bezpieczeństwa – nikt przy zdrowych zmysłach nie da 0777 na taki socket 🙂 chyba że lubi adrenalinę 😉 Tak czy siak – fajny wpis ;DDD

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *