Pertanyaan Mengapa char [] lebih disukai daripada String untuk kata sandi?


Di Swing, bidang kata sandi memiliki getPassword() (kembali char[]) metode bukan yang biasa getText() (kembali String) metode. Demikian pula, saya menemukan saran untuk tidak digunakan String untuk menangani kata sandi.

Kenapa String menimbulkan ancaman terhadap keamanan ketika datang ke kata sandi? Rasanya tidak nyaman untuk digunakan char[].


2894
2018-01-16 14:20


asal


Jawaban:


String tidak dapat diubah. Itu artinya setelah Anda membuat String, jika proses lain dapat membuang memori, tidak ada cara (selain dari refleksi) Anda dapat menyingkirkan data sebelumnya pengumpulan sampah tendangan masuk

Dengan larik, Anda dapat menghapus data secara eksplisit setelah Anda selesai melakukannya. Anda dapat menimpa array dengan apa pun yang Anda suka, dan kata sandi tidak akan ada di mana pun dalam sistem, bahkan sebelum pengumpulan sampah.

Jadi ya, ini aku s masalah keamanan - tetapi bahkan menggunakan char[] hanya mengurangi jendela peluang bagi penyerang, dan itu hanya untuk jenis serangan khusus ini.

Seperti yang tercantum dalam komentar, mungkin array yang dipindahkan oleh pengumpul sampah akan meninggalkan salinan data yang tersesat di memori. Saya percaya ini adalah implementasi khusus - garbage collector mungkin bersihkan semua memori saat berjalan, untuk menghindari hal semacam ini. Bahkan jika itu terjadi, masih ada waktu selama itu char[] berisi karakter yang sebenarnya sebagai jendela serangan.


3714
2018-01-16 14:26



Sementara saran lain di sini tampak valid, ada satu alasan bagus lainnya. Dengan polos String Anda memiliki peluang lebih tinggi tidak sengaja mencetak kata sandi ke log, monitor atau tempat tidak aman lainnya. char[] kurang rentan.

Pertimbangkan ini:

public static void main(String[] args) {
    Object pw = "Password";
    System.out.println("String: " + pw);

    pw = "Password".toCharArray();
    System.out.println("Array: " + pw);
}

Cetakan:

