Pytanie Dlaczego char (preferowany) nad String dla haseł?


W Swingu pole hasła ma getPassword() (zwraca char[]) metoda zamiast zwykłej getText() (zwraca String) metoda. Podobnie, natknąłem się na sugestię, której nie wolno używać String do obsługi haseł.

Dlaczego String stwarzają zagrożenie dla bezpieczeństwa, jeśli chodzi o hasła? Jest niewygodne w użyciu char[].


2901
2018-01-16 14:20


pochodzenie


Czy możesz podać źródło sugestii? Nie mogę wymyślić powodu char[] być bardziej bezpiecznym, z wyjątkiem być może najbardziej amatorskich zagrożeń. - Viruzzo
@Viruzzo spójrz na javadocs. The getText() metoda JPasswordField jest przestarzałe na rzecz getPassword() "ze względów bezpieczeństwa". - dogbane
zwęglać[]? Dlaczego nie bajt []? W każdym analizatorze zrzutu / sterty można zobaczyć ciągi znaków i łatwo zauważyć, że jeden zawiera hasło. Dla char [] jest to trudniejsze, ale nadal, jeśli twój analizator sterty będzie wyświetlał znaki jeden po drugim, będzie taki sam jak String. Dla bajtu [] jest mało prawdopodobne. - mvmn
Aby zapewnić prawidłowy scenariusz, w którym może to mieć znaczenie, powszechną praktyką w kryminalistyce jest zapewnienie, że żadne komputery wymagające dochodzenia nie zostaną wyłączone przed zapakowaniem ich pamięci, na wypadek gdyby korzystały one z szyfrowania oprogramowania. Nawet najlepszy system szyfrowania musi utrzymywać stan zdolny do odszyfrowania. - Zenexer
@ scottlafoy Obie char[] i String obiekty będą przekazywane przez odniesienie; indywidualny char wartości nie będą. Dzieje się tak, gdy przedmioty mijają wartość, a nie odniesienia, że ​​tworzone są wielokrotne kopie. Zasadniczo nie stanowi to problemu, ponieważ takie obiekty są zwykle zaprojektowane jako małe. - Zenexer


Odpowiedzi:


Ciągi są niezmienne. Oznacza to, że po utworzeniu String, jeśli inny proces może zrzucić pamięć, nie ma sposobu (oprócz odbicie) możesz wcześniej pozbyć się danych zbieranie śmieci kopie w.

Dzięki macierzy możesz jawnie wyczyścić dane, gdy skończysz. Możesz nadpisać tablicę dowolnymi przedmiotami, a hasło nie będzie obecne w systemie, nawet przed wyrzuceniem.

Więc tak, to jest obawy związane z bezpieczeństwem - ale nawet używanie char[] ogranicza tylko szansę atakującemu i dotyczy tylko tego konkretnego typu ataku.

Jak zauważono w komentarzach, jest możliwe, że tablice przenoszone przez garbage collector pozostawiają w pamięci zabłąkane kopie danych. Uważam, że jest to specyficzne dla realizacji - śmieciarz może wyczyść całą pamięć, aby uniknąć tego rodzaju rzeczy. Nawet jeśli tak jest, wciąż jest czas, w którym char[] zawiera rzeczywiste znaki jako okno ataku.


3720
2018-01-16 14:26



Rozumiem, że przy reorganizacji pamięci, która może zostać wykonana za pomocą środowiska wykonawczego, kopie nawet char[] może pozostać w pamięci i nigdy nie zostać wyczyszczonym, dopóki ta pamięć nie zostanie ponownie użyta. Nie znam źródła, które to potwierdza. W każdym razie wątpię, aby String został wyczyszczony w czasie GC, ale raczej w czasie, gdy Obiekt jest ponownie przydzielany do tej pamięci. - Mark Peters
@ Mark Peters: Jeśli GC kopie, zanim opróżnisz tablicę znaków, otrzymasz kolejną kopię. Ale ta kopia jest co najmniej w ciężkim, używanym obszarze pamięci, więc szanse są wysokie, gdy zostanie szybko nadpisana. - Mnementh
@ Xeon06: W .NET jest SecureString co jest jeszcze lepsze - ale stosunkowo bolesne w użyciu. - Jon Skeet
Czy mówimy o tym, że złośliwy podmiot, który może odczytać te dane, już odczytał dostęp do pamięci Twojego systemu? Wydaje się, że byłoby to o wiele bardziej niepokojące, że zrobienie tego char [] zamiast String wydaje się po prostu pissingiem w oceanie. - corsiKa
@corsika: Jak wspomniałem w odpowiedzi, redukuje to jeden konkretny wektor ataku, to wszystko. Utrudnia to atakującemu. - Jon Skeet


