Kapan saya membutuhkan SecureString di .NET?

179

Saya mencoba untuk memahami tujuan SecureString .NET. Dari MSDN:

Sebuah instance dari kelas System.String keduanya tidak berubah dan, ketika tidak lagi diperlukan, tidak dapat dijadwalkan secara program untuk pengumpulan sampah; Yaitu, instance hanya-baca setelah dibuat dan tidak mungkin untuk memprediksi kapan instance akan dihapus dari memori komputer. Akibatnya, jika objek String berisi informasi sensitif seperti kata sandi, nomor kartu kredit, atau data pribadi, ada risiko informasi tersebut dapat diungkapkan setelah digunakan karena aplikasi Anda tidak dapat menghapus data dari memori komputer.

Objek SecureString mirip dengan objek String karena memiliki nilai teks. Namun, nilai objek SecureString dienkripsi secara otomatis, dapat dimodifikasi hingga aplikasi Anda menandainya sebagai hanya-baca, dan dapat dihapus dari memori komputer oleh aplikasi Anda atau pengumpul sampah .NET Framework.

Nilai instance dari SecureString secara otomatis dienkripsi ketika instance diinisialisasi atau ketika nilai diubah. Aplikasi Anda dapat membuat instance tidak dapat diubah dan mencegah modifikasi lebih lanjut dengan menggunakan metode MakeReadOnly.

Apakah enkripsi otomatis merupakan hadiah besar?

Dan mengapa saya tidak bisa mengatakan:

SecureString password = new SecureString("password");

dari pada

SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
    pass.AppendChar(c);

Aspek SecureString apa yang saya lewatkan?

Richard Morgan
sumber
3
11 tahun kemudian dan MS tidak lagi merekomendasikan SecureStringuntuk pengembangan baru: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
Matt Thomas

Jawaban:

4

Saya akan berhenti menggunakan SecureString. Sepertinya orang-orang PG menjatuhkan dukungan untuk itu. Bahkan mungkin menariknya di masa mendatang - https://github.com/dotnet/apireviews/tree/master/2015-07-07-securestring .

Kita harus menghapus enkripsi dari SecureString di semua platform di. Core NET - Kita harus usang SecureString - Kita mungkin tidak boleh mengekspos SecureString di .NET Core

Joe Healy
sumber
1
Tautan sudah mati tetapi tampaknya melanjutkan: docs.microsoft.com/en-us/dotnet/api/… .. dengan beberapa panduan yang sangat lemah pada jalur maju execpt "jangan gunakan kredensial" - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. jangan Anda berani menggunakan kata sandi untuk melindungi kunci pribadi sertifikat Anda juga!
felickz
1
Setelah 11 tahun, jawaban ini sekarang menjadi jawaban yang 'baru' yang benar. Tautan tampaknya menjadi basi tetapi bimbingan dari MS adalah: SecureString tidak boleh digunakan
Richard Morgan
109

Beberapa bagian dari kerangka kerja yang saat ini digunakan SecureString:

Tujuan utamanya adalah mengurangi permukaan serangan, bukan menghilangkannya. SecureStrings"disematkan" dalam RAM sehingga Pengumpul Sampah tidak akan memindahkannya atau membuat salinannya. Ini juga memastikan teks biasa tidak akan ditulis ke file Swap atau di dump inti. Enkripsi lebih seperti kebingungan dan tidak akan menghentikan peretas, yang dapat menemukan kunci simetris yang digunakan untuk mengenkripsi dan mendekripsi.

Seperti yang dikatakan orang lain, alasan Anda harus membuat SecureStringkarakter-demi-karakter adalah karena kesalahan pertama yang jelas dalam melakukan sebaliknya: Anda mungkin sudah memiliki nilai rahasia sebagai string polos, jadi apa gunanya?

