Jak łatwo włamać się do JavaScript (w przeglądarce)?

37

Moje pytanie dotyczy bezpieczeństwa JavaScript.

Wyobraź sobie system uwierzytelniania, w którym używasz środowiska JavaScript, takiego jak Backbone lub AngularJS , i potrzebujesz bezpiecznych punktów końcowych. To nie jest problem, ponieważ serwer zawsze ma ostatnie słowo i sprawdzi, czy masz uprawnienia do robienia tego, co chcesz.

Ale co, jeśli potrzebujesz trochę bezpieczeństwa bez angażowania serwera? Czy to jest możliwe?

Załóżmy na przykład, że masz system routingu po stronie klienta i chcesz chronić konkretną trasę dla zalogowanych użytkowników. Pingujesz więc serwer, pytając, czy możesz odwiedzić chronione trasy i kontynuujesz. Problem polega na tym, że podczas pingowania na serwer zapisujesz odpowiedź w zmiennej, więc przy następnym przejściu na prywatną trasę sprawdzi, czy jesteś już zalogowany (brak pingowania na serwer) i zależnie po odpowiedzi pójdzie, czy nie.

Jak łatwo jest zmodyfikować tę zmienną i uzyskać dostęp?

Moja wiedza na temat bezpieczeństwa (i JavaScript) nie jest dobra. Ale jeśli zmienna nie ma zasięgu globalnego i znajduje się w prywatnej części wzorca modułu, który ma tylko metody pobierające, ale nie ustawiające, nawet w takim przypadku, czy możesz to zhakować?

Jesus Rodriguez
źródło
29
Wszystkie te długie odpowiedzi. W skrócie, aby odpowiedzieć na nagłówek, „bardzo możliwe do zhakowania”. Aby odpowiedzieć na pierwsze dwa pytania w jednej linii: „bez serwera utraciłeś bezpieczeństwo” i& „Nie”. Aby odpowiedzieć na pytanie trzecie, „Bardzo łatwe”, a na koniec „Tak, z łatwością”. Proszę bardzo. Odpowiedzi na wszystkie pytania. : P
SpYk3HH
Doskonały przykład, zobacz mój post na blogu o dodawaniu jQuery do dowolnej strony internetowej. spyk3lc.blogspot.com/2013/05/… Teraz, młody padawan, idź i spróbuj. Zobacz, jak łatwo jest dodać jQuery do dowolnej witryny, która jeszcze go nie ma, a następnie użyj jquery, aby bardzo łatwo manipulować dowolną częścią widoku bez długich linii javascript. Bum!
SpYk3HH,
7
Kiedyś, podczas rejestracji nazwy domeny, numer telefonu był polem wymaganym, ale był wymuszony tylko w javascript - nie po stronie serwera. Tak więc wyłączyłem go, zmieniając definicję funkcji (używając Firebug) i voilà! Nie mają mojego numeru telefonu.
Izkata
8
manipulate any part of the sight without long lines strona vs widok
mplungjan
8
Profesjonalni programiści internetowi muszą to naprawić. Proszę. To wstyd więcej niż gramatyka nazistowska rzecz :) plus.google.com/u/0/+MonaNomura/posts/h9ywDhfEYxT
mplungjan

Odpowiedzi:

26

Proszę przeczytać odpowiedź Joachima przed przeczytaniem tego. Omawia ogólne przyczyny luki w zabezpieczeniach po stronie klienta. A teraz sugestia, jak możesz obejść ten problem ...

Bezpieczny schemat komunikacji klient-serwer bez konieczności ręcznego uwierzytelniania na serwerze na każde żądanie:

Jesteś nadal pozwalając serwer mieć ostatnie słowo, a serwer nadal musi potwierdzić wszystko, co klient mówi, ale zdarza się przejrzysty.

Załóżmy protokół HTTPS , aby zapobiec atakom typu man-in-the-middle (MITMA).

  • Klient po raz pierwszy uzgadnia z serwerem, a serwer generuje klucz publiczny dla klienta i zachowuje klucz prywatny za pomocą asymetrycznego schematu szyfrowania. Klient przechowuje „publiczny” klucz serwera w lokalnej pamięci, zaszyfrowany bezpiecznym hasłem, którego nigdzie nie zapisujesz.

  • Klient jest teraz offline. Klient chce wykonywać zaufane działania. Klient wprowadza swoje hasło i pobiera klucz publiczny serwera.

  • Klient wykonuje teraz działania na podstawie swojej wiedzy o tych danych, a następnie szyfruje każdą akcję, którą wykonuje za pomocą klucza publicznego serwera dla tego klienta .

  • Gdy klient jest w trybie online, wysyła swój identyfikator klienta, a wszystkie wykonywane przez niego działania są wysyłane na serwer zaszyfrowany kluczem publicznym serwera.

  • Serwer odszyfrowuje akcje, a jeśli są w prawidłowym formacie, ufa, że ​​pochodzą od klienta.