Podczas gdy inne sugestie wydają się poprawne, jest jeszcze jeden dobry powód. Z gładką String masz znacznie większe szanse przypadkowe wydrukowanie hasła do logów, monitory lub inne niezabezpieczone miejsce. char[] jest mniej wrażliwy.

Rozważ to:

public static void main(String[] args) {
    Object pw = "Password";
    System.out.println("String: " + pw);

    pw = "Password".toCharArray();
    System.out.println("Array: " + pw);
}

Wydruki:

String: Password
Array: [C@5829428e

1080
2018-01-16 19:37



@voo, ale wątpię, że logowałbyś się przez bezpośrednie pisanie do streamingu i konkatenacji. framework logowania przekształcił char [] na dobry wynik - bestsss
@ Thr4wn Domyślna implementacja toString jest classname@hashcode. [C reprezentuje char[]reszta to heksadecymalny kod skrótu. - Konrad Garus
Ciekawy pomysł. Chciałbym zaznaczyć, że nie jest to transpozycja do Scali, która ma sensowny ciąg do Ciągnięcia po tablice. - mauhiz
Napisałem Password typ klasy dla tego. Jest mniej niejasny i trudniej przypadkowo gdzieś przejść. - rightfold
Dlaczego ktoś miałby założyć, że tablica znaków będzie rzutowana jako obiekt? Nie jestem pewien, czy rozumiem, dlaczego każdy lubi tę odpowiedź. Załóżmy, że zrobiłeś to: System.out.println ("Hasło" .toCharArray ()); - GC_


Zacytować oficjalny dokument Przewodnik po architekturze Java Cryptography mówi o tym char[] vs. String hasła (dotyczące szyfrowania opartego na haśle, ale jest to bardziej ogólnie o hasłach):

Logiczne wydaje się zbieranie i przechowywanie hasła w obiekcie   typu java.lang.String. Jednak tutaj jest zastrzeżenie: Objects z   rodzaj String są niezmienne, tj. nie ma zdefiniowanych metod   pozwala na zmianę (nadpisanie) lub wyzerowanie zawartości pliku String   po użyciu. Ta funkcja sprawia String przedmioty nieodpowiednie dla   przechowywanie informacji wrażliwych na bezpieczeństwo, takich jak hasła użytkowników. ty   powinien zawsze gromadzić i przechowywać informacje wrażliwe na bezpieczeństwo w    char zamiast tego tablica.

Wytyczna 2-2 wytycznych bezpiecznego kodowania dla języka programowania Java, wersja 4.0 także mówi coś podobnego (chociaż jest to pierwotnie w kontekście logowania):

Wytyczna 2-2: Nie zapisuj bardzo poufnych informacji

Niektóre informacje, takie jak numery ubezpieczenia społecznego (SSN) i   hasła są bardzo wrażliwe. Ta informacja nie powinna być przechowywana   dłużej niż to konieczne, ani tam, gdzie może być widoczne, nawet przez   administratorzy. Na przykład nie należy go wysyłać do plików dziennika i   jego obecność nie powinna być wykrywalna podczas wyszukiwania. Niektóre przejściowe   dane mogą być przechowywane w zmiennych strukturach danych, takich jak tablice znaków, oraz   wyczyszczone natychmiast po użyciu. Czyszczenie struktur danych zostało zmniejszone   skuteczność w typowych systemach uruchomieniowych Java, gdy obiekty są przenoszone   pamięć przeźroczysto do programisty.

Ta wytyczna ma również implikacje dla wdrożenia i użytkowania   biblioteki niskiego poziomu, które nie mają semantycznej znajomości danych   oni mają do czynienia. Na przykład: parsowanie ciągów niskopoziomowych   biblioteka może rejestrować tekst, na którym działa. Aplikacja może analizować numer SSN   z biblioteką. To stwarza sytuację, w której znajdują się stacje SSN   dostępne dla administratorów z dostępem do plików dziennika.


610
2018-01-17 03:20



to jest właśnie błędne / fałszywe odniesienie, o którym mówię poniżej odpowiedzi Jona, jest to dobrze znane źródło z wieloma krytykami. - bestsss
@Bestass, czy możesz również zacytować odniesienie? - user961954
@bestass przepraszam, ale String jest dość dobrze zrozumiany i jak zachowuje się w JVM ... istnieją dobre powody, aby z niego skorzystać char[] zamiast String podczas postępowania z hasłami w bezpieczny sposób. - SnakeDoc
jeszcze raz, chociaż hasło jest przekazywane jako ciąg znaków z przeglądarki do żądania jako "ciąg znaków", a nie znak "char"? więc bez względu na to, co robisz, jest to ciąg znaków, w którym momencie należy go wykonać i odrzucić, nigdy nie przechowywać w pamięci? - Dawesi


Tablice znaków (char[]) można wyczyścić po użyciu, ustawiając każdy znak na zero, a ciągi nie. Jeśli ktoś może w jakiś sposób zobaczyć obraz pamięci, może zobaczyć hasło w postaci zwykłego tekstu, jeśli użyte są ciągi znaków, ale jeśli char[] jest używany, po wyczyszczeniu danych wartościami 0, hasło jest bezpieczne.


305
2018-01-16 14:27



Domyślnie nie jest bezpieczne. Jeśli mówimy o aplikacji internetowej, większość kontenerów internetowych przekaże hasło do HttpServletRequest obiekt w zwykłym tekście. Jeśli wersja JVM wynosi 1,6 lub mniej, będzie to miejsce w permgen. Jeśli jest w 1.7, będzie nadal czytelny, dopóki nie zostanie zebrany. (Kiedykolwiek to jest.) - avgvstvs
@avgvstvs: ciągi nie są automatycznie przenoszone do przestrzeni permgen, która ma zastosowanie tylko do łańcuchów internowanych. Poza tym, przestrzeń permgen jest również przedmiotem zbierania śmieci, tylko w niższym tempie. Prawdziwym problemem z permgenowską przestrzenią jest jej stały rozmiar, który jest właśnie powodem, dla którego nikt nie powinien bezmyślnie dzwonić intern() na arbitralnych ciągach. Ale masz rację, że String instancje istnieją w pierwszej kolejności (do momentu ich zebrania) i zamieniają je w char[] tablice później go nie zmieniają. - Holger
@Holger patrz docs.oracle.com/javase/specs/jvms/se6/html/...  "W przeciwnym razie tworzone jest nowe wystąpienie klasy String zawierające ciąg znaków Unicode podany w strukturze CONSTANT_String_info, ta instancja klasy jest wynikiem wywodzenia literału łańcuchowego, wreszcie wywoływana jest metoda intern nowej instancji String." W wersji 1.6 JVM wywoływałaby dla ciebie intern po wykryciu identycznych sekwencji. - avgvstvs
@Holger, masz rację, utworzyłem pule stałe i pula ciągów, ale jest także fałszywe to permgen tylko zastosowane do internowanych łańcuchów. Przed wersją 1.7 zarówno constant_pool, jak i string_pool znajdowały się w przestrzeni permgen. Oznacza to, że jedyną klasą łańcuchów, które zostały przydzielone do stosu, były: new String() lub StringBuilder.toString()  Zarządzałem aplikacjami z dużą ilością stałych łańcuchowych, w wyniku czego uzyskaliśmy dużo permenału. Do 1.7. - avgvstvs
@avgvstvs: cóż, stałe ciągów znaków są, zgodnie z wymaganiami JLS, zawsze internowane, stąd stwierdzenie, że internowane ciągi kończyły się w przestrzeni permgenu, niejawnie zastosowane do stałych łańcuchowych. Jedyna różnica polega na tym, że stałe łańcuchowe zostały stworzone w przestrzeni permgena w pierwszej kolejności, podczas gdy wywołanie intern() w dowolnym ciągu może spowodować przydzielenie równoważnego ciągu znaków w przestrzeni permen. Ten ostatni mógł uzyskać GC, jeśli nie istniałby literalny ciąg tej samej zawartości, która dzieli ten obiekt ... - Holger


Niektórzy uważają, że trzeba zastąpić pamięć używaną do przechowywania hasła, gdy już go nie potrzebujesz. Zmniejsza to okno czasowe, w którym osoba atakująca musi odczytać hasło z twojego systemu i całkowicie ignoruje fakt, że atakujący ma już wystarczający dostęp do przejęcia pamięci JVM, aby to zrobić. Osoba atakująca z takim dostępem może złapać kluczowe wydarzenia, czyniąc to całkowicie bezużytecznym (AFAIK, więc proszę poprawić mnie, jeśli się mylę).

Aktualizacja

Dzięki komentarzom muszę zaktualizować moją odpowiedź. Wydaje się, że są dwa przypadki, w których można dodać (bardzo) niewielką poprawę bezpieczeństwa, ponieważ skraca to czas, w jaki hasło może trafić na dysk twardy. Nadal uważam, że jest to przesada w większości przypadków.

  • Twój system docelowy może być źle skonfigurowany lub musisz założyć, że jest i musisz być paranoikiem na temat zrzutów głównych (może być ważny, jeśli systemy nie są zarządzane przez administratora).
  • Twoje oprogramowanie musi być zbyt paranoiczne, aby zapobiec wyciekom danych, gdy atakujący uzyska dostęp do sprzętu - używając takich rzeczy jak TrueCrypt (wycofany), VeraCrypt, lub CipherShed.

Jeśli to możliwe, wyłączenie zrzutu pamięci i pliku wymiany zajmie się obydwoma problemami. Wymagają one jednak uprawnień administratora i mogą zmniejszać funkcjonalność (mniej pamięci do użycia), a pobieranie pamięci RAM z działającego systemu nadal stanowi poważny problem.


183
2018-01-16 14:32



Zastąpiłbym "całkowicie bezużytecznym" słowem "tylko niewielka poprawa bezpieczeństwa". Na przykład możesz uzyskać dostęp do zrzutu pamięci, jeśli masz dostęp do odczytu do katalogu tmp, źle skonfigurowanej maszyny i awarii w aplikacji. W takim przypadku nie można zainstalować keyloggera, ale Ty mógłby przeanalizuj zrzut rdzenia. - Joachim Sauer
Wycieranie niezaszyfrowanych danych z pamięci, gdy tylko skończysz, jest uważane za najlepszą praktykę nie dlatego, że jest niezawodny (nie jest); ale dlatego, że zmniejsza poziom zagrożenia. Ataki w czasie rzeczywistym nie są w ten sposób powstrzymywane; ale ponieważ obsługuje narzędzie do łagodzenia skutków obrażeń, znacznie zmniejsza ilość danych, które są eksponowane podczas ataku wstecznego na migawkę pamięci (np. kopię pamięci aplikacji zapisaną w pliku wymiany lub odczytaną z pamięci z działającego serwera i przeniesiony do innego, zanim jego stan się nie powiódł). - Dan Neely
Zgadzam się z podejściem tej odpowiedzi. Zaryzykowałbym zaproponować, by większość naruszeń bezpieczeństwa następowała na znacznie wyższym poziomie abstrakcji niż bity w pamięci. Oczywiście, istnieją prawdopodobnie scenariusze w hiper-bezpiecznych systemach obronnych, w których może to stanowić poważny problem, ale poważnie myślenie na tym poziomie jest przesadą dla 99% aplikacji, w których wykorzystuje się system .NET lub Java (ponieważ wiąże się to ze zbieraniem śmieci). - kingdango
Po penetracji pamięci serwera przez Heartbleed, ujawniając hasła, zamieniłbym ciąg "tylko drobny poprawa bezpieczeństwa" na "absolutnie niezbędny, aby nie używać String dla haseł, ale zamiast tego użyj char []". - Peter vdL
@PetervdL heartbleed pozwala tylko na odczyt konkretnej, wielokrotnie wykorzystywanej kolekcji buforów (używanej zarówno w przypadku danych krytycznych dla bezpieczeństwa, jak i we / wy sieci bez konieczności usuwania między - ze względu na wydajność), nie można łączyć tego z ciągiem Java, ponieważ z założenia nie można ich użyć ponownie . Nie można również odczytać pamięci losowej za pomocą Javy, aby uzyskać zawartość ciągu. Problemy z językiem i projektowaniem, które prowadzą do serca, nie są możliwe w przypadku ciągów Java. - josefx


  1. Ciągi są niezmienne w Javie, jeśli przechowujesz hasło w postaci zwykłego tekstu, będzie ono dostępne w pamięci, dopóki Garbage collector nie usunie go, a ponieważ Strings są używane w puli String do ponownego wykorzystania, istnieje spora szansa, że ​​pozostaną w pamięci przez długi czas, co stanowi zagrożenie bezpieczeństwa. Ponieważ każdy, kto ma dostęp do zrzutu pamięci, może znaleźć hasło w czystym tekście
  2. Rekomendacja Java za pomocą getPassword() metoda JPasswordField, która zwraca char [] i przestarzałe getText() metoda, która zwraca hasło w czystym tekście określającym powód bezpieczeństwa.
  3. toString () zawsze istnieje ryzyko drukowania zwykłego tekstu w pliku logu lub konsoli, ale jeśli użyjesz Array, nie wydrukujesz zawartości tablicy, zamiast jej miejsce w pamięci zostanie wydrukowane.

    String strPwd = "passwd";
    char[] charPwd = new char[]{'p','a','s','s','w','d'};
    System.out.println("String password: " + strPwd );
    System.out.println("Character password: " + charPwd );
    

    Hasło ciągu: passwd

    Hasło postaci: [C @ 110b2345

Końcowe przemyślenia: Chociaż używanie char [] nie wystarczy, należy usunąć zawartość, aby była bezpieczniejsza. Sugeruję również pracę z hashowanym lub zaszyfrowanym hasłem zamiast zwykłego tekstu i usuwanie go z pamięci zaraz po zakończeniu uwierzytelniania.


117
2017-12-27 20:22



"Ponieważ każdy, kto ma dostęp do zrzutu pamięci, może znaleźć hasło w czytelnym tekście" Jak uzyskać dostęp do zrzutu pamięci i znaleźć tam hasło? - Mohit Kanwar
@MohitKanwar Wyzwalanie pamięci przez jmap lub jvisualvm. Jeśli ustawiona jest opcja heapdumponoutofmemoryerror, może być możliwe wyzwolenie zdalnego zrzutu stosu, powodując brak pamięci. Po zakończeniu zrzutu możesz przejść przez to narzędzie za pomocą jvisualvm lub analizatora pamięci Eclipse lub narzędzi wiersza poleceń i sprawdzić wszystkie obiekty i ich wartości. - Ryan


Nie sądzę, że jest to słuszna sugestia, ale mogę przynajmniej odgadnąć przyczynę.

Myślę, że motywacją jest chęć upewnienia się, że można wymazać wszystkie ślady hasła w pamięci natychmiast i z pewnością po ich użyciu. Z a char[] możesz nadpisać każdy element tablicy pustym znakiem lub coś na pewno. Nie można edytować wartości wewnętrznej pliku String w ten sposób.

Ale samo to nie jest dobrą odpowiedzią; dlaczego nie wystarczy upewnić się, że odniesienie do char[] lub String nie ucieka? Wtedy nie ma problemu z bezpieczeństwem. Ale chodzi o to String obiekty mogą być intern()w teorii i utrzymywane przy życiu w stałej puli. Przypuszczam, że używam char[] zabrania takiej możliwości.


72
2018-01-16 14:27



Nie powiedziałbym, że problem polega na tym, że twoje referencje będą lub nie "uciekną". Po prostu ciągi pozostaną niezmienione w pamięci przez pewien dodatkowy czas, podczas gdy char[] może być modyfikowany, a następnie nie ma znaczenia, czy jest on gromadzony czy nie. A ponieważ interakcja z ciągami musi być wykonana jawnie dla nie-literałów, to tak samo, jak mówienie, że a char[] można odwoływać się do pola statycznego. - Groo
nie jest hasłem w pamięci jako ciąg znaków z postu formularza? - Dawesi


Odpowiedź została już udzielona, ​​ale chciałbym podzielić się problemem, który ostatnio odkryłem ze standardowymi bibliotekami Java. Podczas gdy teraz dokładają wszelkich starań, aby zastąpić ciągi haseł char[] wszędzie (co oczywiście jest dobre), inne dane krytyczne dla bezpieczeństwa wydają się być pomijane, jeśli chodzi o usuwanie go z pamięci.

Myślę o np. Prywatny klucz klasa. Rozważ scenariusz, w którym można załadować prywatny klucz RSA z pliku PKCS # 12, używając go do wykonania niektórych operacji. Teraz w tym przypadku samo podsłuchanie hasła nie pomoże ci tak długo, jak fizyczny dostęp do pliku klucza zostanie właściwie ograniczony. Jako atakujący znacznie lepiej byłoby, gdybyś uzyskał klucz bezpośrednio zamiast hasła. Pożądana informacja może być wyciekła rozmaitością, zrzuty pamięci, sesja debuggera lub pliki wymiany to tylko kilka przykładów.

A jak się okazuje, nie ma nic, co pozwoliłoby ci wyczyścić prywatne informacje o PrivateKey z pamięci, ponieważ nie ma interfejsu API, który umożliwia wymazanie bajtów tworzących odpowiednie informacje.

To jest zła sytuacja, jak ta papier opisuje, w jaki sposób można potencjalnie wykorzystać tę okoliczność.

Biblioteka OpenSSL na przykład nadpisuje krytyczne sekcje pamięci przed uwolnieniem kluczy prywatnych. Ponieważ Java zbiera śmieci, potrzebowalibyśmy jawnych metod wymazywania i unieważniania prywatnych informacji dla kluczy Java, które mają być zastosowane natychmiast po użyciu klucza.


58
2018-01-19 19:39



Jednym ze sposobów obejścia tego jest użycie implementacji PrivateKey to faktycznie nie ładuje swojej prywatnej zawartości do pamięci: na przykład za pomocą tokena sprzętowego PKCS # 11. Być może implementacja oprogramowania PKCS # 11 może zająć się ręcznym czyszczeniem pamięci. Być może używając czegoś takiego jak sklep NSS (który dzieli większość jego implementacji z PKCS11 typ sklepu w Javie) jest lepszy. The KeychainStore (Magazyn kluczy OSX) ładuje pełną zawartość klucza prywatnego do jego PrivateKey instancji, ale nie powinno to być konieczne. (Nie jestem pewien co WINDOWS-MY KeyStore działa w systemie Windows.) - Bruno
@Bruno Pewnie, żetony oparte na sprzęcie nie cierpią z tego powodu, ale co z sytuacjami, gdy jesteś mniej lub bardziej zmuszony do używania klucza programowego? Nie każde wdrożenie ma budżet na udostępnienie HSM. Przechowywanie kluczy oprogramowania będzie musiało w pewnym momencie załadować klucz do pamięci, więc IMO powinniśmy przynajmniej mieć możliwość wyczyszczenia pamięci w dowolnym momencie. - emboss
Absolutnie, właśnie zastanawiałem się, czy niektóre implementacje oprogramowania równoważne HSM mogą zachowywać się lepiej pod względem czyszczenia pamięci. Na przykład, podczas korzystania z uwierzytelniania klienta w Safari / OSX, proces Safari nigdy nie widzi klucza prywatnego, biblioteka bazowa protokołu SSL dostarczona przez system operacyjny rozmawia bezpośrednio z demonem bezpieczeństwa, który monituje użytkownika o użycie klucza z pęku kluczy. Chociaż wszystko to dzieje się w oprogramowaniu, podobna separacja może pomóc, jeśli podpisanie zostanie przekazane innej jednostce (nawet opartej na oprogramowaniu), która lepiej wyładuje lub wyczyści pamięć. - Bruno
@Bruno: Interesujący pomysł, dodatkowa warstwa pośrednia, która zajmuje się czyszczeniem pamięci, rzeczywiście rozwiązałaby to w sposób przejrzysty. Wpisanie opakowania PKCS # 11 do magazynu kluczy oprogramowania mogło już wystarczyć? - emboss


Jak stwierdza Jon Skeet, nie ma innego wyjścia, z wyjątkiem użycia refleksji.

Jednakże, jeśli refleks jest dla ciebie opcją, możesz to zrobić.

public static void main(String[] args) {
    System.out.println("please enter a password");
    // don't actually do this, this is an example only.
    Scanner in = new Scanner(System.in);
    String password = in.nextLine();
    usePassword(password);

    clearString(password);

    System.out.println("password: '" + password + "'");
}

private static void usePassword(String password) {

}

private static void clearString(String password) {
    try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] chars = (char[]) value.get(password);
        Arrays.fill(chars, '*');
    } catch (Exception e) {
        throw new AssertionError(e);
    }
}

po uruchomieniu

please enter a password
hello world
password: '***********'

Uwaga: jeśli ciąg znaków [] został skopiowany jako część cyklu GC, istnieje szansa, że ​​poprzednia kopia znajduje się gdzieś w pamięci.

Ta stara kopia nie pojawiłaby się w zrzucie sterty, ale jeśli masz bezpośredni dostęp do surowej pamięci procesu, możesz ją zobaczyć. Generalnie powinieneś unikać każdego, kto ma taki dostęp.


41
2017-07-08 09:26



Lepiej zrobić coś, co uniemożliwi wydrukowanie długości hasła, z którego otrzymujemy '***********'. - chux
@chux możesz użyć znaku o zerowej szerokości, choć może to być bardziej mylące niż użyteczne. Nie można zmienić długości tablicy znaków bez użycia funkcji Niebezpieczne. ;) - Peter Lawrey
Z powodu Deduplikacji napisanej w Java 8, myślę, że zrobienie czegoś takiego może być dość destrukcyjne ... możesz skończyć wyczyszczanie innych ciągów w twoim programie, które przez przypadek miały taką samą wartość hasła String. Jest mało prawdopodobne, ale możliwe ... - jamp
@jamp Java 9 ma mieć tę funkcję, jak również ciąg kompaktowy (to znaczy, używając bajtu []]). Nie wierzę, że tak się stanie w Javie 8. - Peter Lawrey
@PeterLawrey Musi być włączony z argumentem JVM, ale już istnieje. Można przeczytać o tym tutaj: blog.codecentric.de/en/2014/08/... - jamp


