Vraag Waarom heeft char [] de voorkeur boven String voor wachtwoorden?


In Swing heeft het wachtwoordveld een getPassword() (komt terug char[]) methode in plaats van het gebruikelijke getText() (komt terug String) methode. Op dezelfde manier ben ik een suggestie tegengekomen om niet te gebruiken String om wachtwoorden af ​​te handelen.

Waarom doet String vormen een bedreiging voor de veiligheid als het gaat om wachtwoorden? Het voelt onhandig om te gebruiken char[].


2894
2018-01-16 14:20


oorsprong


antwoorden:


Strings zijn onveranderlijk. Dat betekent dat zodra u de String, als een ander proces het geheugen kan dumpen, is er geen manier (afgezien van reflectie) kunt u eerder van de gegevens af garbage collection begint te werken.

Met een array kunt u de gegevens expliciet wissen als u klaar bent. U kunt de array overschrijven met alles wat u maar wilt, en het wachtwoord zal nergens in het systeem aanwezig zijn, zelfs niet voor het verzamelen van garbage.

Dus ja, dit is een veiligheidsprobleem - maar zelfs gebruik char[] verlaagt alleen de kans voor een aanvaller en alleen voor dit specifieke type aanval.

Zoals opgemerkt in de opmerkingen, is het mogelijk dat arrays die door de garbage collector worden verplaatst, losse kopieën van de gegevens in het geheugen achterlaten. Ik geloof dat dit implementatiespecifiek is - de vuilnisman mei wis alle herinneringen als het gaat, om dit soort dingen te voorkomen. Zelfs als dat zo is, is er nog steeds de tijd waarin het char[] bevat de eigenlijke karakters als een aanvalsvenster.


3714
2018-01-16 14:26



Hoewel andere suggesties hier geldig lijken, is er een andere goede reden. Met duidelijk String je hebt veel hogere kansen per ongeluk afdrukken van het wachtwoord naar logboeken, monitoren of een andere onveilige plaats. char[] is minder kwetsbaar.

Overweeg dit:

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

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

prints:

String: Password
Array: [C@5829428e

1078
2018-01-16 19:37



Om een ​​officieel document te citeren, de Java Cryptography Architecture-gids zegt dit over char[] vs. String wachtwoorden (over codering op basis van wachtwoorden, maar dit gaat natuurlijk meer over wachtwoorden):

Het lijkt logisch om het wachtwoord in een object te verzamelen en op te slaan   van type java.lang.String. Hier is echter de waarschuwing: Objects van   type String zijn onveranderlijk, d.w.z. er zijn geen methoden gedefinieerd die   sta toe dat u de inhoud van a. wijzigt (overschrijft) of op nul zet String   na gebruik. Deze functie maakt String objecten ongeschikt voor   het opslaan van beveiligingsgevoelige informatie zoals gebruikerswachtwoorden. U   moet altijd beveiligingsgevoelige informatie verzamelen en opslaan in a    char array in plaats daarvan.

Richtlijn 2-2 van de Secure Coding Guidelines voor de Java-programmeertaal, versie 4.0 zegt ook iets soortgelijks (hoewel het oorspronkelijk in de context van logging is):

Richtlijn 2-2: registreer geen zeer gevoelige informatie

Sommige informatie, zoals sofinummers (SSN's) en   wachtwoorden, is zeer gevoelig. Deze informatie mag niet worden bewaard   voor langer dan nodig, noch waar te zien, zelfs door   beheerders. Het moet bijvoorbeeld niet worden verzonden naar logbestanden en   de aanwezigheid ervan moet niet worden gedetecteerd door middel van zoekopdrachten. Sommige van voorbijgaande aard   gegevens kunnen worden bewaard in veranderlijke datastructuren, zoals char arrays, en   gewist onmiddellijk na gebruik. Het opruimen van datastructuren is verminderd   effectiviteit op typische Java runtime-systemen wanneer objecten worden verplaatst   geheugen transparant naar de programmeur.

Deze richtlijn heeft ook implicaties voor de implementatie en het gebruik van   bibliotheken op lager niveau die geen semantische kennis van de gegevens hebben   ze hebben te maken met. Als een voorbeeld, een string-parsering op laag niveau   bibliotheek kan de tekst waar het op werkt loggen. Een toepassing kan een SSN ontleden   met de bibliotheek. Dit creëert een situatie waarin de SSN's zijn   beschikbaar voor beheerders met toegang tot de logbestanden.


610
2018-01-17 03:20



Tekenreeksen (char[]) kan na gebruik worden gewist door elk teken in te stellen op nul en Strings niet. Als iemand op de een of andere manier het geheugenbeeld kan zien, kunnen ze een wachtwoord in platte tekst zien als strings worden gebruikt, maar als char[] wordt gebruikt, na het opschonen van gegevens met 0's, is het wachtwoord veilig.


305
2018-01-16 14:27



Sommige mensen denken dat je het geheugen dat wordt gebruikt om het wachtwoord op te slaan moet overschrijven zodra je het niet langer nodig hebt. Dit vermindert het tijdvenster dat een aanvaller heeft om het wachtwoord van uw systeem te lezen en negeert volledig het feit dat de aanvaller al voldoende toegang nodig heeft om het JVM-geheugen te kapen om dit te doen. Een aanvaller met zoveel toegang kan je belangrijkste gebeurtenissen opvangen, waardoor dit volkomen nutteloos wordt (AFAIK, dus corrigeer me als ik het mis heb).

Bijwerken

Dankzij de opmerkingen moet ik mijn antwoord bijwerken. Blijkbaar zijn er twee gevallen waarin dit een (zeer) kleine verbetering van de beveiliging kan toevoegen, omdat dit de tijd vermindert dat een wachtwoord op de harde schijf terecht kan komen. Toch denk ik dat het voor de meeste gebruiksgevallen overdreven is.

  • Uw doelsysteem is mogelijk slecht geconfigureerd of u moet ervan uitgaan dat het is en u moet paranoïde zijn over core dumps (kan geldig zijn als de systemen niet door een beheerder worden beheerd).
  • Uw software moet te paranoïde zijn om te voorkomen dat gegevens lekken terwijl de aanvaller toegang krijgt tot de hardware - met behulp van dingen zoals TrueCrypt (Stopgezet), VeraCryptof CipherShed.

Indien mogelijk zou het uitschakelen van core dumps en het swap-bestand voor beide problemen zorgen. Ze zouden echter beheerdersrechten vereisen en mogelijk de functionaliteit verminderen (minder geheugen om te gebruiken) en RAM-geheugen ophalen van een werkend systeem zou nog steeds een geldig probleem zijn.


183
2018-01-16 14:32



  1. Strings zijn onveranderlijk in Java als u het wachtwoord opslaat als platte tekst, zal het in het geheugen beschikbaar zijn totdat de garbage collector het wist en omdat strings worden gebruikt in Stringpool voor herbruikbaarheid, is er een vrij grote kans dat het voor een lange duur in het geheugen blijft vormt een beveiligingsrisico. Iedereen die toegang heeft tot geheugendump kan het wachtwoord in duidelijke tekst vinden
  2. Java-aanbeveling gebruik makend van getPassword() methode van JPasswordField die een teken [] retourneert en verouderd is getText() methode die het wachtwoord in duidelijke tekst retourneert met vermelding van de beveiligingsreden.
  3. toString () er is altijd een risico van het afdrukken van platte tekst in een logbestand of console, maar als u Array gebruikt, drukt u de inhoud van de array niet af, maar wordt de geheugenlocatie afgedrukt.

    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 );
    

    String-wachtwoord: passwd

    Karakter wachtwoord: [C @ 110b2345

Laatste gedachten: Hoewel het gebruik van char [] niet alleen voldoende is, moet je ook inhoud wissen om veiliger te zijn. Ik stel ook voor om te werken met hashed of gecodeerd wachtwoord in plaats van platte tekst en het uit het geheugen te wissen zodra de authenticatie is voltooid.


117
2017-12-27 20:22



Ik denk niet dat dit een goede suggestie is, maar ik kan op zijn minst raden wat de reden is.

Ik denk dat de motivatie ervoor wil zorgen dat je alle sporen van het wachtwoord onmiddellijk en met zekerheid na het gebruik in het geheugen kunt wissen. Met een char[] je zou elk element van de array kunnen overschrijven met een spatie of iets zeker. U kunt de interne waarde van a niet bewerken String op die manier.

Maar dat alleen is geen goed antwoord; waarom niet alleen zorgen voor een verwijzing naar de char[] of String ontsnapt niet? Dan is er geen beveiligingsprobleem. Maar het punt is dat String objecten kunnen zijn intern()in theorie en levend gehouden in het constante zwembad. Ik veronderstel dat ik het gebruik char[] verbiedt deze mogelijkheid.


72
2018-01-16 14:27



Het antwoord is al gegeven, maar ik wil graag een probleem delen dat ik de laatste tijd heb ontdekt met Java-standaardbibliotheken. Hoewel ze er nu voor zorgen dat de wachtwoordstrings worden vervangen door char[] overal (wat natuurlijk een goede zaak is), lijkt andere veiligheidskritische data over het hoofd gezien als het gaat om het uit het geheugen te halen.

Ik denk bijvoorbeeld aan de Prive sleutel klasse. Overweeg een scenario waarin u een privé-RSA-sleutel uit een PKCS # 12-bestand laadt, waarmee u een bepaalde bewerking uitvoert. Nu, in dit geval, zou het snuiven van het wachtwoord alleen niet veel kunnen, zolang de fysieke toegang tot het sleutelbestand op de juiste manier beperkt is. Als aanvaller zou je veel beter af zijn als je de sleutel rechtstreeks hebt verkregen in plaats van het wachtwoord. De gewenste informatie kan worden gelekt. Een veelvoud, core-dumps, een debugger-sessie of swap-bestanden zijn slechts enkele voorbeelden.

En aangezien het blijkt, is er niets dat u de privéinformatie van een laat verwijderen PrivateKey uit het geheugen, want er is geen API waarmee je de bytes kunt wissen die de bijbehorende informatie vormen.

Dit is een slechte situatie, zoals deze papier beschrijft hoe deze omstandigheid potentieel kan worden uitgebuit.

De OpenSSL-bibliotheek overschrijft bijvoorbeeld kritieke geheugensecties voordat persoonlijke sleutels worden vrijgegeven. Omdat Java vuilnis is verzameld, hebben we expliciete methoden nodig om privégegevens voor Java-sleutels te wissen en ongeldig te maken, die onmiddellijk na het gebruik van de sleutel moeten worden toegepast.


58
2018-01-19 19:39



Zoals Jon Skeet stelt, is er geen manier behalve door reflectie te gebruiken.

Als reflectie echter een optie voor u is, kunt u dit doen.

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);
    }
}