String: Password
Array: [C@5829428e

1078
2018-01-16 19:37



Untuk mengutip dokumen resmi, the Panduan Arsitektur Kriptografi Java mengatakan ini tentang char[] vs. String kata sandi (tentang enkripsi berbasis kata sandi, tetapi ini lebih umum tentang kata sandi tentu saja):

Tampaknya logis untuk mengumpulkan dan menyimpan kata sandi dalam suatu objek   tipe java.lang.String. Namun, inilah peringatannya: Objects dari   mengetik String tidak dapat diubah, yaitu, tidak ada metode yang mendefinisikan itu   memungkinkan Anda untuk mengubah (menimpa) atau menghapus isi dari a String   setelah digunakan. Fitur ini membuat String benda yang tidak cocok untuk   menyimpan informasi sensitif keamanan seperti kata sandi pengguna. Kamu   harus selalu mengumpulkan dan menyimpan informasi sensitif keamanan di a    char larik saja.

Panduan 2-2 Panduan Coding Aman untuk Bahasa Pemrograman Java, Versi 4.0 juga mengatakan sesuatu yang serupa (meskipun aslinya dalam konteks penebangan):

Panduan 2-2: Jangan mencatat informasi yang sangat sensitif

Beberapa informasi, seperti nomor Jaminan Sosial (SSN) dan   kata sandi, sangat sensitif. Informasi ini tidak boleh disimpan   lebih lama dari yang diperlukan atau di mana ia dapat dilihat, bahkan oleh   administrator. Misalnya, itu tidak boleh dikirim ke file log dan   kehadirannya tidak boleh terdeteksi melalui pencarian. Beberapa sementara   data dapat disimpan dalam struktur data yang bisa berubah, seperti array char, dan   dibersihkan segera setelah digunakan. Kliring struktur data telah berkurang   keefektifan pada sistem runtime Java yang khas saat objek dipindahkan   memori secara transparan kepada programmer.

Pedoman ini juga memiliki implikasi untuk implementasi dan penggunaan   perpustakaan tingkat rendah yang tidak memiliki pengetahuan semantik dari data   mereka berurusan dengan. Sebagai contoh, parsing string tingkat rendah   perpustakaan dapat mencatat teks kerjanya. Aplikasi dapat mengurai SSN   dengan perpustakaan. Ini menciptakan situasi di mana SSN berada   tersedia untuk administrator dengan akses ke file-file log.


610
2018-01-17 03:20



Susunan karakter (char[]) dapat dihapus setelah digunakan dengan mengatur setiap karakter ke nol dan Strings tidak. Jika seseorang dapat melihat gambar memori, mereka dapat melihat kata sandi dalam teks biasa jika String digunakan, tetapi jika char[] digunakan, setelah membersihkan data dengan 0, kata sandi aman.


305
2018-01-16 14:27



Sebagian orang percaya bahwa Anda harus menimpa memori yang digunakan untuk menyimpan kata sandi setelah Anda tidak lagi membutuhkannya. Ini mengurangi jendela waktu penyerang harus membaca kata sandi dari sistem Anda dan sepenuhnya mengabaikan fakta bahwa penyerang sudah membutuhkan akses yang cukup untuk membajak memori JVM untuk melakukan hal ini. Seorang penyerang dengan banyak akses dapat menangkap peristiwa kunci Anda membuat ini benar-benar tidak berguna (AFAIK, jadi tolong perbaiki saya jika saya salah).

Memperbarui

Terima kasih atas komentar yang saya miliki untuk memperbarui jawaban saya. Rupanya ada dua kasus di mana ini dapat menambahkan perbaikan keamanan (sangat) kecil karena mengurangi waktu password bisa mendarat di hard drive. Masih saya pikir itu berlebihan untuk sebagian besar kasus penggunaan.

  • Sistem target Anda mungkin tidak dikonfigurasi dengan baik atau Anda harus mengasumsikannya dan Anda harus paranoid tentang dump inti (bisa berlaku jika sistem tidak dikelola oleh administrator).
  • Perangkat lunak Anda harus terlalu paranoid untuk mencegah kebocoran data dengan penyerang mendapatkan akses ke perangkat keras - menggunakan hal-hal seperti TrueCrypt (tidak dilanjutkan), VeraCrypt, atau CipherShed.

Jika memungkinkan, menonaktifkan core dumps dan file swap akan mengatasi kedua masalah. Namun, mereka akan membutuhkan hak administrator dan dapat mengurangi fungsi (kurang menggunakan memori) dan menarik RAM dari sistem yang sedang berjalan masih akan menjadi perhatian yang valid.


183
2018-01-16 14:32



  1. String tidak dapat diubah di Jawa jika Anda menyimpan kata sandi sebagai teks biasa, itu akan tersedia dalam memori sampai Pengumpul sampah membersihkannya dan karena String digunakan dalam kolam String untuk digunakan kembali, kemungkinan besar kemungkinan akan tetap tersimpan dalam memori untuk durasi yang lama, yang menimbulkan ancaman keamanan. Karena siapa pun yang memiliki akses ke dump memori dapat menemukan kata sandi dalam teks yang jelas
  2. Rekomendasi Java menggunakan getPassword() metode JPasswordField yang mengembalikan char [] dan tidak digunakan lagi getText() metode yang mengembalikan kata sandi dalam teks jelas yang menyatakan alasan keamanan.
  3. toString () selalu ada risiko pencetakan teks biasa di file log atau konsol tetapi jika menggunakan Array Anda tidak akan mencetak isi dari array, bukan lokasi memorinya dicetak.

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

    Kata sandi string: passwd

    Kata sandi karakter: [C @ 110b2345

Pemikiran akhir: Meskipun menggunakan char [] tidak cukup Anda perlu menghapus konten agar lebih aman. Saya juga menyarankan untuk bekerja dengan sandi yang di-hash atau dienkripsi bukan teks biasa dan menghapusnya dari memori segera setelah otentikasi selesai.


117
2017-12-27 20:22



Saya tidak berpikir ini adalah saran yang valid, tapi, saya setidaknya bisa menebak alasannya.

Saya pikir motivasi ingin memastikan bahwa Anda dapat menghapus semua jejak kata sandi dalam memori segera dan dengan pasti setelah digunakan. Dengan char[] Anda bisa menimpa setiap elemen dari array dengan kosong atau sesuatu yang pasti. Anda tidak dapat mengedit nilai internal a String seperti itu.

Tapi itu saja bukan jawaban yang bagus; mengapa tidak hanya memastikan referensi ke char[] atau String tidak melarikan diri? Maka tidak ada masalah keamanan. Tapi masalahnya adalah itu String benda bisa intern()ed dalam teori dan terus hidup di dalam kolam konstan. Saya kira menggunakan char[] melarang kemungkinan ini.


72
2018-01-16 14:27



Jawabannya sudah diberikan, tapi saya ingin berbagi masalah yang saya temukan akhir-akhir ini dengan pustaka standar Java. Sementara mereka berhati-hati sekarang mengganti string kata sandi dengan char[] di mana-mana (yang tentu saja merupakan hal yang baik), data keamanan penting lainnya tampaknya diabaikan ketika datang untuk membersihkannya dari memori.

Saya sedang memikirkan mis. itu PrivateKey kelas. Pertimbangkan skenario di mana Anda akan memuat kunci RSA pribadi dari file PKCS # 12, menggunakannya untuk melakukan beberapa operasi. Sekarang dalam hal ini, mengendus kata sandi saja tidak akan banyak membantu Anda selama akses fisik ke file kunci dibatasi dengan benar. Sebagai penyerang, Anda akan jauh lebih baik jika Anda langsung mendapatkan kunci alih-alih kata sandi. Informasi yang diinginkan dapat dibocorkan manifold, core dumps, sesi debugger atau file swap hanyalah beberapa contoh.

Dan ternyata, tidak ada yang memungkinkan Anda menghapus informasi pribadi dari PrivateKey dari memori, karena tidak ada API yang memungkinkan Anda menghapus byte yang membentuk informasi yang sesuai.

Ini adalah situasi yang buruk, karena ini kertas menjelaskan bagaimana keadaan ini berpotensi dieksploitasi.

Perpustakaan OpenSSL misalnya menimpa bagian memori penting sebelum kunci privat dibebaskan. Karena Java adalah sampah yang dikumpulkan, kita perlu metode eksplisit untuk menghapus dan membatalkan informasi pribadi untuk kunci Java, yang harus diterapkan segera setelah menggunakan kunci.


58
2018-01-19 19:39



Seperti dinyatakan Jon Skeet, tidak ada cara kecuali dengan menggunakan refleksi.

Namun, jika refleksi adalah pilihan untuk Anda, Anda dapat melakukan ini.

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

ketika dijalankan

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

Catatan: jika char String [] telah disalin sebagai bagian dari siklus GC, ada kemungkinan salinan sebelumnya ada di suatu tempat dalam memori.

Salinan lama ini tidak akan muncul di timbunan sampah, tetapi jika Anda memiliki akses langsung ke memori mentah dari proses yang Anda bisa melihatnya. Secara umum Anda harus menghindari siapa pun yang memiliki akses tersebut.


41
2017-07-08 09:26