Edytować: Wracając do tej odpowiedzi po roku badań nad bezpieczeństwem, zdaję sobie sprawę, że to dość niefortunna implikacja, że ​​kiedykolwiek faktycznie porównasz hasła w postaci zwykłego tekstu. Proszę, nie rób tego. Użyj bezpiecznego jednokierunkowego skrótu z solą i rozsądną liczbą iteracji. Rozważ skorzystanie z biblioteki: trudno to naprawić!

Oryginalna odpowiedź: A co z wykorzystaniem funkcji String.equals ()? ocena zwarcia, a zatem jest podatny na atak czasowy? Może to być mało prawdopodobne, ale możesz teoretycznie czas na porównanie haseł w celu ustalenia poprawności sekwencji znaków.

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // Quits here if Strings are different lengths.
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // Quits here at first different character.
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

Niektóre dodatkowe zasoby na ataki czasowe:


32
2018-04-08 00:04



Ale to może być również w porównaniu do char [], gdzie będziemy robić to samo również przy sprawdzaniu poprawności haseł. więc w jaki sposób char [] jest lepszy niż ciąg? - Mohit Kanwar
Masz całkowitą rację, błąd można popełnić w obu kierunkach. Znajomość problemu jest najważniejsza, ponieważ w Javie nie ma jawnej metody porównywania haseł dla haseł opartych na ciągach lub char []. Powiedziałbym, że pokusa użycia funkcji compare () dla Strings jest dobrym powodem, aby pójść z char []. W ten sposób masz przynajmniej kontrolę nad tym, jak dokonuje się porównania (bez przedłużania String, co jest imo bólu). - Graph Theory
Poza tym porównywanie haseł w postaci zwykłego tekstu to i tak nie jest dobra, pokusa do użycia Arrays.equals dla char[] jest tak wysokie jak dla String.equals. Jeśli ktokolwiek się o to troszczył, istniała specjalna klasa kluczy zawierająca rzeczywiste hasło i zajmująca się problemami - och, czekaj, prawdziwe pakiety bezpieczeństwa mieć dedykowane klasy kluczowe, ten Q & A dotyczy tylko nawyk poza nimi, powiedzmy, np. JPasswordField, używać char[] zamiast String (gdzie używają rzeczywiste algorytmy byte[] tak czy inaczej). - Holger
Oprogramowanie zabezpieczające powinno zrobić coś takiego sleep(secureRandom.nextInt()) przed odrzuceniem próby zalogowania się, to nie tylko usunięcie możliwości ataków czasowych, ale także przeciwdziałanie próbom brutalnej siły. - Holger


