Czy metody prywatne są naprawdę bezpieczne?

92

W Javie privatemodyfikator dostępu uważa się za bezpieczny, ponieważ nie jest widoczny poza klasą. Wtedy świat zewnętrzny również nie wie o tej metodzie.

Ale pomyślałem, że odbicie w Javie może użyć do złamania tej reguły. Rozważ następujący przypadek:

public class ProtectedPrivacy{

  private String getInfo(){
     return "confidential"; 
  }

}  

Teraz z innej klasy otrzymam informacje:

public class BreakPrivacy{

   public static void main(String[] args) throws Exception {
       ProtectedPrivacy protectedPrivacy = new ProtectedPrivacy();
       Method method = protectedPrivacy.getClass().getDeclaredMethod("getInfo", null);
       method.setAccessible(true);
       Object result = method.invoke(protectedPrivacy);
       System.out.println(result.toString());
   }
} 

W tej chwili uważałem, że nadal prywatna metoda jest bezpieczna, ponieważ aby zrobić coś takiego jak powyżej, musimy znać nazwę metody. Ale jeśli klasa zawiera metody prywatne napisane przez kogoś innego, nie mamy ich widoczności.

Ale mój punkt stał się nieważny, ponieważ poniższy wiersz kodu.

Method method[] = new ProtectedPrivacy().getClass().getDeclaredMethods();

Teraz method[]zawiera wszystkie rzeczy, które należy zrobić powyżej. Moje pytanie brzmi: czy istnieje sposób na uniknięcie tego rodzaju działań przy użyciu odbicia w języku Java?

Cytuję pewien punkt z dokumentacji Java, aby wyjaśnić moje pytanie.

Wskazówki dotyczące wyboru poziomu dostępu:

Jeśli inni programiści używają Twojej klasy, chcesz mieć pewność, że błędy wynikające z niewłaściwego użycia nie wystąpią. Poziomy dostępu mogą ci w tym pomóc. Użyj najbardziej restrykcyjnego poziomu dostępu, który ma sens dla konkretnego członka. Użyj prywatnego, chyba że masz dobry powód, aby tego nie robić.

Ruchira Gayan Ranaweera
źródło
1
Korzystanie z obfuskatora może pomóc, ponieważ getDeclaredMethodszwróci nazwy, które wyglądają jak śmieci.
Sergey Kalinichenko
213
Prywatne, chronione, publiczne itp. Nie służą bezpieczeństwu, lecz zapobieganiu popełnianiu błędów przez ludzi o dobrych intencjach. Nie używaj go jako środka bezpieczeństwa
Richard Tingle
20
Kod Java i tak można dekompilować. „Atakujący” może po prostu pobrać dekompilator Javy i odczytać Twój kod lub zmienić „prywatny” na „publiczny”.
11684
7
Aby wyjaśnić, co skomentował @RichardTingle (+1 btw). Istnieje wielka i ważna różnica między ochroną a bezpieczeństwem. Gdzie ten ostatni problem jest łagodzony za pomocą modyfikatorów dostępu; aby nie zepsuć rzeczy przez przypadek
użytkownik
3
Wszystko to jest dość jasne dla programistów, w których nie istnieją modyfikatory dostępu, jak w Pythonie. W tych językach prywatne / publiczne są po prostu rozróżniane przez konwencję w nazwach (w Pythonie wszystko, co zaczyna się od pojedynczego podkreślenia, powinno być traktowane jako prywatne). To sprawia, że ​​jest całkiem jasne, że oznaczenie czegoś jako „prywatnego” nie dodaje żadnych zabezpieczeń, ale jest to tylko prosty sposób na powiedzenie, co ludzie powinni używać z twojego API. Mam wątpliwości, że sprawdzenie kompilatora pod kątem tych rzeczy w ogóle pomaga, ponieważ cholernie łatwo jest przestrzegać konwencji bez „zewnętrznej” pomocy.
Bakuriu

Odpowiedzi:

95

Zależy to od tego, co masz na myśli mówiąc „bezpieczny”. Jeśli pracujesz z menedżerem ds. Bezpieczeństwa, który na to pozwala, to tak, możesz robić różne nieprzyjemne rzeczy z refleksją. Ale w takim środowisku bibliotekę można prawdopodobnie po prostu zmodyfikować, aby mimo to upublicznić metodę.

Kontrola dostępu jest faktycznie „doradcza” w takim środowisku - skutecznie ufasz, że kod będzie dobrze działał. Jeśli nie ufasz uruchomionemu kodowi, powinieneś użyć bardziej restrykcyjnego menedżera bezpieczeństwa.

Jon Skeet
źródło
3
Wiem, że byłby styczny, ale czy mógłbyś trochę wyjaśnić, co w tym sensie byłby bardziej restrykcyjny menedżer?
Gray
12
@Gray: Instancja SecurityManager, która w check*zasadzie generuje wyjątki w odpowiednich wywołaniach.
Jon Skeet
40

