Питання Чому char [] віддавав перевагу над паролями String?


У Swing у полі пароля є getPassword() (повертає char[]) метод замість звичайного getText() (повертає String) метод. Аналогічним чином, я наткнувся на пропозицію не використовувати String для обробки паролів.

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


2901
2018-01-16 14:20


походження


Чи можете ви навести джерело пропозиції? Я не можу подумати про причину char[] бути більш безпечним, за винятком, можливо, найбільш непрофесійних загроз. - Viruzzo
@ Віруццо погляньте на javadocs. The getText() метод JPasswordField застаріла на користь getPassword() "з міркувань безпеки". - dogbane
char []? Чому не байт []? У будь-якому потоці аналізатора dump / heap ви можете побачити Strings, і це легко помітити, якщо він містить пароль. Для char [] важче, але все-таки, якщо ваш аналіз нахилу буде відображати символи по черзі, він буде таким самим, як String. Для байту [] це малоймовірно. - mvmn
Для того, щоб надати дійсний сценарій, де це може стати важливим, загальноприйнятою практикою є судова експертиза, щоб не вимикати жодного комп'ютера, що потребує розслідування, до того, як вони скинуть пам'ять, якщо вони використовують шифрування програмного забезпечення. Навіть найкраща система шифрування повинна підтримувати стан, здатний розшифровувати. - Zenexer
@scottlafoy Обидва char[] і String об'єкти будуть передані за посиланням; індивідуальний char значення не буде. Це коли об'єкти передаються вартість, а не посилання, що створюються декілька копій. Як правило, це не проблема, оскільки такі об'єкти, як правило, призначені бути невеликими. - Zenexer


Відповіді:


Струни незмінні. Це означає, як тільки ви створите String, якщо інший процес може скинути пам'ять, немає ніякого способу (крім рефлексія) Ви можете позбутися даних раніше сміття збирання удар по

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

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

Як зазначалося в коментарях, цілком можливо, що масиви, що рухаються смітником, залишать брудні копії даних у пам'яті. Я вважаю, що це специфічне для реалізації - збирач сміття може очистити всю пам'ять, як це відбувається, щоб уникнути такого роду речі. Навіть якщо це станеться, ще є час, протягом якого char[] містить фактичні символи як вікно атаки.


3720
2018-01-16 14:26