To są wszystkie powody, które należy wybrać zwęglać[] tablica zamiast Strunowy dla hasła.

1.  Ponieważ ciągi są niezmienne w Javie, jeśli przechowujesz hasło w postaci zwykłego tekstu, będzie ono dostępne w pamięci, dopóki Garbage collector go nie usunie, a ponieważ String jest używany w puli String do ponownego wykorzystania, istnieje spora szansa, że ​​pozostanie w pamięci przez długi czas, które stanowią zagrożenie bezpieczeństwa. Ponieważ każdy, kto ma dostęp do zrzutu pamięci, może znaleźć hasło w czystym tekście i to jest kolejny powód, dla którego zawsze powinieneś używać zaszyfrowanego hasła niż zwykłego tekstu. Ponieważ Ciągi są niezmienne, nie ma możliwości zmiany zawartości Ciągów, ponieważ każda zmiana spowoduje utworzenie nowego Ciągu, a jeśli zwrócisz [], możesz ustawić cały jego element jako pusty lub zero. Przechowywanie hasła w tablicy znaków wyraźnie zmniejsza ryzyko kradzieży hasła.

2.  Java sama zaleca użycie metody getPassword () z JPasswordField, która zwraca char [] i przestarzałą metodę getText (), która zwraca hasło w czystym tekście określającym powód bezpieczeństwa. Dobrze jest postępować zgodnie z radą zespołu Java i stosować się do standardów, a nie przeciwdziałać temu.

3.  W przypadku String istnieje zawsze ryzyko drukowania zwykłego tekstu w pliku dziennika lub konsoli, ale jeśli użyjesz Array, nie będziesz drukował zawartości tablicy, zamiast jej miejsce w pamięci zostanie wydrukowane. choć nie jest to prawdziwy powód, ale wciąż ma sens.

    String strPassword="Unknown";
    char[] charPassword= new char[]{'U','n','k','w','o','n'};
    System.out.println("String password: " + strPassword);
    System.out.println("Character password: " + charPassword);

    String password: Unknown
    Character password: [C@110b053

Odniesienie od: http://javarevisited.blogspot.com/2012/03/why-character-array-is-better-than.html Mam nadzieję że to pomoże.


32
2018-05-08 10:17



To jest zbędne. Ta odpowiedź jest dokładną wersją odpowiedzi napisanej przez @SrujanKumarGulla stackoverflow.com/a/14060804/1793718. Nie kopiuj ani nie kopiuj dwóch razy tej samej odpowiedzi. - Lucky
Jaka jest różnica między 1.) System.out.println ("Hasło postaci:" + charPassword); i 2.) System.out.println (charPassword); Ponieważ daje to samo "nieznane" jako wynik. - Vaibhav_Sharma