Atak na zabezpieczenia protected/private Javy/Scali i PHP5

Opublikowane przez Jarosław Zabiełło Sun, 21 Jun 2009 03:45:00 GMT

Być może jednym z argumentów, dla których Ruby znajduje więcej zainteresowania ze strony programistów Javy niż Python, jest podobny mechanizm zabezpieczenia dostępu do atrybutów klasy za pomocą słów kluczowych private i protected. W języku PHP 5 wprowadzono podobny mechanizm. Ci jednak, którzy myślą, że te kwalifikatory zakresu dostępu stanowią jakiekolwiek poważne zabezpieczenie, są w głębokim błędzie. W banalny sposób można to obejść zarówno w w PHP 5, jak i w Javie. Przy czym o ile, w Javie wynika to raczej z celowego projektu w mechanizmie refleksji, w PHP 5 wygląda to na dziurę bezpieczeństwa…

Aby przełamać zabezpieczenia dostępu do atrybutów private i protected w wypadku Javy wystarczy użyć refleksji. W związku z tym, że Scala korzysta z tego samego mechanizmu refleksji co Java, problem ten dotyczy tak samo Scali. Poniższy kod wyświetla wartości atrybutów private i protected.

   
import java.lang.reflect.{Field}

class SecureData  {
  private val priv = "my-private-value" 
  protected val prot = "my-protected-value" 
}

object Main {
  def main(args:Array[String]) {
    val obj = new SecureData()
    for(f <- obj.getClass.getDeclaredFields) {
      f.setAccessible(true)
      println(f.getName+"="+f.get(obj))
    }
  }
}

Powyższy kod nie jest jakimś hackerskim zabiegiem. Jest zwykłym użyciem tego co daje refleksja. Tzn. domyślnie metoda setAccessible pozwala zmienić dostęp do tych atrybutów (można to samo zrobić z metodami), o ile nie podejmiemy specjalnych działań zapobiegawczych poprzez użycie np. SecurityManagera.

W PHP 5 użycie refleksji nic nie pomoże, bo nie ma tam metody setAccessible. Jednakże, na skutek błędów w implementacji PHP 5 (sprawdzane włącznie z najnowszym, wydanym 2 dni temu, PHP 5.3 RC4) złamanie zabezpieczeń tego języka jest też bardzo łatwe. Poniższa funkcja expose wyświetli każdy atrybut danej instancji klasy, bez względu na to czy atrybut ten jest chroniony kwalifikatorem protected czy private.

<?php
class SecureData {
  private $priv="my-private-value";
  protected $prot="my-protected-value";
}

function expose($obj, $attr_name) {
  $a = (array)$obj;
  $r = new ReflectionProperty($obj, $attr_name);
  if ($r->isPrivate())
    return $a["\0".get_class($obj)."\0".$attr_name];
  elseif ($r->isProtected()) 
    return $a["\0*\0".$attr_name];
  else 
    return $obj->$attr_name;
}

$c = new SecureData;
echo expose($c, 'priv'); # => my-private-value
echo expose($c, 'prot'); # => my-protected-value
?>

Można też iść “na całość” i zmodyfikować treści udostępnianie przez te atrybuty, co może być trochę katastrofalne w skutkach. Wystarczy zamiast rzutowania do array’a użyć metody settype. Jedyną wadą jest to, że po użyciu settype zniszczona została cała informacja o klasie i interfejsie obiektu.

<?php
function hack(&$obj, $attr_name, $value) {
  $r = new ReflectionProperty($obj, $attr_name);
  $class = get_class($obj);
  settype($obj, 'array');
  if ($r->isPrivate())
    $obj["\0$class\0".$attr_name] = $value;
  elseif ($r->isProtected()) 
    $obj["\0*\0".$attr_name] = $value;
} 

$c = new SecureData;
hack($c, 'priv', 'my-hacked-private-value'); 
var_dump($c);
/*
array(2) {
  ["Cpriv"]=>
  string(23) "my-hacked-private-value" 
  ["*prot"]=>
  string(18) "my-protected-value" 
} 
*/
?>