SecureStringIni adalah langkah pertama dalam menyelesaikan masalah Ayam-dan-Telur, jadi meskipun sebagian besar skenario saat ini mengharuskan untuk mengubahnya kembali menjadi string reguler untuk memanfaatkannya sama sekali, keberadaan mereka dalam kerangka kerja sekarang berarti dukungan yang lebih baik bagi mereka dalam masa depan - setidaknya ke titik di mana program Anda tidak harus menjadi tautan yang lemah.

Chris Wenham
sumber
2
Saya berlari melintasinya melihat properti Password ProcessStartInfo; bahkan tidak memperhatikan jenisnya, saya hanya mengaturnya ke string biasa sampai compiler membentak saya.
Richard Morgan
Tidak akan mudah untuk menemukan kunci enkripsi simetris, karena SecureString didasarkan pada DPAPI, yang tidak persis menyimpan kunci dalam plaintext ...
AviD
1
Juga, ini bukan masalah ayam-dan-telur, karena ini bukan pengganti untuk enkripsi dalam penyimpanan - tetapi merupakan solusi untuk string NET.
AviD
2
"Kamu mungkin sudah memiliki nilai rahasia sebagai string biasa, jadi apa gunanya?" Apakah ada jawaban untuk pertanyaan ini? Sepertinya ini adalah solusi yang "lebih baik daripada tidak sama sekali" jika Anda bermaksud menyimpan kata sandi yang tersimpan dalam memori untuk jangka waktu lama.
xr280xr
2
Bagaimana dengan memiliki beberapa contoh kode sederhana penggunaan? Saya percaya saya akan lebih memahami bagaimana dan kapan menggunakannya.
codea
37

Sunting : Jangan gunakan SecureString

Bimbingan saat ini sekarang mengatakan bahwa kelas tidak boleh digunakan. Detailnya dapat ditemukan di tautan ini: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Dari artikel:

DE0001: SecureString tidak boleh digunakan

Motivasi

  • Tujuannya SecureStringadalah untuk menghindari menyimpan rahasia dalam memori proses sebagai teks biasa.
  • Namun, bahkan di Windows, SecureStringtidak ada sebagai konsep OS.
    • Itu hanya membuat jendela memperpendek teks biasa; itu tidak sepenuhnya mencegahnya sebagai. NET masih harus mengubah string ke representasi teks biasa.
    • Manfaatnya adalah bahwa representasi teks biasa tidak bertahan sebagai contoh System.String- masa pakai buffer asli lebih pendek.
  • Isi array tidak dienkripsi kecuali pada .NET Framework.
    • Dalam .NET Framework, konten array char internal dienkripsi. .NET tidak mendukung enkripsi di semua lingkungan, baik karena API yang hilang atau masalah manajemen utama.

Rekomendasi

Jangan gunakan SecureStringuntuk kode baru. Saat porting kode ke .NET Core, pertimbangkan bahwa isi array tidak dienkripsi dalam memori.

Pendekatan umum berurusan dengan kredensial adalah untuk menghindarinya dan sebaliknya mengandalkan cara lain untuk mengotentikasi, seperti sertifikat atau otentikasi Windows.

Edit Akhir: Ringkasan Asli di bawah ini

Banyak jawaban bagus; inilah sinopsis singkat dari apa yang telah dibahas.

Microsoft telah mengimplementasikan kelas SecureString dalam upaya memberikan keamanan yang lebih baik dengan informasi sensitif (seperti kartu kredit, kata sandi, dll.). Secara otomatis menyediakan:

  • enkripsi (dalam kasus dump memori atau caching halaman)
  • menyematkan memori
  • kemampuan untuk menandai sebagai hanya-baca (untuk mencegah modifikasi lebih lanjut)
  • konstruksi aman dengan TIDAK mengizinkan string konstan untuk dilewatkan

Saat ini, SecureString terbatas digunakan tetapi mengharapkan adopsi yang lebih baik di masa depan.

Berdasarkan informasi ini, konstruktor dari SecureString tidak boleh hanya mengambil string dan mengirisnya ke char array karena memiliki string yang dieja mengalahkan tujuan SecureString.