wanneer uitgevoerd

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

Opmerking: als het teken [] van de tekenreeks is gekopieerd als onderdeel van een GC-cyclus, bestaat de kans dat de vorige kopie zich ergens in het geheugen bevindt.

Deze oude kopie zou niet in een heap-dump verschijnen, maar als je direct toegang hebt tot het onbewerkte geheugen van het proces, zou je het kunnen zien. Over het algemeen moet je iedereen die zo'n toegang heeft vermijden.


41
2017-07-08 09:26



Bewerk: Terugkomend op dit antwoord na een jaar van veiligheidsonderzoek, realiseer ik me dat dit de nogal ongelukkige implicatie is dat je ooit leesbare wachtwoorden zou kunnen vergelijken. Alsjeblieft niet doen. Gebruik een veilige eenrichtingshash met een zout en een redelijk aantal iteraties. Overweeg een bibliotheek te gebruiken: dit spul is moeilijk om goed te krijgen!

Origineel antwoord: Hoe zit het met het feit dat String.equals () gebruikt kortsluiting evaluatieen is daarom kwetsbaar voor een timingaanval? Het is misschien onwaarschijnlijk, maar dat zou wel kunnen theoretisch tijd de wachtwoordvergelijking om de juiste reeks tekens te bepalen.

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;
}

Wat meer middelen voor timingaanvallen:


32
2018-04-08 00:04