Na tym tle stanowisko Pythona nie brzmi wcale aż tak źle. W Pythonie wszystkie metody i atrybuty są publiczne. Jeśli programista chce to ma dostęp do wszystkiego. I tak przecież będzie miał taki dostęp jak się uprze, więc po co mu utrudniać życie, prawda? Wg filozofii Pythona, jedyną zaletą posiadani takich kwalifikatorów dostępu byłoby to, aby uniknąć pomyłki a nie, aby bić programistę po łapach. W Pythonie rozwiązano to za pomocą tzw. “manglingu” nazw. Otóż metody (i atrybuty) których nazwy zaczynają się od dwóch znaków podkreślenia (ale równocześnie nie mogą się kończyć takimi znakami) są traktowane specjalnie przy próbie uzyskania do nich dostępu. Tzn. formalnie nadal są public (tak jak wszystko inne), trzeba jednak użyć trochę innej, lekko zamotanej, składni. I to jest wystarczające, aby programista się nie pomylił.

                   
class SecureData(object):
  __priv = "my-private=value" 
  _prot = "my-protected-value" 

try: 
  print SecureData.__priv
except AttributeError:
  print "Wrong syntax, you lame!" 

print SecureData._SecureData__priv 
# => my-private=value  

Zobacz też

Tagi , , , , , , ,  | 27 comments

