Подтвердить что ты не робот

Как я могу обеспечить уничтожение объекта String в Java?

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

Они не хотели этого делать и вместо этого открывали доступ к моей учетной записи пользователя Windows.

Так как я доверяю парню, но недостаточно, чтобы позволить ему работать 90 минут с открытой моей сессией, я просто добавлю приглашение для входа в свою программу, попросив комбинацию имени пользователя и пароля и войду на SQL Server с ней, Я запишусь и доверяю своему приложению, чтобы он сделал только то, что ему нужно.

Это, однако, создает небольшой риск для безопасности. руководство по паролям полей поверх Sun Сайт Oracle указывает, что пароли должны содержать минимальное количество времени, требуемое в памяти, и с этой целью метод getPassword возвращает массив char[], который вы можете обнулить, как только вы закончите с ним.

Однако класс Java DriverManager принимает только те объекты String как пароли, поэтому я не смогу избавиться от пароля, как только закончите с ним. И поскольку мое приложение, кстати, довольно слабое в распределении и требованиях к памяти, кто знает, как долго он выживет в памяти? Программа будет работать довольно долго, как указано выше.

Конечно, я не могу контролировать все классы SQL Server JDBC с моим паролем, но я надеялся, что смогу контролировать, что я делаю с моим паролем.

Есть ли верный способ уничтожить/обнулить объект String с Java? Я знаю, что оба вида против языка (уничтожение объекта не является детерминированным, а объекты String неизменяемы), а System.gc() тоже непредсказуем, но все же; любая идея?

4b9b3361

Ответ 1

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

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

Ответ 2

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

  char[] chars = {'a', 'b', 'c'};
  Constructor<String> con = String.class.getDeclaredConstructor(int.class, int.class, char[].class);
  con.setAccessible(true);
  String password = con.newInstance(0, chars.length, chars);
  System.out.println(password);

  //erase it
  Arrays.fill(chars, '\0');
  System.out.println(password);

Изменить

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

Ответ 3

если вы абсолютно обязательны, сохраните WeakReference в строке и продолжайте собирать память до тех пор, пока вы не нажмете сборку мусора строки, которую вы можете обнаружить, протестировав, если значение weakreference стало нулевым. это может по-прежнему оставлять байты в адресном пространстве процесса. может быть, еще пару отбросов сборщика мусора даст вам комфорт? поэтому после того, как ваша исходная строка будет отменена, вы создадите еще одну слабую ссылку и отбросите ее до тех пор, пока она не обнуляется, что означало бы полный цикл сбора мусора.

каким-то образом, я должен добавить LOL к этому, хотя мой ответ выше совершенно серьезно:)

Ответ 4

Вы можете изменить значение внутреннего char[] с помощью отражения.

Вы должны быть осторожны, чтобы либо изменить его на массив с одинаковой длиной, либо обновить поле count. Если вы хотите использовать его как запись в наборе или как значение на карте, вам нужно будет пересчитать хэш-код и установить значение поля hashCode.

Таким образом, минимальный код для достижения этого -

        String password = "password";

        Field valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);
        char[] chars  = (char[]) valueField.get(password);

        chars[0] = Character.valueOf('h');

        System.out.println(password);

Ответ 5

Я не уверен в классе DriverManager.
Вообще говоря, вы правы, рекомендуется указывать пароль в массивах char и явно очищать память после использования.
Наиболее распространенный пример:

KeyStore store = KeyStore. getInstance(KeyStore, getDefaultType()) ;
char[] password = new char[] {'s','e','c','r','e','t'};
store .load(is, password );
//After finished clear password!!!
Arrays. fill(password, '\u0000' ) ;

В JSSE и JCA дизайн имел именно это в виду. Вот почему API-интерфейсы ожидают char[], а не строки.
Поскольку, поскольку вы очень хорошо знаете, что Строки неизменяемы, пароль имеет право на будущую сборку мусора, и вы не можете после этого reset. Это может привести к угрозам безопасности со стороны вредоносных программ, которые отскакивают области памяти.
Я не думаю, что в этом случае вы смотрите на работу. На самом деле есть аналогичный вопрос:
Почему диспетчер драйверов не использует char массивы?
но нет четкого ответа.
Похоже, что концепция состоит в том, что пароль уже хранится в файле свойств (уже есть конструктор конструктора DriverManager, принимающий свойства), и поэтому сам файл уже накладывает больший риск, чем фактическая загрузка пароля из файла в строку. < ш > Или у дизайнеров API были некоторые предположения о безопасности машины, обращающейся к БД.
Я думаю, что самым безопасным вариантом было бы попытаться изучить, если это возможно, то, как DriverManager использует пароль, т.е. Поддерживает ли он внутреннюю ссылку и т.д.

Ответ 6

Интересный вопрос. Некоторые googeling показали это: http://securesoftware.blogspot.com/2009/01/java-security-why-not-to-use-string.html. Согласно комментарию, это не повлияет.

Что произойдет, если вы не сохраните String в переменной, а передаете ее через новую строку (char [])?

Ответ 7

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

Может быть, поможет альтернативный подход? Я не знаком с SQL Server, но в Oracle практика заключается в том, чтобы пользователь имел собственные объекты базы данных и набор хранимых процедур, а пользователь B не имел ничего, кроме разрешения на запуск для процедур. Таким образом, пароль больше не является проблемой.

В вашем случае будет вашим пользователем, который владеет всем объектом базы данных и хранимыми процедурами, и вашему сотруднику потребуются права на запуск хранимых процедур, которые, как мы надеемся, администраторы баз данных будут менее неохотно давать.

Ответ 8

поэтому я не смогу избавиться от пароля, как только закончим.

Почему бы и нет?

Если вы получаете соединение через

Driver.getConnection 

вам просто нужно передать пароль и позволить ему быть gc'ed.

void loginScreen() {
   ... 
   connect( new String( passwordField.getPassword() ) );
   ...
}
...
void connect( String withPassword ) { 
    connection = DriverManager.getConnection( url, user, withPassword );
    ...
}//<-- in this line there won't be any reference to your password anymore.

Когда элемент управления возвращается из метода connect, больше нет ссылки для вашего пароля. Никто больше не может его использовать. Если вам нужно создать новый сеанс, вам нужно снова вызвать "подключиться" с новым паролем. Ссылка на объект, который хранит вашу информацию, теряется.

Ответ 9

Если строка не удерживается менеджером драйверов JDBC (большой, если), я бы не стал беспокоиться о том, чтобы заставить его уничтожить. Современная JVM, по-прежнему запускает сборку мусора довольно быстро даже с большим количеством доступной памяти. Проблема заключается в том, эффективна ли сборка мусора "безопасное стирание". Я сомневаюсь, что это так. Я бы предположил, что он просто забывает ссылку на эту ячейку памяти и ничего не обнуляет.