Uwaga:

  • Nigdzie nie możesz przechowywać hasła klienta, w przeciwnym razie osoba atakująca byłaby w stanie pobrać klucz i podpisać akcje jako własne. Bezpieczeństwo tego schematu zależy wyłącznie od integralności klucza generowanego przez serwer dla klienta. Klient nadal będzie musiał zostać uwierzytelniony na serwerze, gdy poprosi o ten klucz.

  • W rzeczywistości nadal polegasz na serwerze, a nie na kliencie. Każdą czynność wykonywaną przez klienta należy sprawdzić na serwerze.

  • Możliwe jest uruchamianie zewnętrznych skryptów w robotach sieciowych . Pamiętaj, że każde Twoje żądanie JSONP jest teraz znacznie większym problemem bezpieczeństwa. Musisz chronić klucz za wszelką cenę. Po utracie atakujący może podszyć się pod użytkownika.

  • Spełnia to Twoje żądanie, aby „nie pingować na serwer”. Atakujący nie może po prostu naśladować żądania HTTP z fałszywymi danymi, jeśli nie zna klucza.

  • Odpowiedź Joachima jest nadal poprawna . Nadal wykonujesz wszystkie uwierzytelnienia na serwerze . Jedyne, co tutaj zapisałeś, to konieczność każdorazowego sprawdzania hasła na serwerze. Teraz musisz zaangażować serwer tylko wtedy, gdy chcesz zatwierdzić lub pobrać zaktualizowane dane. Wszystko, co tutaj zrobiliśmy, to zapisanie zaufanego klucza po stronie klienta i poproszenie go o ponowne sprawdzenie.

  • Jest to dość powszechny schemat dla aplikacji jednostronicowych (na przykład w AngularJS).

  • Nazywam klucz publiczny serwera „publicznym” ze względu na to, co to oznacza w programach takich jak RSA , ale w rzeczywistości jest to poufna informacja w systemie i powinna być chroniona.

  • Nigdzie nie zachowałbym hasła w pamięci. Zmusiłbym użytkownika do przesyłania swojego hasła offline za każdym razem, gdy zaczyna on uruchamiać kod offline.

  • Nie rzucaj własną kryptografią - do uwierzytelniania używaj znanej biblioteki, takiej jak Stanford.

  • Weź tę radę taką, jaka jest . Przed uruchomieniem tego rodzaju uwierzytelniania w aplikacji o znaczeniu krytycznym dla biznesu skonsultuj się z ekspertem ds . Bezpieczeństwa . Jest to poważny problem, który jest zarówno bolesny, jak i łatwy do popełnienia błędu.

Bardzo ważne jest, aby żadne inne skrypty nie miały dostępu do strony. Oznacza to, że zezwalasz tylko zewnętrznym skryptom na pracę w sieci. Nie możesz ufać żadnym innym zewnętrznym skryptom, które mogłyby przechwycić twoje hasło, gdy użytkownik je wprowadzi.

Użyj pola hasła, prompta nie wbudowanego, jeśli nie masz całkowitej pewności i nie odkładaj jego wykonania (to znaczy, nie powinno dojść do punktu, w którym zdarzenia mają do niego dostęp, ale tylko w kodzie synchronizacji). I tak naprawdę nie przechowuj hasła w zmiennej - znowu działa to tylko wtedy, gdy ufasz, że komputer użytkownika nie zostanie naruszony (chociaż to wszystko dotyczy również sprawdzania poprawności na serwerze).

Chciałbym jeszcze raz dodać, że nadal nie ufamy klientowi . Nie możesz ufać samemu klientowi i myślę, że odpowiedź Joachima to ugruntowała. Zyskaliśmy jedynie wygodę, że nie musimy pingować serwera przed rozpoczęciem pracy.

Powiązany materiał:

Benjamin Gruenbaum
źródło
3
„Nie rzucaj własnym krypto” dotyczy tak samo protokołów, jak i prymitywów, jeśli nie bardziej, ponieważ ludzie zwykle nie są wystarczająco głupi, aby tworzyć własne prymitywy, myślą, że mogą stworzyć protokół, który jest w porządku . Nie rozumiem też, dlaczego RSA jest tutaj potrzebne. Skoro używasz HTTPS, dlaczego nie wygenerować 16-32 bajtowego wspólnego hasła i wymagać, aby był wysyłany wraz z przyszłymi żądaniami? Oczywiście, jeśli to zrobisz, koniecznie użyj niezmiennego w czasie sprawdzania równości w łańcuchu ...
Reid
2
+1 @ Ree Tak, właśnie rozmawiałem na ten temat z pewnym ekspertem na ten temat. Z grubsza oparłem tutaj prymitywny schemat na protokole, o którym się dowiedziałem (przez Rabina IIRC), ale przynajmniej dopóki nie znajdę szczegółów (i uzasadnię na przykład, dlaczego w tym przypadku potrzebne jest szyfrowanie asymetryczne). Nie używaj schematu sugerowanego w tej odpowiedzi bez uprzedniej konsultacji z ekspertem ds . Bezpieczeństwa . Widziałem takie podejście stosowane w kilku miejscach, ale w praktyce oznacza to niewiele, jeśli w ogóle. Zredagowałem również odpowiedź, aby to poprawić.
Benjamin Gruenbaum,
Korzystanie z monitu nie zapewnia większego bezpieczeństwa. Osoba atakująca może zastąpić window.promptmetodę w celu przechwycenia frazy hasła lub uruchomienia własnego monitu (być może w ramach elementu iframe!). Pole hasła ma jeszcze jedną zaletę: znaki nie są wyświetlane.
Rob W
@RobW Oczywiście cały ten schemat jest bezwartościowy, jeśli strona została naruszona. Jeśli atakujący ma dostęp do uruchomienia JavaScript na stronie, mamy problemy. Pomysł polegał na tym, że blokuje, ale masz rację, wszelkie korzyści związane z bezpieczeństwem są wątpliwe. Zobacz ostatni link na liście.
Benjamin Gruenbaum
1
Po pierwsze. Świetna odpowiedź, myślę, że nie mogę prosić o nic innego. Z drugiej strony chciałem robić projekty dla zwierząt domowych dla mnie (i oczywiście dla tego, kto chce z niego skorzystać) i może to jest przesada (a może nie). Chodzi mi o to, że nie chcę robić nic poważnego i obawiam się, że nie mogę rzucić takiego auta, które wyjaśniłeś, braku wiedzy. Myślę, że rzuciłbym prosty czek 401 lub jeśli nie mam żadnego żądania, po prostu pinguję serwer za każdym razem, gdy uzyskuję dostęp do tego rodzaju tras. Token uwierzytelniania jest również opcją (i być może najbliższą rzeczą związaną z tym, co tu wyjaśniłeś AFAIK). Dziękuję :)
Jesus Rodriguez
89

To proste: każdy mechanizm bezpieczeństwa, który polega na tym, że klient robi tylko to, co mu każesz, może zostać naruszony, gdy osoba atakująca ma kontrolę nad klientem.

Państwo może mieć kontrole bezpieczeństwa na kliencie, ale tylko skutecznie działać jako „cache” (w celu uniknięcia podejmowania kosztownych round-trip do serwera, jeśli klient już wie, że odpowiedź będzie „nie”).

Jeśli chcesz zachować informacje od zestawu użytkowników, upewnij się, że klient tych użytkowników nigdy nie dostanie się do tych informacji. Jeśli wyślesz te „tajne dane” wraz z instrukcjami ”, ale nie wyświetlaj ich”, wyłączenie kodu sprawdzającego to żądanie stanie się trywialne.

Jak widać, ta odpowiedź tak naprawdę nie wspomina o żadnych szczegółach dotyczących JavaScript / przeglądarki. To dlatego, że ta koncepcja jest taka sama, bez względu na to, jaki jest twój klient. Tak naprawdę nie ma znaczenia, że ​​jest to gruby klient (tradycyjna aplikacja klient / serwer), oldschoolowa aplikacja internetowa lub aplikacja jednostronicowa z rozbudowanym skryptem JavaScript po stronie klienta.

Gdy Twoje dane opuszczą serwer, musisz założyć, że atakujący ma do nich pełny dostęp.

Joachim Sauer
źródło
Dziękuję Ci. Byłoby wspaniale, gdybyś mógł to trochę wyjaśnić, moja wiedza na temat bezpieczeństwa nie jest tak dobra, a jedyne, co rozumiem, to to, że się mylę: P. Kiedy mówisz o buforowaniu, to tylko do buforowania, gdy serwer mówi nie? Mam na myśli to, że jeśli serwer powiedział kiedyś „tak”, nie możesz tego buforować, prawda?
Jesus Rodriguez
3
Ja bym tylko dodać, że jest to możliwe do zmiennych zamykających dostęp w przeglądarce (jak w schemacie modułu swój wymienionym), zwłaszcza z debuggera :)
Benjamin Gruenbaum
2
@BenjaminGruenbaum: zachęcamy do rozwinięcia tego w odpowiedź, która skupia się na JS. Przedstawiłem tu tylko ogólny przegląd niezależny od technologii. Przydałaby się również odpowiedź dotycząca JS!
Joachim Sauer
1
Krótko mówiąc, jeśli niektóre informacje / trasy / cokolwiek jest dostępne w zależności od zmiennej javascript, to i tak nie będzie bezpieczne, ponieważ możesz łatwo zmienić tę zmienną, aby powiedzieć, co potrzebujesz uzyskać dostęp do tych prywatnych rzeczy.
Jesus Rodriguez
4
@ JoachimSauer Myślę, że nie trafiłbym w sedno tego pytania, które ładnie przybiliście. Bez względu na to, jakie środki bezpieczeństwa podejmuje klient, możliwe jest pogorszenie komunikacji . Możesz tworzyć bardzo solidne systemy uwierzytelniania w JavaScript, ale to nic nie jest warte, gdy tylko wstawisz komunikację do serwera, który inni klienci traktują również jako źródło prawdy. Kod źródłowy jest wysyłany po stronie klienta, inteligentny atakujący może go po prostu odczytać i naśladować żądanie HTTP do serwera. Należy traktować wszystko, co pochodzi od klienta, jako niezaufane, chyba że zostanie ono zweryfikowane na serwerze.
Benjamin Gruenbaum,
10

Społeczność graczy mówi: „ Klient jest w rękach wroga". Każdy kod działający poza bezpiecznym obszarem, takim jak serwer, jest podatny na atak. W najbardziej podstawowym scenariuszu jest podatny na to, że nie zostanie uruchomiony w pierwszej kolejności. To klient decyduje o uruchomieniu" kodu bezpieczeństwa ", a użytkownik może po prostu „zrezygnuj”. Podczas gdy w natywnym kodzie masz co najmniej automatyczne zaciemnianie w asemblerze i dodatkową warstwę ochrony przez fakt, że atakujący musi być dobrym programistą, aby nim manipulować, JS jest zwykle nieobsługiwany i jako zwykły tekst. Wszystko, czego potrzebujesz, aby przeprowadzić atak, to prymitywne narzędzia, takie jak serwer proxy i edytor tekstu. Atakujący będzie nadal potrzebował pewnego poziomu wiedzy na temat programowania, ale o wiele łatwiej jest zmodyfikować skrypt za pomocą dowolnego edytora tekstu niż wstrzyknąć kod do pliku wykonywalnego.

nvoigt
źródło
Zgromadzenie nie jest zaciemnieniem, a kto wierzy inaczej, nigdy nie grał w
grę wojenną
@miniBill: Chociaż średnio doświadczony atakujący nie będzie miał problemów ze złożonym kodem, zapewni bardzo prosty filtr górnoprzepustowy. (Niestety, jest to akademickie, ponieważ wyeliminowanie dzieciaków ze skryptów zapewnia minimalny wzrost bezpieczeństwa dla scenariusza PO)
Piskvor
1

To nie jest kwestia włamania się do JavaScript. Gdybym chciał zaatakować twoją aplikację, użyłbym prywatnego serwera proxy, który pozwoliłby mi przechwytywać, modyfikować i odtwarzać ruch. Wygląda na to, że proponowany system zabezpieczeń nie obejmuje żadnych zabezpieczeń.

Mark E. Haase
źródło
0

Mówiąc konkretnie o Angular:

Ochrona trasy po stronie klienta po prostu nie istnieje. Nawet jeśli „ukryjesz” przycisk do tej trasy, użytkownik zawsze może go wpisać, Angular narzeka, ale klient może to zakodować.

W pewnym momencie administrator będzie musiał poprosić serwer o dane potrzebne do renderowania widoku, jeśli użytkownik nie jest upoważniony do dostępu do tych danych, nie otrzyma ich, ponieważ chroniłeś te dane po stronie serwera , a Twoja aplikacja Angular powinna odpowiednio obsługiwać 401.

Jeśli próbujesz chronić nieprzetworzone dane, nie możesz po stronie klienta, jeśli chcesz, aby konkretny użytkownik mógł tylko przeglądać wspólne dane w określony sposób, utwórz „widoki” danych na serwerze zamiast wysyłać nieprzetworzone dane dla klienta i ich reorganizacja (powinieneś już to zrobić ze względu na wydajność).

Uwaga dodatkowa: nigdy nie powinno być żadnych wrażliwych elementów wbudowanych w szablony widoku, których zażąda Angular, po prostu nie rób tego. Jeśli z jakiegoś szalonego powodu istnieje, musisz chronić te szablony widoków po stronie serwera, tak jak gdybyś robił renderowanie po stronie serwera.

znak
źródło