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

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

Почему String создает угрозу безопасности при использовании паролей? Это неудобно использовать char[].

3016
задан Ahamed 16 янв. '12 в 17:20
источник поделиться
18 ответов

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

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

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

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

3853
ответ дан Jon Skeet 16 янв. '12 в 17:26
источник поделиться

В то время как другие предложения здесь выглядят действительно, есть еще одна веская причина. С обычным 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
1114
ответ дан Konrad Garus 16 янв. '12 в 22:37
источник поделиться

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

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

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

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

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

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

634
ответ дан Bruno 17 янв. '12 в 6:20
источник поделиться

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

319
ответ дан alephx 16 янв. '12 в 17:27
источник поделиться

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

Обновление

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

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

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

194
ответ дан josefx 16 янв. '12 в 17:32
источник поделиться
  • Строки являются неизменяемыми в Java, если вы храните пароль в виде обычного текста, он будет доступен в памяти до тех пор, пока сборщик мусора не очистит его, и поскольку строки используются в пуле строк для повторного использования, вероятность того, что он останется в памяти в течение длительного времени, что создает угрозу безопасности. Поскольку любой, у кого есть доступ к дампу памяти, может найти пароль в ясном тексте
  • Рекомендация Java с использованием метода getPassword() JPasswordField, который возвращает метод char [] и устаревший getText(), который возвращает пароль в текстовом виде с указанием причины безопасности.
  • toString() всегда существует риск печати обычного текста в файле журнала или консоли, но если вы используете 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 [] не только достаточно, вам нужно стереть контент, чтобы быть более безопасным. Я также предлагаю работать с хешированным или зашифрованным паролем вместо обычного текста и освобождать его из памяти, как только будет завершена аутентификация.

125
ответ дан Srujan Kumar Gulla 27 дек. '12 в 23:22
источник поделиться

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

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

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

76
ответ дан Sean Owen 16 янв. '12 в 17:27
источник поделиться

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

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

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

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

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

61
ответ дан emboss 19 янв. '12 в 22:39
источник поделиться

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

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

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, есть вероятность, что предыдущая копия находится где-то в памяти.

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

44
ответ дан Peter Lawrey 08 июля '15 в 12:26
источник поделиться

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

Оригинальный ответ: Что касается того факта, что 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 08 апр. '15 в 3:04
источник поделиться

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

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

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

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

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

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

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

36
ответ дан Human Being 08 мая '14 в 13:17
источник поделиться

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

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

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

33
ответ дан Oleg Mikheev 24 янв. '15 в 10:43
источник поделиться

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

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

29
ответ дан Geek 28 июля '15 в 13:46
источник поделиться

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

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

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

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

17
ответ дан Pritam Banerjee 09 дек. '16 в 22:02
источник поделиться

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

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

16
ответ дан Neeraj Gahlawat 07 сент. '17 в 13:47
источник поделиться

Строка неизменна и идет в пул строк. После написания это нельзя перезаписать.

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

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

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

14
ответ дан ACV 26 апр. '18 в 1:39
источник поделиться

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

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

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

12
ответ дан Saathvik 28 июля '17 в 9:33
источник поделиться

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

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

Благодарю.

0
ответ дан Neeraj 03 дек. '18 в 9:31
источник поделиться

Другие вопросы по меткам или Задайте вопрос