Informasi tambahan:

  • Sebuah posting dari blog .NET Security berbicara hampir sama dengan yang dibahas di sini.
  • Dan satu lagi meninjau kembali dan menyebutkan alat yang BISA membuang konten SecureString.

Sunting: Saya merasa sulit untuk memilih jawaban terbaik karena ada banyak informasi bagus; Sayang sekali tidak ada opsi jawaban yang dibantu.

Richard Morgan
sumber
19

Jawaban singkat

mengapa saya tidak bisa mengatakan:

SecureString password = new SecureString("password");

Karena sekarang Anda ada passworddalam memori; tanpa ada cara untuk menghapusnya - yang merupakan titik aman dari SecureString .

Jawaban panjang

Alasan SecureString ada adalah karena Anda tidak dapat menggunakan ZeroMemory untuk menghapus data sensitif ketika Anda selesai menggunakannya. Itu ada untuk memecahkan masalah yang ada karena CLR.

Dalam aplikasi asli biasa Anda akan menelepon SecureZeroMemory:

Mengisi satu blok memori dengan nol.

Catatan : SecureZeroMemory identik dengan ZeroMemory, kecuali kompiler tidak akan mengoptimalkannya.

Masalahnya adalah bahwa Anda tidak dapat memanggil ZeroMemoryatau SecureZeroMemorydi dalam. NET. Dan dalam string .NET tidak berubah; Anda bahkan tidak bisa menimpa isi string seperti yang dapat Anda lakukan dalam bahasa lain:

//Wipe out the password
for (int i=0; i<password.Length; i++)
   password[i] = \0;

Jadi apa yang bisa kamu lakukan? Bagaimana kami menyediakan kemampuan dalam .NET untuk menghapus kata sandi, atau nomor kartu kredit dari memori ketika kami selesai melakukannya?

Satu-satunya cara yang bisa dilakukan adalah menempatkan string di beberapa blok memori asli , di mana Anda dapat menelepon ZeroMemory. Objek memori asli seperti:

  • sebuah BSTR
  • sebuah HGLOBAL
  • Memori tidak dikelola CoTaskMem

SecureString memberikan kembali kemampuan yang hilang

Di .NET, Strings tidak dapat dihapus ketika Anda selesai menggunakannya:

  • mereka tidak berubah; Anda tidak dapat menimpa isinya
  • kamu tidak bisa Disposedari mereka
  • pembersihan mereka adalah pada belas kasihan pemulung

SecureString ada sebagai cara untuk membagikan keamanan string, dan dapat menjamin pembersihan mereka saat Anda membutuhkannya.

Anda mengajukan pertanyaan:

mengapa saya tidak bisa mengatakan:

SecureString password = new SecureString("password");

Karena sekarang Anda ada passworddalam memori; tanpa cara untuk menghapusnya. Terjebak di sana sampai CLR memutuskan untuk menggunakan kembali memori itu. Anda telah menempatkan kami kembali di tempat kami mulai; aplikasi yang berjalan dengan kata sandi yang tidak dapat kami singkirkan, dan tempat penyimpanan memori (atau Pemantau Proses) dapat melihat kata sandi.

SecureString menggunakan API Perlindungan Data untuk menyimpan string yang dienkripsi dalam memori; dengan cara itu string tidak akan ada di swapfiles, crash dumps, atau bahkan di jendela variabel lokal dengan seorang kolega yang melihat Anda seharusnya.

Bagaimana saya membaca kata sandi?

Lalu pertanyaannya: bagaimana cara saya berinteraksi dengan string? Anda benar - benar tidak menginginkan metode seperti:

String connectionString = secureConnectionString.ToString()

karena sekarang Anda segera kembali ke tempat Anda memulai - kata sandi yang tidak dapat Anda singkirkan. Anda ingin memaksa pengembang untuk menangani string sensitif dengan benar - sehingga dapat dihapus dari memori.

Itulah sebabnya .NET menyediakan tiga fungsi penolong yang berguna untuk menyusun SecureString menjadi memori yang tidak dikelola:

Anda mengubah string menjadi gumpalan memori yang tidak dikelola, menanganinya, dan kemudian menghapusnya lagi.

Beberapa API menerima SecureStrings . Sebagai contoh di ADO.net 4.5 SqlConnection.Credential mengambil set SqlCredential :

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

Anda juga dapat mengubah kata sandi dalam String Koneksi:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

Dan ada banyak tempat di dalam. NET di mana mereka terus menerima String polos untuk tujuan kompatibilitas, kemudian dengan cepat mengubahnya menjadi SecureString.

Bagaimana cara memasukkan teks ke dalam SecureString?

Ini masih menyisakan masalah:

Bagaimana cara saya mendapatkan kata sandi ke dalam SecureString?

Ini tantangannya, tetapi intinya adalah membuat Anda memikirkan keamanan.

Terkadang fungsionalitas sudah disediakan untuk Anda. Misalnya, kontrol WPF PasswordBox dapat mengembalikan kata sandi yang Anda masukkan sebagai SecureString secara langsung:

Kata SandiBox. Properti Kata sandi Aman

Mendapat kata sandi yang saat ini dipegang oleh PasswordBox sebagai SecureString .

Ini membantu karena di mana-mana Anda dulu menyebarkan string mentah, Anda sekarang memiliki sistem tipe yang mengeluh bahwa SecureString tidak kompatibel dengan String. Anda ingin pergi selama mungkin sebelum harus mengubah SecureString Anda kembali ke string biasa.

Mengonversi SecureString cukup mudah:

  • SecureStringToBSTR
  • PtrToStringBSTR

seperti dalam:

private static string CreateString(SecureString secureString)
{
    IntPtr intPtr = IntPtr.Zero;
    if (secureString == null || secureString.Length == 0)
    {
        return string.Empty;
    }
    string result;
    try
    {
        intPtr = Marshal.SecureStringToBSTR(secureString);
        result = Marshal.PtrToStringBSTR(intPtr);
    }
    finally
    {
        if (intPtr != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(intPtr);
        }
    }
    return result;
}

Mereka hanya benar-benar tidak ingin Anda melakukannya.

Tapi bagaimana cara mendapatkan string ke SecureString? Yang perlu Anda lakukan adalah berhenti memasukkan kata sandi dalam sebuah String . Anda perlu memilikinya di sesuatu yang lain. Bahkan sebuah Char[]array akan sangat membantu.

Saat itulah Anda dapat menambahkan setiap karakter dan menghapus plaintext ketika Anda selesai:

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

Anda perlu kata sandi Anda disimpan di beberapa memori yang dapat Anda hapus. Masukkan ke dalam SecureString dari sana.


tl; dr: SecureString ada untuk memberikan yang setara dengan ZeroMemory .

Beberapa orang tidak melihat gunanya menghapus kata sandi pengguna dari memori ketika perangkat terkunci , atau menghapus penekanan tombol dari memori setelah mereka diautentikasi . Orang-orang itu tidak menggunakan SecureString.

Ian Boyd
sumber
14

Ada beberapa skenario di mana Anda dapat menggunakan SecureString dengan bijaksana dalam versi Framework saat ini. Ini benar-benar hanya berguna untuk berinteraksi dengan API yang tidak dikelola - Anda dapat menyusunnya menggunakan Marshal.SecureStringToGlobalAllocUnicode.

Segera setelah Anda mengonversinya ke / dari System.String, Anda telah mengalahkan tujuannya.

Sampel MSDN menghasilkan SecureString karakter sekaligus dari input konsol dan meneruskan string aman ke API yang tidak dikelola. Ini agak berbelit-belit dan tidak realistis.

Anda mungkin mengharapkan versi .NET masa depan untuk memiliki lebih banyak dukungan untuk SecureString yang akan membuatnya lebih bermanfaat, misalnya:

  • SecureString Console.ReadLineSecure () atau serupa dengan membaca input konsol ke SecureString tanpa semua kode berbelit-belit dalam sampel.

  • Penggantian WinForms TextBox yang menyimpan properti TextBox.Text sebagai string yang aman sehingga kata sandi dapat dimasukkan dengan aman.

  • Ekstensi ke API terkait keamanan untuk memungkinkan kata sandi diteruskan sebagai SecureString.