Comments

  1. Avatar yrk powiedział about 3 hours later:

    Umyka mi chyba sens rozważań w jakim języku i jak łatwo jest złamać “zabezpieczenie” typu private. To trochę tak, jak określać bezpieczeństwo opisując “atak” na komputer znajdujący się fizycznie pod ręką. Przecież jest jasne, że jeśli ktoś dysponuje kodem, to – trudniej lub łatwiej – zawsze dojdzie do tego, jak to zostało napisane, i – w najgorszym wypadku – po prostu przepisze klasę z dostępem do wszystkiego na czym mu zależy.

    Programowanie obiektowe jest umową, której nie łamie się nie dlatego, że nie można, ale dlatego, że takie działanie powoduje katastrofalne skutki w projekcie (w fazie maintenance i ew. reuse). Co z tego, że mogę mieć dostęp do prywatnego pola, skoro przy najbliższej zmianie implementacji klasy dostanę za to po łapach?

    O tym, jak nawet teoretycznie obiektowe mechanizmy mogą na wywieść w pole pisze Allen Holub w http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html (trochę zbyt ortodoksyjne, jak dla mnie, ale pokazuje pewne niebezpieczeństwa). No cóż, Java sprawdza się w dużych projektach, gdzie takie niuanse mogą mieć znaczenie. W projektach np. w Rails raczej wątpię.

  2. Avatar Antek powiedział about 4 hours later:

    Dzięki za artykuł. To ważna informacja dla programistów, bo czasem o czymś takim się po prostu nie myśli.

  3. Avatar LBO powiedział about 4 hours later:

    Przedstawiasz sytuacje, gdzie Java i PHPbad, ponieważ mają zabezpieczenia, które można złamać, a Python jest good, bo takich zabezpieczeń nie ma (chociaż z kodu, który napisałeś wynika, że istnieją – tylko obejście jest wbudowane w język :D)

  4. Avatar pi0tr powiedział about 5 hours later:

    Szczerze mówiąc nigdy nie myślałem o private i protected w kategorii szeroko pojętego “bezpieczeństwa”. Dla mnie jest to po prostu mechanizm enkapsulacji, nigdy nie przyszłoby mi do głowy wykorzystanie tego mechaznizmu do czegokolwiek innego niż ukrycie stanu czy implementacji. Obejście tego mechanizmu nie jest dla mnie “złamaniem” bezpieczeństwa tylko złamaniem pewnej zasady, która nazywa się information hiding (hiding, nie security!).

  5. Avatar hauser powiedział about 5 hours later:

    Tytuł i pierwszy paragraf sugerują, że piszesz o wykradaniu super tajnych danych z obiektów. A chyba nie o to (mam nadzieję) chodzi w tym artykule :)

    Jako ciekawostka można wykorzystać ten mechanizm do podmiany wartości Stringów w Javie: http://www.javaspecialists.eu/archive/Issue014.html

  6. Avatar Jan Koprowski powiedział about 5 hours later:

    Chwała Bogu, że można odczytać private z zewnątrz. Testowanie stanu klas bez tego mechanizmu byłoby katastrofą !

  7. Avatar Secator powiedział about 6 hours later:

    @Pi0tr – No własnie! Public/private ma znaczenie tylko w utrzymywaniu ‘czystego’ namespace’u metod i zmiennych obiektu danej klasy. W Javie, Rubym, PHP jest to element składni, w Pythonie jest to umowa między programistami. Efekt jest dokładnie ten sam.

  8. Avatar Robert Pankowecki powiedział about 6 hours later:

    Jeśli zaś chodzi o Ruby to zmiana widoczności metody też jest banalna a użycie send() żeby wywołać metodę nawet bez zmieniania jej widoczności jeszcze prostsze.

  9. Avatar Jarosław Zabiełło powiedział about 7 hours later:

    @LBO: nie twierdzę, że Java jest tu zła, bo możliwość zmiany widoczności elementów klasy jest tam dostępna, powiedzmy, oficjalnie. Jednak w wypadku PHP5 to jest typowy, nieudokumentowany hack, i to nie wygląda najlepiej.

    @pi0tr: może to i nie jest złamanie bezpieczeństwa (raczej reguł enkapsulacji), ale spotkałem wiele osób zarzucających Pythonowi “brak bezpieczeństwa” z racji nie posiadania przez niego tychże struktur w składni języka. Dlatego podałem parę przykładów pokazujących ile są warte te ich “zabezpieczenia”.

  10. Avatar sprae powiedział about 19 hours later:

    Można to nawet skrócić, bo owe struktury do “zabezpieczania”, jak wiemy służą głównie kompilatorowi, aby mógł wskazać możliwie dużo błędów w procesie kompilacji.

  11. Avatar Jiima powiedział 1 day later:

    @JZ O, cieszę się, że znowu piszesz, do niedawna jedynie aktywność blipowa dawała do zrozumienia że żyjesz.

    Tak naprawdę, to cała ta chryja z private/protected i “jestem wielka haker, czytam pola prywatne” to jakieś kosmiczne nieporozumienie. Private / protected służy wyłącznie na oznaczenie które elementy klasy są częścią implementacji a które interfejsu. Pola publiczne nie powinny istnieć w ogóle. Reszta to kwestia kontraktu – np. w takim Eifellu jest takich kontraktów więcej i nikt nigdy nie pomyślał że są one dla bezpieczeństwa… dokładniej, są. Dla bezpieczeństwa programisty by kompilator / runtime dało mu po łapkach jak wyprodukuje buga. Dyskusja czy takie podejście (statyczne sprawdzanie kodu v. seria unitów która sprawdza dynamiczny kod “w praniu”) jest lepsze nie ma sensu, jedni lubią tak inni inaczej.

    Dostęp do prywatnych pól w Javie (a także dostęp do prywatnych – innych nie ma – pól w Ruby) nie jest “pomyłką” ale narzędziem, z którego należy rozważnie korzystać – np. przy tworzeniu mechanizmów serializacji. Używa tego chociażby Hibernate.

    Czy bug w PHP jest “błędem bezpieczeństwa”? Nie. Powiedz mi, jak można to wykorzystać do włamania na witrynę w PHP 5.3. Prawdę mówiąc, większym bugiem w PHP jest cały model obiektowy, pretensjonalnie wzorowany na Javie (interfejsy, statyczne typowanie – ale tylko obiektów i tablic) podczas gdy Java jest zupełnie innym rodzajem języka, statycznie i mocno typowanym, podczas gdy PHP jest typowane nie tylko dynamicznie, ale na dokładkę słabo (kłania się dziedzictwo Perla).

    Tak offtopic, bo usłyszałem sporo sprzecznych odpowiedzi. Które maki są na procesorach 64bit? Maca Pro możesz pominąć :). Potrzebuję maszyny na której chodzi java 6, a zdaje się Apple wypuściło ją tylko dla 64bit leoparda.

  12. Avatar Jarosław Zabiełło powiedział 1 day later:

    @Jiima: a czy my jesteśmy dziećmi aby jakiś kompilator bił nas po łapach? ;) Przecież i tak można dobrać się do prywatnych pól. Manglowanie nazw w Pythonie jest wystarczające, aby się nie pomylić. Mnie tam publiczne pola nie przeszkadzają o ile jest możliwość użycia przezroczystych akcesorów. W Javie to jest bardzo kiepsko zrobione. Scala nie ma z tym problemu.

    Co do modelu obiektowego PHP wzorowanego na języku z “innej bajki” to 100% zgoda. Ja tam zawsze mówię że twórcy języka PHP to lamerzy. ;)

    Co do Maka, to wszystkie modele mające Core2Duo (a nie CoreDuo) są 64bit. Czyli w tej chwili dotyczy to wszystkich modeli Maków jakie są w ofercie (włącznie z Mac mini i białym MacBookiem) Leopard nie jest w pełni 64bit. Nowy Snow Leopard (który oficjalnie jeszcze nie wyszedł) ma być pozbawiony kodu universal (dla PPC), czyli będzie działał tylko na intelowskich Makach (ma też używać karty graficznej aby wspomagać główny procesor) Większość jego kodu ma być 64bit, ale na 100% będzie na nim działać też kod 32bit.

  13. Avatar Jiima powiedział 2 days later:

    @JZ No więc, ty jesteś gieroj i nikt cię po łapach bić nie musi. Trochę więc nie rozumiem twojej fascynacji Scalą – statycznie, mocno typowany język funkcyjny, pisany tak by bić po łapkach. Prywatne pola są jak już wspomniałem również w Rubym, Smalltalku i wielu innych. Nie ma ich natomiast w Javascripcie. Ja gieroj nie jestem, ale mogę gierojować w Pythonie i Javascripcie, nawet sporo tego robię, dlatego dyskusja “static versus dynamic” czy “public versus 70 levels of access control” jest trochę poza mną. Ale znam ludzi z zespołów w których zdarzyło mi się pracować i wiem, że czasem dodatkowa warstwa statycznej analizy kodu ma swoje zalety. Weźmy chociażby takiego (znienawidzonego tu i ówdzie) Perla – pragma strict jest jednym z najlepszych dodatków do tego języka, zabezpieczająca chociażby przed durnym zadaniem poszukiwania literówki w nazwie zmiennej. Kod strict lepiej też współpracuje z różnymi mechanizmami w IDE. Tak więc coś za coś. Irytującą cechą Pythona jest to, że tam czasami nie ma “coś za coś” – metaprogramowanie w Pythonie, pomimo jego dynamicznego charakteru nie jest wcale aż takie łatwe i miłe jak np. w Ruby. Pierwsze co by się przydało, to możliwość stworzenia anonimowej funkcji nieco dłuższej niż wyrażenie lambda…

  14. Avatar Jiima powiedział 2 days later:

    @JZ Za informacje o modelach maców dzięki, sprzedawca w Ispocie nie był w stanie mi powiedzieć co z tymi 64-bit procesorami, choć wyglądał na typowego mac-fana, z tych co atakują bejsbolem każdy napotkany PC. Głównie nie zależy mi na tej 64-bitowej architekturze, co właśnie na Javie 6 a ona na 32-bitowych Leopardach nie chodzi.

  15. Avatar Jacek Laskowski powiedział 2 days later:

    Nie programuję w Scali, PHP czy Pythonie i jedynie ze względu na Javę w tytule zerknąłem na ten wpis. Muszę jednak przyznać, że tyle informacji w tak krótkim wpisie już dawno nie czytałem i to z takim zainteresowaniem. Muszę samemu się tego nauczyć! Kiedy następne wpisy o “możliwościach” języków?! :)

  16. Avatar Jarosław Zabiełło powiedział 2 days later:

    @Jiimma: mnie te prywatne pola w ogóle nie przeszkadzają. Chciałem tylko pokazać że nie są one aż tak prywatne jak co niektórzy myślą.

    Moim zdaniem kod dynamicznie typowany tworzy się trochę szybciej i nie można powiedzieć że nie nadaje się o dużych systemów (vide Smalltalk, Erlang) Statyczne typowanie jakie jest w C, C++ czy Javie jest w moim odczuciu upierdliwe. Bez sensu są te wszystkie deklaracje tam, gdzie kompilator powinien sam wiedzieć z jakim typem ma do czynienia. Scala i Haskell mają na tyle dobrą inferencję typów że prawie się nie czuje że to są języki statycznie typowane. Też nie odczuwa się, że jakoś trzeba się dużo więcej naklepać kodu.

    Ale z drugiej strony rozumiem trochę opozycję wewnątrz środowiska Javy aby wprowadzać jakieś znaczące nowości do języka (np. closures odrzucone bodajże z propozycji dodania dla Javy 7) Może niech Java zostanie sobie już taka toporna jaka jest, pełniąc funkcję assemblera do JVM. Natomiast do normalnej pracy z platformą JVM wygodniej wybrać Scalę. A jak komuś nie pasuje, to wciąż jest ok. 249 języków do wyboru (wielokrotnie więcej niż to co reklamuje Microsoft dla swojego .NET + Java jest multiplatformowa).

    Co do metaprogramowania, to też się zgodzę że w Ruby wygląda to dużo ładniej niż w Pythonie. Tak w ogóle, to Python 3.0 mnie rozczarował. Prawie nic nie wnosi w stosunku do wersji 2.x. Nie tylko taki JavaScript ale nawet PHP (w wersji 5.3) ma już funkcje anonimowe lepsze od Pythona.

    A Scala mi się podoba bo łączy w sobie najlepsze cechy jakie można by oczekiwać od nowoczesnego języka programowania. Ta teza może zasługuje na jakiś cykl w blogu… ;)

  17. Avatar dmilith powiedział 3 days later:

    Popieram pomysł na wpisy porównawcze języków programowania. Czekam na podobne wpisy i pewnie nie tylko ja

  18. Avatar riklaunim powiedział 8 days later:

    Czy mi się wydaje czy to co staje się zbyt popularne to na tym blogu zaczyna zbierać złe opinie… a nowości, o których mało kto słyszał stają się przełomowe i w ogóle idealne ? :)

  19. Avatar radom powiedział 9 days later:

    Pomysł dobry.

  20. Avatar Hubert Łępicki powiedział 20 days later:

    Ojoj, ja się przyłącze do głosów mówiących że private/protected nigdy nie miały być mechanizmem zapewniającym bezpieczeństwo. W C++ to jest również do obejścia.

    Jest to pewien mechanizm ułatwiający projektowanie systemów i interfejsów ale nie jest niczym więcej.

  21. Avatar bigZbig powiedział about 1 month later:

    Zgadzam się z przedmówcami, że temu wpisowi niepotrzebnie nadano posmak taniej sensacji. Moim zdaniem zawarte tu informacje o wiele lepiej zostały by odebrane gdyby artykuł traktował o tym jak w razie potrzeby (np. w trakcie debugowania) dobrać się do zmiennych prywatnych i chronionych.

  22. Avatar Nowaker powiedział 2 months later:

    @bigZbig, autor niestety dobrał całkowicie głupi tytuł wpisu, więc wystawił się na krytykę.

  23. Avatar wstyd! powiedział 4 months later:

    Jak zostawisz otwarte drzwi w domu to tęż będziesz się dziwił, ze można przez nie przejść?! Brakuje Ci podstaw!

  24. Avatar Jarosław Zabiełło powiedział 4 months later:

    Głupiś i tyle. Java ma nad tym kontrolę. Ale w wypadku PHP to jest hack przed którym nie ma obrony. Chodzi o porównanie podejścia w różnych językach. Nie podchodź aż tak literalnie do tytułu. Wyluzuj gościu.

  25. Avatar Jiima powiedział 4 months later:

    @JZ “Głupiś” to kiepski argument. A wiadomo też, że private to żadne “zabezpieczenie”, a raczej odrobina wygody i nic więcej. We wszystkich znanych mi językach da się “włamać” na prywatne property, za wyjątkiem pokrętnych implementacji opartych o closure (np. w JS czy Perlu). Nawet w C++, choć rozwiązanie jest zależne od platformy (wymaga znajomości layoutu “obiektu” który jest de facto podrasowaną strukturą, w pamięci)

  26. Avatar Jarosław Zabiełło powiedział 4 months later:

    Jiima: ja to wiem, ale często można spotkać zarzut tego typu (głownie w stosunku do Pythona) sugerujący że jak język nie ma jawnego wsparcia dla metod prywatnych i protected to jest jakaś jego duża wada.

  27. Avatar helpi powiedział 7 months later:

    Mnie się nawet podobają te podkreślniki w pythonie, jeśli widzę wywołania gdzieś w kodzie, nie muszę patrzeć na ich definicję, bo już wiem z czym mam do czynienia. Ogólnie, to kwestia lubię/nie lubię i nie ma co się kłócić jakie podejście jest lepsze.

    Szczerze to te “nowoczesne” języki polecane na tym blogu jakoś mi nie podchodzą. Jeśli siedzę 8h dziennie przy kodzie (swoim lub kogoś) to najbardziej cenię jego przejrzystość i łatwość analizy. Jak na razie, przynajmniej dla mnie wygrywa Python. Oczywiście są głosy, że trójka jest “nienowoczesna”, ale przecież głównym założeniem było oczyścić kod, wywalić to co nie potrzebne, a nie rewolucja. Bardziej liczę na rozwój PyPy czy Unladen Swallow.

    A co jest nie tak z funkacjami anonimowymi? Funkcje zagnieżdżone, czy jednolinijkowe lambdy są be ?

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz