it-swarm.com.ru

Почему char [] предпочтительнее, чем String для паролей?

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

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

3086
Ahamed

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

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

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

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

3941
Jon Skeet

В то время как другие предложения здесь кажутся действительными, есть еще одна веская причина. С простой 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: [[email protected]
1136
Konrad Garus

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

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

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

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

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

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

639
Bruno

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

326
alephx

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

Обновление

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

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

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

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

    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 [] не достаточно, вам нужно стереть контент, чтобы быть более безопасным. Я также предлагаю работать с хешированным или зашифрованным паролем вместо простого текста и удалять его из памяти, как только аутентификация будет завершена.

131
Srujan Kumar Gulla

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

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

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

76
Sean Owen

Ответ уже дан, но я хотел бы поделиться проблемой, которую я обнаружил в последнее время, со стандартными библиотеками Java. Несмотря на то, что теперь они очень внимательно следят за заменой строк пароля на char[] везде (что, конечно, хорошо), другие важные для безопасности данные, похоже, упускаются из виду, когда дело доходит до очистки их из памяти.

Я имею в виду, например, класс PrivateKey . Рассмотрим сценарий, в котором вы бы загружали закрытый ключ RSA из файла PKCS # 12, используя его для выполнения какой-либо операции. Теперь, в этом случае, прослушивание пароля само по себе не очень поможет, если физический доступ к файлу ключа должным образом ограничен. Как злоумышленнику, вам было бы гораздо лучше, если бы вы получили ключ вместо пароля. Желаемой информацией может быть утечка коллектора, дампы ядра, сеанс отладчика или файлы подкачки - это только некоторые примеры.

И, как выясняется, нет ничего, что позволяло бы очистить личную информацию PrivateKey из памяти, потому что нет API, который позволял бы стирать байты, формирующие соответствующую информацию.

Это плохая ситуация, так как paper описывает, как это обстоятельство может быть потенциально использовано.

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

61
emboss

Как утверждает Джон Скит, нет другого пути, кроме как с помощью отражения. 

Однако, если рефлексия - вариант для вас, вы можете сделать это.

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

когда беги

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

Примечание: если символ String [char] был скопирован как часть цикла GC, существует вероятность, что предыдущая копия находится где-то в памяти. 

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

46
Peter Lawrey

Это все причины, поэтому следует выбирать массив char [] вместо String для пароля.

1. Поскольку строки являются неизменяемыми в Java, если вы сохраните пароль в виде обычного текста, он будет доступен в памяти до тех пор, пока сборщик мусора не очистит его, и поскольку String используется в пуле String для повторного использования, существует довольно высока вероятность того, что он останется в памяти на длительный срок, что создает угрозу безопасности. 

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

2. Сама Java рекомендует использовать метод getPassword () из JPasswordField, который возвращает char [], вместо устаревшего метода getText (), который возвращает пароли в виде открытого текста с указанием соображений безопасности. Хорошо следовать советам команды Java и придерживаться стандартов, а не идти против них.

3. При использовании String всегда существует риск печати простого текста в файле журнала или консоли, но если вы используете массив, вы не будете печатать содержимое массива, а вместо этого будет напечатано его расположение в памяти. Хотя это и не реальная причина, это все же имеет смысл.

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: [[email protected]

Ссылка на этот блог . Надеюсь, это поможет.

37
Human Being

Edit: Возвращаясь к этому ответу после года исследований в области безопасности, я понимаю, что это весьма прискорбно означает, что вы когда-либо будете сравнивать незашифрованные пароли. Пожалуйста, не надо. Используйте безопасный односторонний хеш с солью и разумным количеством итераций . Подумайте об использовании библиотеки: этот материал трудно понять правильно!

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

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

Еще несколько ресурсов о временных атаках:

37
Graph Theory

Нет ничего, что массив char дает вам против String, если вы не очистите его вручную после использования, и я не видел, чтобы кто-нибудь на самом деле делал это. Поэтому для меня предпочтение char [] vs String немного преувеличено.

Посмотрите на широко используемый Библиотека Spring Security здесь и спросите себя - парни из Spring Security некомпетентны или пароли char [] просто не имеют особого смысла. Когда какой-нибудь злобный хакер захватит дампы памяти вашего RAM, убедитесь, что он получит все пароли, даже если вы используете сложные способы их скрытия.

Тем не менее, Java постоянно меняется, и некоторые страшные функции, такие как функция дедупликации String в Java 8 могут включать объекты String без вашего ведома. Но это другой разговор.

33
Oleg Mikheev

Строки являются неизменяемыми и не могут быть изменены после того, как они были созданы. Создание пароля в виде строки будет оставлять ложные ссылки на пароль в куче или в пуле строк. Теперь, если кто-то берет кучу дампов Java-процесса и тщательно просматривает его, он может угадать пароли. Конечно, эти неиспользуемые строки будут собираться мусором, но это зависит от того, когда включится GC.

С другой стороны, char [] изменяются, как только аутентификация завершена, вы можете перезаписать их любым символом, таким как все M или обратные слэши. Теперь, даже если кто-то получит дамп кучи, он не сможет получить пароли, которые в данный момент не используются. Это дает вам больше контроля в том смысле, что вы сами очищаете содержимое объекта от ожидания, пока GC сделает это.

29
Geek

1) Поскольку строки являются неизменяемыми в Java, если вы храните пароль в виде обычного текста, он будет доступен в памяти до тех пор, пока сборщик мусора не очистит его, и поскольку String используется в пуле строк для повторного использования, существует довольно высокая вероятность того, что он останется в памяти на длительный срок, что создает угрозу безопасности. Поскольку любой, кто имеет доступ к дампу памяти, может найти пароль в виде открытого текста, и это еще одна причина, по которой вы всегда должны использовать зашифрованный пароль, а не простой текст. Поскольку Strings являются неизменяемыми, содержимое Strings не может быть изменено, потому что любое изменение приведет к созданию новой String, в то время как если вы char [], вы все равно можете установить все его элементы как пустые или нулевые. Поэтому хранение пароля в массиве символов снижает риск кражи пароля.

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

16
Neeraj Gahlawat

Краткий и простой ответ будет потому, что char[] является изменяемым, а объекты String - нет.

Strings в Java являются неизменяемыми объектами. Вот почему они не могут быть изменены после создания, и поэтому единственный способ удаления их содержимого из памяти - сбор мусора. Это будет только тогда, когда память, освобожденная объектом, может быть перезаписана, и данные исчезнут.

Теперь сборка мусора в Java не происходит с гарантированным интервалом. Таким образом, String может сохраняться в памяти в течение длительного времени, и если в течение этого времени произойдет сбой процесса, содержимое строки может оказаться в дампе памяти или в каком-либо журнале. 

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

16
Pritam Banerjee

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

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

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
 allowUser = true;
 cleanPassword(passw);
 cleanPassword(dbPassword);
 passw=null;
}

private static void cleanPassword (char[] pass) {
 for (char ch: pass) {
  ch = '0';
 }
}

Один из сценариев, в котором злоумышленник может использовать его, - это аварийный дамп - когда JVM дает сбой и генерирует дамп памяти - вы сможете увидеть пароль.

Это не обязательно злонамеренный внешний злоумышленник. Это может быть пользователь поддержки, который имеет доступ к серверу для целей мониторинга. Он мог заглянуть в сбой и найти пароли.

15
ACV

Строка в Java неизменна. Таким образом, всякий раз, когда создается строка, она остается в памяти до тех пор, пока не будет собрана сборка мусора. Таким образом, любой, кто имеет доступ к памяти, может прочитать значение строки. 
Если значение строки изменено, то в итоге будет создана новая строка. Таким образом, как исходное значение, так и измененное значение остаются в памяти до тех пор, пока они не будут удалены. 

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

Из соображений безопасности лучше хранить пароль в виде массива символов.

11
Saathvik

Использование в строки или Char [] пароля всегда спорно, потому что оба подход имеет свою собственную полезность и недостатки, а также. Это в зависимости от потребностей, которые пользователь ожидает выполнения. Нижеприведенные строки могут помочь лучше понять, когда использовать какой контейнер: Поскольку String в Java является неизменным, всякий раз, когда кто-то пытается манипулировать вашей строкой, он создает новый объект, а существующий остается без ограничений. Это может рассматриваться как преимущество для хранения пароля как String, но с другой стороны. Строковый объект остается в памяти даже после использования. Так или иначе, если кто-то получил место в памяти, может легко отследить ваш пароль, хранящийся в этом месте…. При входе в Char [], который является изменяемым, но имеет преимущество в том, что после использования программист может явно очистить массив или переопределить значения. Так что после неиспользования он очищается, и никто никогда не сможет узнать об информации, которую вы сохранили.

Исходя из вышеизложенных обстоятельств, можно получить идею, использовать ли String или Char [] для их требования.

Благодарю.

1
Neeraj

Короче говоря, это из-за того, что может случиться с сохраненным паролем после того, как вы закончите его использовать. 

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

Все это связано с тем, как сборщики мусора и неизменяемые объекты работают.

0
hamza belmellouki