Вопрос: Почему char [] предпочитает использовать String для паролей?


В Swing поле пароля имеет getPassword()(возвраты char[]) вместо обычного getText()(возвраты String). Точно так же я столкнулся с предложением не использовать Stringдля обработки паролей.

Почему Stringпредставляют угрозу безопасности, когда речь идет о паролях? Это неудобно использовать char[],


2861


источник


Ответы:


Строки неизменяемы , Это означает, что после создания String, если другой процесс может сбрасывать память, нет способа (кроме отражение ) вы можете избавиться от данных до вывоз мусора удары в.

С помощью массива вы можете явно стереть данные после того, как вы закончите с ним. Вы можете переписать массив всем, что угодно, и пароль не будет присутствовать нигде в системе, даже до сбора мусора.

Так что да, это является проблема безопасности - но даже использование char[]только уменьшает окно возможностей для злоумышленника, и это только для этого конкретного типа атаки.

Как отмечено в комментариях, возможно, что массивы, перемещаемые сборщиком мусора, оставят в памяти остаточные копии данных. Я считаю, что это специфично для реализации - сборщик мусора май очистите всю память, как она есть, чтобы избежать такого рода вещей. Даже если это так, еще есть время, в течение которого char[]содержит фактические символы в качестве окна атаки.


3670



Хотя другие предложения здесь выглядят действительно, есть еще одна веская причина. С равнинной Stringу вас гораздо больше шансов случайно распечатать пароль для журналов , мониторы или другое небезопасное место. char[]менее уязвим.

Учти это:

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

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

Печать:

String: Password
Array: [C@5829428e

1067



Чтобы процитировать официальный документ, Руководство по архитектуре криптографии Java говорит об этом char[]против Stringпаролей (о шифровании на основе пароля, но это, в основном, касается паролей, конечно):

Казалось бы логичным собирать и хранить пароль в объекте   типа java.lang.String, Однако, вот оговорка: Objectс   тип Stringявляются неизменными, то есть нет методов, которые определяют, что   позволяют изменять (перезаписывать) или обнулять содержимое Stringпосле использования. Эта функция делает Stringобъекты, непригодные для   хранения секретной информации, такой как пароль пользователя. Вы   должен всегда собирать и хранить конфиденциальную информацию безопасности в charмассив.

Руководство 2-2 Руководства по безопасному кодированию для языка программирования Java, версия 4.0 также говорит что-то подобное (хотя оно первоначально в контексте ведения журнала):

Руководящий принцип 2-2: не регистрировать высокочувствительную информацию

Некоторая информация, такая как номера социального страхования (SSN) и   пароли, очень чувствительны. Эта информация не должна сохраняться   дольше, чем необходимо, и там, где это возможно, даже   администраторы. Например, он не должен быть отправлен в файлы журналов и   его присутствие не должно обнаруживаться при поиске. Некоторые переходные процессы   данные могут храниться в изменяемых структурах данных, таких как массивы символов и   очищается сразу после использования. Очистка структур данных сократилась   эффективности по типичным системам времени исполнения Java, поскольку объекты перемещаются в   памяти прозрачно программисту.

Это руководство также имеет последствия для внедрения и использования   библиотеки нижнего уровня, не имеющие семантического знания данных   они имеют дело с. В качестве примера, синтаксический анализ на нижнем уровне   библиотека может регистрировать текст, над которым он работает. Приложение может анализировать SSN   с библиотекой. Это создает ситуацию, когда SSN   доступный для администраторов, имеющих доступ к файлам журнала.


604



Массивы символов ( char[]) можно очистить после использования, установив каждый символ в ноль, а строки - нет. Если кто-то может каким-то образом увидеть изображение памяти, они могут видеть пароль в виде обычного текста, если используются строки, но если char[]используется после очистки данных с 0, пароль защищен.


298



Некоторые люди считают, что вам нужно перезаписать память, используемую для хранения пароля, как только она вам больше не понадобится. Это уменьшает время, которое атакующий должен прочитать пароль из вашей системы и полностью игнорирует тот факт, что злоумышленнику уже достаточно нужен доступ, чтобы захватить JVM-память для этого. Злоумышленник с таким доступом может поймать ваши ключевые события, делая это совершенно бесполезным (AFAIK, поэтому, пожалуйста, поправьте меня, если я ошибаюсь).

Обновить

Благодаря комментариям я должен обновить свой ответ. По-видимому, есть два случая, когда это может добавить (очень) незначительное улучшение безопасности, поскольку это сокращает время, когда пароль может приземлиться на жесткий диск. Тем не менее, я думаю, что это слишком много для большинства случаев использования.

  • Ваша целевая система может быть плохо сконфигурирована, или вы должны предположить, что она есть, и вы должны быть параноидальными в отношении дампов ядра (может быть действительным, если системы не управляются администратором).
  • Ваше программное обеспечение должно быть слишком параноидальным, чтобы предотвратить утечку данных, когда злоумышленник получает доступ к аппаратным средствам - используя такие вещи, как TrueCrypt (Прекращено), VeraCrypt , или CipherShed ,

Если возможно, отключение дампов ядра и файла подкачки позаботится обо всех проблемах. Тем не менее, они потребуют прав администратора и могут уменьшить функциональность (меньше памяти для использования) и вытащить ОЗУ из работающей системы, все равно будет актуальной проблемой.


178



  1. Строки неизменяемы в Java, если вы храните пароль в виде обычного текста, он будет доступен в памяти до тех пор, пока сборщик мусора не очистит его, и поскольку строки используются в пуле String для повторного использования, существует довольно высокая вероятность того, что он останется в памяти в течение длительного времени, что представляет угрозу безопасности. Поскольку любой, у кого есть доступ к дампу памяти, может найти пароль в виде открытого текста
  2. Рекомендация Java с помощью getPassword()метод JPasswordField, который возвращает char [] и устарел getText()метод, который возвращает пароль в ясном тексте с указанием причины безопасности.
  3. нанизывать() всегда существует риск печати обычного текста в файле журнала или консоли, но если вы используете Array, вы не будете печатать содержимое массива, а его память будет распечатана.

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

    Строковый пароль: passwd

    Пароль персонажа: [C @ 110b2345

Последние мысли: Хотя использование char [] не только достаточно, вам нужно стереть контент, чтобы быть более безопасным. Я также предлагаю работать с хешированным или зашифрованным паролем вместо обычного текста и освобождать его из памяти, как только будет завершена аутентификация.


114



Я не думаю, что это правильное предложение, но я могу по крайней мере догадаться по этой причине.

Я считаю, что мотивация заключается в том, что вы можете быстро и с уверенностью стереть все следы пароля в памяти и после его использования. С char[]вы можете перезаписать каждый элемент массива пустым или что-то точно. Вы не можете редактировать внутреннее значение Stringсюда.

Но это одно не является хорошим ответом; почему бы не просто убедиться, что ссылка на char[]или Stringне убегает? Тогда нет проблем с безопасностью. Но дело в том, что Stringобъекты могут быть intern()в теории и поддерживался в постоянном пуле. Я полагаю, используя char[]запрещает эту возможность.


69



The answer has already been given, but I'd like to share an issue that I discovered lately with Java standard libraries. While they take great care now of replacing password strings with char[] everywhere (which of course is a good thing), other security-critical data seems to be overlooked when it comes to clearing it from memory.

I'm thinking of e.g. the PrivateKey class. Consider a scenario where you would load a private RSA key from a PKCS#12 file, using it to perform some operation. Now in this case, sniffing the password alone wouldn't help you much as long as physical access to the key file is properly restricted. As an attacker, you would be much better off if you obtained the key directly instead of the password. The desired information can be leaked manifold, core dumps, a debugger session or swap files are just some examples.

And as it turns out, there is nothing that lets you clear the private information of a PrivateKey from memory, because there's no API that lets you wipe the bytes that form the corresponding information.

This is a bad situation, as this paper describes how this circumstance could be potentially exploited.

The OpenSSL library for example overwrites critical memory sections before private keys are freed. Since Java is garbage-collected, we would need explicit methods to wipe and invalidate private information for Java keys, which are to be applied immediately after using the key.


55



As Jon Skeet states, there is no way except by using reflection.

However, if reflection is an option for you, you can do this.

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

when run

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

Note: if the String's char[] has been copied as a part of a GC cycle, there is a chance the previous copy is somewhere in memory.

This old copy wouldn't appear in a heap dump, but if you have direct access to the raw memory of the process you could see it. In general you should avoid anyone having such access.


38



Edit: Coming back to this answer after a year of security research, I realize it makes the rather unfortunate implication that you would ever actually compare plaintext passwords. Please don't. Use a secure one-way hash with a salt and a reasonable number of iterations. Consider using a library: this stuff is hard to get right!

Original answer: What about the fact that String.equals() uses short-circuit evaluation, and is therefore vulnerable to a timing attack? It may be unlikely, but you could theoretically time the password comparison in order to determine the correct sequence of characters.

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

Some more resources on timing attacks:


29