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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

Чтобы процитировать официальный документ, Руководство по архитектуре криптографии 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 доступный для администраторов, имеющих доступ к файлам журнала.

Ответ 4

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

Ответ 5

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

Обновление

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

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

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

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

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

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

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

Ответ 9

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

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

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

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

Ответ 10

Это все причины, нужно выбрать массив 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: [[email protected]

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

Ответ 11

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

Оригинальный ответ: Что касается того факта, что 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;
}

Еще несколько ресурсов по таймингу:

Ответ 12

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

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

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

Ответ 13

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

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

Ответ 14

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

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

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

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

Ответ 15

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

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

Ответ 16

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

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 падает и генерирует дамп памяти - вы сможете увидеть пароль.

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

Ответ 17

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

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

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

Ответ 18

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

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

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

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