Я розумію, що з реорганізацією обсягу пам'яті, що може бути зроблено з runtime, копії навіть a char[] може залишатися в пам'яті і ніколи не очищатись, поки ця пам'ять не буде використана повторно. Я не знаю джерела, який це підтверджує. У будь-якому випадку я сумніваюсь, що String буде очищений під час GC, але в той час, коли об'єкт перерозподілений над цією пам'яттю. - Mark Peters
@ Марк Пітерс: якщо GC починається, перед очищенням масиву ви маєте іншу копію. Але ця копія, принаймні, знаходиться у важко використаній області пам'яті, тому, швидше за все, вона дуже швидко перезаписана. - Mnementh
@ Xeon06: у .NET там SecureString що ще краще - але відносно болісно використовувати. - Jon Skeet
Чи ми глядаємо про те, що шкідливий об'єкт, який може читати ці дані, вже має доступ до пам'яті для вашої системи? Схоже, що було б настільки більшою турботою, що робити цей символ [] замість String просто здається піском в океані. - corsiKa
@corsika: Як я вже згадував у відповідь, це зменшує один конкретний вектор атаки, це все. Це змушує злочинця важче. - 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: [C@5829428e

1080
2018-01-16 19:37



@ voo, але я сумніваюся, що ви б журнали через прямий запис потоку та конкатенації. Система реєстрації перетворить символ [] на гарний вихід - bestsss
@ Thr4wn За замовчуванням виконується toString є classname@hashcode. [C репрезентує char[], решта - шістнадцятковий хеш-код. - Konrad Garus
Цікава ідея. Я хотів би зазначити, що це не переноситься на Scala, що має сенс для String для масивів. - mauhiz
Я б написав a Password клас типу для цього. Це менш незрозуміло, і важче випадково пройти кудись. - rightfold
Чому хтось вважатиме, що масив символів збирається бути об'єктом? Я не впевнений, що я отримую, чому кожен любить цю відповідь. Припустимо, що ви це зробили: System.out.println ("Пароль" .toCharArray ()); - GC_


Цитувати офіційний документ Інструкція з архітектури Java Cryptography говорить про це char[] проти String паролі (про шифрування на основі пароля, але це, звичайно, про паролі, звичайно):

Здавалося б, логічно збирати і зберігати пароль у об'єкті   типу java.lang.String. Проте тут є застереження: Objectс   тип String є незмінними, тобто не визначено ніяких методів   дозволить вам змінити (перезаписати) або виключити вміст a String   після використання Ця функція робить String об'єкти непридатні для   зберігання конфіденційної інформації, такої як паролі користувача. ви   повинен завжди збирати та зберігати конфіденційну інформацію в a    char масив замість.

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

Рекомендація 2-2: Не вводьте вкрай конфіденційну інформацію

Деяка інформація, така як номери соціального страхування (SSN) та   паролі, дуже чутливі. Цю інформацію не слід зберігати   для довше, ніж потрібно, і де його можна побачити, навіть   адміністратори Наприклад, його не слід надсилати до файлів журналу та   її присутність не повинна бути виявлена ​​під час пошуку. Деякі перехідні   дані можуть зберігатися в змінних структурах даних, таких як масивів символів, і   очищається відразу після використання. Скидання структур даних скоротилось   ефективність в типових Java-системах виконання, оскільки об'єкти переміщуються   пам'ять прозоро для програміста.

Ця рекомендація також має наслідки для реалізації та використання   бібліотеки нижчого рівня, які не мають семантичного знання даних   вони мають справу. Наприклад, це синтаксичний аналіз низького рівня   Бібліотека може зареєструвати текст, на якому він працює. Програма може аналізувати SSN   з бібліотекою. Це створює ситуацію, коли існують SSN   доступний для адміністраторів з доступом до файлів журналів.


610
2018-01-17 03:20



це саме помилкова / фальшива довідка, я розповідаю про відповідь нижче Джона, це добре відомо джерело з великою критикою. - bestsss
@ Bestass, будь ласка, ви також можете навести посилання? - user961954
@ вибачте, але String досить добре зрозуміла і як вона поводиться в JVM ... Є хороші причини для використання char[] замість String при роботі з паролями безпечно. - SnakeDoc
Ще раз, хоча пароль передано як рядок з браузера на запит як "рядок" не символ? так що незалежно від того, що ви робите, це рядок, в який момент він повинен діяти і відкидатися, ніколи не зберігати в пам'яті? - Dawesi


Символьні масиви (char[]) можна очистити після використання, встановивши кожен символ на нуль, а Strings - не. Якщо хтось може як-небудь побачити образ пам'яті, він може бачити пароль у звичайному тексті, якщо використовуються рядки, але якщо char[] використовується, після очищення даних з 0, пароль є безпечним.


305
2018-01-16 14:27



Не захищено за замовчуванням. Якщо ми говоримо про веб-додаток, більшість веб-контейнерів передаватимуть пароль у HttpServletRequest об'єкт у звичайному тексті. Якщо версія JVM становить 1,6 або нижче, вона буде в просторі пермгена. Якщо це 1,7, це все одно буде читатися, поки не буде зібрано. (Коли завгодно.) - avgvstvs
@ avgvstvs: рядки не автоматично переміщуються в permgen-просторі, це стосується лише інтернованих рядків. Крім того, приміщення пермгену також підлягає збиранню сміття, лише за більш низькою ставкою. Реальне питання з промгенним простором - це фіксований розмір, що саме є причиною, чому ніхто не повинен бездумно дзвонити intern() на довільні рядки. Але ви маєте рацію в цьому String екземпляри існують у першу чергу (до збирання) і перетворення їх у char[] Масиви потім не змінюють його. - Holger
@ Холгер бачити docs.oracle.com/javase/specs/jvms/se6/html/...  "В іншому випадку створюється новий екземпляр класу String, що містить послідовність символів Unicode, заданих структурою CONSTANT_String_info; цей екземпляр класу є результатом виведення рядка буквами. Нарешті, викликається метод intern для нового екземпляра String". У 1.6, JVM буде викликати інтерна для вас, коли він виявив однакові послідовності. - avgvstvs
@ Холгер, ви правильно, я зібрав постійний басейн і рядок пулу, але це також помилково, що промгенний простір тільки застосовується до інтернованих струн. До 1.7, як constant_pool, так і string_pool перебували в permgen просторі. Це означає, що єдиний клас струн, який був виділений для купи, як ви сказали, new String() або StringBuilder.toString()  Я керував програмами з великою кількістю констант струменя, і в результаті у нас було багато повзучості permgen. До 1.7. - avgvstvs
@avgvstvs: ну, строкові константи, як і JLS-мандати, завжди інтерновані, отже, твердження про те, що інтерновані рядки опинилися в просторі permgen, застосовуються до строкових констант неявно. Різниця полягає лише в тому, що в пермгенному просторі вперше створені константи рядка, тоді як виклик intern() на довільному рядку може викликати виділення еквівалентної рядки в промженні permgen. Останній міг отримати GC'ed, якщо не було жодного літерального рядка того самого вмісту, який поділяє цей об'єкт ... - Holger


Деякі люди вважають, що вам доведеться перезаписати пам'ять, яка використовується для зберігання пароля, коли ви більше цього не потребуєте. Це зменшує часовий проміжок часу, коли зловмисник повинен прочитати пароль у вашій системі, і повністю ігнорує той факт, що атакуючий вже потребує достатнього доступу до викрадення пам'яті JVM, щоб це зробити. Зловмисник з тим великим доступом може зловити ваші ключові події, що робить це абсолютно марним (AFAIK, тому, будь ласка, виправте мене, якщо я помиляюся).

Оновити

Завдяки коментарям я повинен оновити мою відповідь. Очевидно, є два випадки, коли це може додати (дуже) незначне покращення безпеки, оскільки це зменшує час пароля може приземлитися на жорсткому диску. Тим не менш, я думаю, що це надмірне використання для більшості випадків використання.

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

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


183
2018-01-16 14:32



Я заміню "повністю марним" на "лише незначне підвищення безпеки". Наприклад, ви можете отримати доступ до дампа пам'яті, якщо ви, можливо, прочитали доступ до каталогу tmp, погано налаштовується машину та збій у вашій програмі. У такому випадку ви не зможете встановити кейлоггер, але ви міг проаналізувати основний звалище. - Joachim Sauer
Видалення незашифрованих даних із пам'яті, як тільки ви закінчите, вважається найкращою практикою не тому, що це надійно (це не так); а тому, що він зменшує рівень впливу вашої загрози. Завдяки цьому не заважають атаці в реальному часі; але тому, що він слугує інструментом пом'якшення шкоди, суттєво зменшує кількість даних, які виявляються при зворотній атаці на знімок пам'яті (наприклад, копію пам'яті додатків, яка була записана в файл підкачки або яку було знято з пам'яті від працюючого сервера і перемістився до іншого, перш ніж його стан не вдалося). - Dan Neely
Я схильний погодитися з ставленням до цієї відповіді. Я б ризикнув запропонувати, що більшість порушень безпеки наслідків виникають на набагато більш високому рівні абстракції, ніж біти в пам'яті. Звичайно, існують, ймовірно, сценарії для гіперзахищених систем захисту, де це може викликати серйозне занепокоєння, але серйозно мислити на цьому рівні - це надмірне використання 99% програм, де використовуються .NET або Java (як це пов'язано з збиранням сміття). - kingdango
Після розповсюдження серцевої пам'яті серверної пам'яті, виявлення паролів, я замінив рядок "лише незначним поліпшенням безпеки" на "абсолютно необхідним, щоб не використовувати String для паролів, а замість цього використовувати символ []". - Peter vdL
@PetervdL серцебиття дозволяло лише читання певної повторної колекції буферів (використовується як для критичних даних безпеки, так і для мережевого вводу / виводу, без взаємозв'язку між ними - з міркувань продуктивності), ви не можете об'єднати це з Java String, оскільки вони за дизайном не можуть бути повторно використані . Також ви не можете прочитати у випадковій пам'яті за допомогою Java, щоб отримати вміст String. Проблеми мови та дизайну, що призводять до серцебиття, просто неможливі за допомогою Java Strings. - josefx


  1. Струни незмінні в Java, якщо ви зберігаєте пароль у вигляді простого тексту, він буде доступний у пам'яті, поки збирач сміття не очистить його, а оскільки рядки використовуються в басейні String для повторного використання, існує досить висока ймовірність того, що він залишатиметься пам'яттю на тривалий термін, що створює загрозу безпеки. Оскільки кожен, хто має доступ до дамп пам'яті, може знайти пароль у вигляді чіткого тексту
  2. Рекомендація Java використовуючи getPassword() метод JPasswordField, який повертає символ [] і застарілий 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 [] не достатньо, ви повинні стерти вміст, щоб бути більш безпечним. Я також пропоную працювати з хешованим або зашифрованим паролем замість звичайного тексту та очищення його від пам'яті, як тільки буде завершено автентифікацію.


117
2017-12-27 20:22



"Оскільки кожен, хто має доступ до дамп пам'яті, може знайти пароль у вигляді чіткого тексту" Як я можу отримати доступ до дампа пам'яті і знайти там пароль? - Mohit Kanwar
@MohitKanwar Тригер дампа пам'яті за допомогою jmap або jvisualvm. Якщо ви встановите помилку "пам'ять про помилку", ви зможете викликати віддалений завантаження, виникла ситуація з пам'яттю. Після того, як ви маєте дамп, ви можете пройти його за допомогою інструмента аналізатора jvisualvm або eclipse або інструментів командного рядка та перевірити всі об'єкти та їх значення. - Ryan


Я не думаю, що це дійсна пропозиція, але я можу принаймні здогадатися з причини.

Я думаю, що мотивація полягає в тому, щоб бути впевненим у тому, що ви можете стерти всі сліди пароля в пам'яті якнайшвидше і з упевненістю після його використання. З char[] ви можете переписати кожен елемент масиву порожнім чи щось напевно. Ви не можете редагувати внутрішню цінність a String цей шлях.

Але це одна не є правильною відповіддю; чому б не просто переконатися, що посилання на char[] або String не втекти? Тоді немає проблем із безпекою. Але справа така String об'єкти можуть бути intern()в теоретичному плані і зберігається в постійному басейні. Я думаю, що використовую char[] забороняє цю можливість.


72
2018-01-16 14:27



Я б не сказав, що проблема полягає в тому, що ваші посилання будуть або не "втекти". Це просто, що рядки залишаться незміненими в пам'яті протягом деякого додаткового часу, в той час як char[] може бути змінена, і тоді це не має значення, чи збирається він чи ні. І оскільки строку інтернування потрібно робити явно для не літералів, це те ж саме, що говорити про те, що a char[] може бути посилання на статичне поле. - Groo
Чи не є паролем у пам'яті як рядок з публікації форми? - Dawesi