Tanpa hal di atas, SecureString akan memiliki nilai terbatas.

Joe
sumber
12

Saya percaya alasan mengapa Anda harus melakukan penambahan karakter alih-alih satu instantiasi datar adalah karena di latar belakang meneruskan "kata sandi" ke konstruktor SecureString menempatkan bahwa "kata sandi" string dalam memori mengalahkan tujuan dari string aman.

Dengan menambahkan Anda hanya menempatkan karakter pada satu waktu ke memori yang mungkin tidak berdekatan satu sama lain secara fisik membuatnya lebih sulit untuk merekonstruksi string asli. Saya bisa saja salah di sini, tetapi begitulah dijelaskan kepada saya.

Tujuan kelas adalah untuk mencegah data yang aman dari terkena melalui dump memori atau alat serupa.

JoshReedSchramm
sumber
11

MS menemukan bahwa pada contoh tertentu menyebabkan server (desktop, apa pun) untuk crash ada kalanya lingkungan runtime akan melakukan dump memori yang mengekspos isi dari apa yang ada di memori. Secure String mengenkripsi itu dalam memori untuk mencegah penyerang bisa mengambil konten string.

kemiller2002
sumber
5

Salah satu manfaat besar dari SecureString adalah bahwa ia seharusnya menghindari kemungkinan data Anda disimpan ke disk karena caching halaman. Jika Anda memiliki kata sandi di memori dan kemudian memuat program besar atau kumpulan data, kata sandi Anda mungkin akan ditulis ke file swap saat program Anda paging kehabisan memori. Dengan SecureString, setidaknya data tidak akan berada di disk Anda tanpa batas dalam teks yang jelas.

Jason Z
sumber
4

Saya kira itu karena string dimaksudkan untuk menjadi aman, yaitu seorang hacker seharusnya tidak dapat membacanya. Jika Anda menginisialisasi dengan string, peretas dapat membaca string asli.

OregonGhost
sumber
4

Nah, seperti yang dinyatakan deskripsi, nilai disimpan terenkripsi, dengan berarti bahwa dump memori proses Anda tidak akan mengungkapkan nilai string (tanpa kerja yang cukup serius).

Alasan Anda tidak bisa hanya membangun SecureString dari string konstan adalah karena Anda akan memiliki versi string yang tidak terenkripsi dalam memori. Membatasi Anda untuk membuat string menjadi beberapa bagian mengurangi risiko memiliki seluruh string dalam memori sekaligus.

Mark Bessey
sumber
2
Jika mereka membatasi konstruksi dari string konstan, maka baris foreach (char c dalam "password" .ToCharArray ()) akan mengalahkan itu, bukan? Itu harus lulus. AppChar ('p'); pass.AppendChar ('a'); dll?
Richard Morgan
Ya, Anda dapat dengan mudah membuang apa yang diberikan sedikit perlindungan SecureString kepada Anda. Mereka berusaha membuatnya susah untuk benar-benar menembak diri sendiri di kaki. Jelas harus ada beberapa cara untuk mendapatkan nilai masuk dan keluar dari SecureString, atau Anda tidak dapat menggunakannya untuk apa pun.
Mark Bessey
1

Kasus penggunaan lain adalah ketika Anda bekerja dengan aplikasi pembayaran (POS) dan Anda tidak bisa menggunakan struktur data yang tidak dapat diubah untuk menyimpan data sensitif karena Anda adalah pengembang yang cermat. Misalnya: jika saya akan menyimpan data kartu sensitif atau metadata otorisasi ke string yang tidak dapat diubah, selalu ada kasus ketika data ini akan tersedia dalam memori untuk waktu yang lama setelah dibuang. Saya tidak bisa begitu saja menimpanya. Keuntungan besar lainnya di mana data sensitif seperti itu disimpan dalam memori terenkripsi.

Roma
sumber