Modyfikatory dostępu nie mają nic wspólnego z bezpieczeństwem. W rzeczywistości możesz i powinieneś traktować modyfikatory dostępu jako odwrotność bezpieczeństwa - nie ma to na celu ochrony Twoich danych ani algorytmów, ale ma chronić ludzi przed wymogiem posiadania wiedzy o Twoich danych i algorytmach. Dlatego domyślnym modyfikatorem jest pakiet - jeśli pracują nad pakietem, prawdopodobnie już muszą wiedzieć.

Wraz ze znajomością danych i metodami Twojego kodu przychodzi odpowiedzialność za to, aby wiedzieć, kiedy i jak go używać. Nie ustawiasz prywatnego na swojej metodzie inIt, aby ktoś nie dowiedział się o tym, robisz to, ponieważ (a) nie będą wiedzieć, że wywołasz to tylko po foo i tylko jeśli bar = 3.1415 i (b) ponieważ wiedza o tym nie pomoże im.

Modyfikatory dostępu można podsumować prostym zwrotem „TMI, stary, nie musiałem tego wiedzieć”.

jmoreno
źródło
3
Świetna odpowiedź, ale musiałem wygooglować, aby dowiedzieć się, że TMI oznacza zbyt dużo informacji. Chyba muszę spędzić więcej czasu w dolinie :-)
mikelong
7

Mówiąc „bezpieczny”, chronisz siebie lub innych programistów, którzy używają twojego API, aby nie uszkodzić obiektu, wywołując twoją prywatną metodę. Ale jeśli Ty lub oni naprawdę musicie wywołać tę metodę, mogą to zrobić za pomocą Reflection.

Arsen Alexanyan
źródło
6

Pytanie brzmi, od kogo próbujesz go uratować . Moim zdaniem taki klient twojego kodu jest tutaj stracony.

Każdy fragment kodu (napisany przez Ciebie lub inne osoby), który próbuje uzyskać dostęp do privateczłonka powyższej klasy, jest w zasadzie kopaniem własnego grobu. privateczłonkowie nie stanowią części publicznego interfejsu API i mogą ulec zmianie bez powiadomienia. Jeśli zdarzy się, że klient zużyje jednego z takich prywatnych członków w sposób podany powyżej, zepsuje się, jeśli zaktualizuje do nowszej wersji API, w którym prywatny członek został zmodyfikowany.


źródło
5

Łatwość wiąże się z odpowiedzialnością. Są rzeczy, których nie możesz zrobić i rzeczy, które możesz zrobić, ale nie powinieneś .

Prywatny modyfikator jest dostarczany / używany jako / w najbardziej ograniczony sposób. Elementy członkowskie, które nie powinny być widoczne poza klasą, należy zdefiniować jako prywatne. Ale, jak widzimy, można to złamać za pomocą Refleksji. Ale to nie znaczy, że nie powinieneś używać prywatnych - lub są one niebezpieczne. Chodzi o to, abyś używał rzeczy rozsądnie lub w konstruktywny sposób (jak refleksja).

Raúl
źródło
5

Zakładając, że ufasz programistom klienta twojego API, innym spojrzeniem jest to, jak „bezpieczne” jest korzystanie z tych konkretnych funkcji.

Twoje publicznie dostępne funkcje powinny zapewniać przejrzysty, dobrze udokumentowany, rzadko zmieniający się interfejs w kodzie. Twoje prywatne funkcje mogą być traktowane jako szczegół implementacji i mogą się zmieniać w czasie, więc nie są bezpieczne w użyciu bezpośrednio.

Jeśli programista kliencki robi wszystko, co w jego mocy, aby ominąć te abstrakcje, w pewien sposób deklaruje, że wie, co robi. Co ważniejsze, rozumieją, że nie jest on obsługiwany i może przestać działać z przyszłymi wersjami Twojego kodu.

robbie_c
źródło
5

privatenie służy bezpieczeństwu, lecz zachowaniu czystości kodu i zapobieganiu błędom. Pozwala użytkownikom na modularyzację kodu (i sposobu jego tworzenia) bez martwienia się o wszystkie szczegóły innych modułów

Po wydaniu kodu ludzie mogą dowiedzieć się, jak to działa. Nie ma sposobu, aby „ukryć” logikę, jeśli ostatecznie chcesz, aby kod działał na komputerze. Nawet kompilacja do formatu binarnego to tylko poziom zaciemnienia.

Nie ma więc możliwości skonfigurowania interfejsu API do robienia specjalnych rzeczy, których nie chcesz, aby inni mogli wywoływać. W przypadku internetowego interfejsu API metody, nad którymi chcesz mieć kontrolę, można umieścić po stronie serwera.

Manishearth
źródło