Ne zaman .NET'te bir SecureString gerekir?


179

Ben .NET'in SecureString amacı grok çalışıyorum. MSDN'den:

System.String sınıfının bir örneği hem değiştirilemez hem de artık gerekmediğinde, çöp toplama için programlı olarak planlanamaz; yani, örnek oluşturulduktan sonra salt okunurdur ve örneğin bilgisayar belleğinden ne zaman silineceğini tahmin etmek mümkün değildir. Sonuç olarak, bir String nesnesi parola, kredi kartı numarası veya kişisel veriler gibi hassas bilgiler içeriyorsa, uygulamanız verileri bilgisayar belleğinden silemediği için bilgilerin kullanıldıktan sonra açığa çıkma riski vardır.

SecureString nesnesi, metin değerine sahip olması nedeniyle bir String nesnesine benzer. Ancak, SecureString nesnesinin değeri otomatik olarak şifrelenir, uygulamanız salt okunur olarak işaretlenene kadar değiştirilebilir ve uygulamanız veya .NET Framework çöp toplayıcısı tarafından bilgisayar belleğinden silinebilir.

SecureString örneğinin değeri, örnek başlatıldığında veya değer değiştirildiğinde otomatik olarak şifrelenir. Uygulamanız örneği değiştirilemez hale getirebilir ve MakeReadOnly yöntemini çağırarak daha fazla değişiklik yapılmasını önleyebilir.

Otomatik şifreleme büyük kazanç mıdır?

Ve neden sadece şunu söyleyemem:

SecureString password = new SecureString("password");

onun yerine

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

SecureString'in hangi yönünü kaçırıyorum?


3
11 yıl sonra ve MS artık SecureStringyeni geliştirme önermiyor
Matt Thomas

Yanıtlar:


4

SecureString kullanmayı bırakacağım. Görünüşe göre PG'liler destek veriyorlar. Gelecekte bile çekebilirsiniz - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring .

.NET Core'daki tüm platformlarda SecureString'den şifrelemeyi kaldırmalıyız - SecureString'i geçersiz kılmalıyız - .NET Core'da SecureString'i muhtemelen göstermemeliyiz


1
Bağlantı öldü, ancak devam ediyor gibi görünüyor: docs.microsoft.com/en-us/dotnet/api/… .. yol ileri yürütme "kimlik bilgilerini kullanma" konusunda bazı çok zayıf rehberlik ile - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. sertifikalarınızın özel anahtarını korumak için bir parola kullanmaya cesaret edemezsiniz!
felickz

1
11 yıl sonra, bu cevap şimdi 'yeni' doğru cevap olarak görünüyor. Bağlantılar bayat gibi gözüküyor, ancak MS'den gelen rehberlik: SecureString kullanılmamalıdır
Richard Morgan

109

Şu anda kullanılan çerçevenin bazı bölümleri SecureString:

Asıl amaç, ortadan kaldırmak yerine saldırı yüzeyini azaltmaktır. SecureStringsÇöp Toplayıcı onu hareket ettirmeyecek veya kopyalarını çıkarmayacak şekilde RAM'de "sabitlendi". Ayrıca düz metnin Swap dosyasına veya çekirdek dökümlerine yazılmamasını sağlar. Şifreleme daha çok şaşırtmaya benzer ve şifrelenmek ve şifresini çözmek için kullanılan simetrik anahtarı bulabilecek kararlı bir hacker'ı durdurmaz .

Diğerlerinin söylediği gibi, bir SecureStringkarakter karakter yaratmanızın nedeni, aksi takdirde yapmanın ilk bariz kusurundan kaynaklanmaktadır: muhtemelen zaten düz bir dize olarak gizli değere sahipsiniz, peki anlamı nedir?

SecureStringTavuk ve Yumurta problemini çözmenin ilk adımıdır, bu nedenle mevcut senaryoların çoğu, bunları herhangi bir şekilde kullanmak için bunları düzenli dizgilere dönüştürmeyi gerektirse de, çerçevedeki varlıkları artık gelecek - en azından programınızın zayıf bağlantı olması gerekmeyen bir noktaya.


2
ProcessStartInfo's Password özelliğine bakarak onun üzerinden koştu; türüne bile dikkat etmedim, derleyici havlayana kadar düzenli bir dizgeye ayarladım.
Richard Morgan

Simetrik şifreleme anahtarını bulmak kolay olmayacak, çünkü SecureString tam olarak bir metni düz metinde saklamayan
DPAPI'ye dayanıyor

1
Ayrıca, depolamadaki şifrelemenin yerini almadığı için tavuk ve yumurta problemi o kadar da değil - ama değişmez, yönetilen .NET dizeleri için bir çözüm.
AviD

2
"Muhtemelen düz bir dize olarak gizli değere sahipsiniz, peki anlamı nedir?" Bu sorunun bir cevabı var mı? Uzun bir süre bellekte saklanan bir şifreyi saklamak istiyorsanız, en iyi ihtimalle "hiç yoktan daha iyi" bir çözüm gibi görünüyor.
xr280xr

2
Birkaç basit kod kullanım örneğine sahip olmaya ne dersiniz? Nasıl ve ne zaman kullanacağımı daha iyi anlayabileceğime inanıyorum.
codea

37

Düzenleme : SecureString kullanma

Mevcut rehberlik şimdi sınıfın kullanılmaması gerektiğini söylüyor. Ayrıntılar şu bağlantıda bulunabilir: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Makaleden:

DE0001: SecureString kullanılmamalıdır

Motivasyon

  • Amaç SecureString, işlem belleğinde sırların düz metin olarak saklanmasını önlemektir.
  • Ancak, Windows'ta bile SecureStringbir işletim sistemi konsepti olarak mevcut değildir.
    • Sadece pencerenin düz metni kısaltmasını sağlar; .NET'in dizeyi düz metin gösterimine dönüştürmesi gerektiğinden tam olarak engellemez.
    • Bunun yararı, düz metin gösteriminin bir örneği olarak takılmamasıdır System.String- yerel arabellek ömrü daha kısadır.
  • Dizinin içeriği, .NET Framework dışında şifrelenmemiş.
    • .NET Framework'te, iç karakter dizisinin içeriği şifrelenir. .NET, eksik API'ler veya anahtar yönetimi sorunları nedeniyle tüm ortamlarda şifrelemeyi desteklemez.

Öneri

SecureStringYeni kod için kullanmayın . Kodu .NET Core'a taşırken, dizinin içeriğinin bellekte şifrelenmediğini düşünün.

Kimlik bilgileriyle başa çıkmanın genel yaklaşımı bunlardan kaçınmak ve bunun yerine sertifikalar veya Windows kimlik doğrulaması gibi diğer kimlik doğrulama araçlarına güvenmektir.

Düzenlemeyi Sonlandır: Aşağıdaki Orijinal Özet

Çok büyük cevaplar; işte tartışılanların kısa bir özeti.

Microsoft, SecureString sınıfını hassas bilgilerle (kredi kartları, parolalar vb.) Daha iyi güvenlik sağlamak amacıyla uyguladı. Otomatik olarak şunları sağlar:

  • şifreleme (bellek dökümü veya sayfa önbelleklemesi durumunda)
  • hafızaya sabitleme
  • salt okunur olarak işaretleme yeteneği (daha fazla değişiklik yapılmasını önlemek için)
  • sabit bir ipin geçmesine izin VERMEYEN güvenli yapı

Şu anda, SecureString kullanımı sınırlıdır, ancak gelecekte daha iyi benimsenmesini beklemektedir.

Bu bilgilere dayanarak, SecureString yapıcısı sadece bir string almalı ve stringin hecelenerek SecureString'in amacını bozduğundan onu char dizisine ayırmamalıdır.

İlave bilgi:

  • .NET Security blogunda yayınlanan bir yazı , burada ele alınanla aynı şeyden bahsediyor.
  • Bir diğeri de onu tekrar ziyaret ediyor ve SecureString'in içeriğini dökebilecek bir araçtan bahsediyor.

Düzenleme: Birçok iyi bilgi var gibi en iyi cevabı seçmek zor buldum; çok kötü, yardımlı cevap seçeneği yoktur.


19

Kısa cevap

neden sadece söyleyemem:

SecureString password = new SecureString("password");

Çünkü şimdi passwordhafızanız var; tamamen silmenin bir yolu yok - ki bu tamamen SecureString'in noktası .

Uzun cevap

SecureString'in var olmasının nedeni , ZeroMemory'yi işiniz bittiğinde hassas verileri silmek için kullanamamanızdır . CLR nedeniyle var olan bir sorunu çözmek için var .

Normal bir yerel uygulamada şunları çağırırsınız SecureZeroMemory:

Bir bellek bloğunu sıfırlarla doldurur.

Not : SecureZeroMemory ile aynıdır ZeroMemory, ancak derleyici onu optimize etmez.

Sorun olduğunu olamaz diyoruz ZeroMemoryya SecureZeroMemoryiç .NET. Ve .NET dizeleri değişmez; diğer dillerde olduğu gibi dizenin içeriğinin üzerine bile yazamazsınız :

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

Peki ne yapabilirsin? İşimiz bittiğinde .NET'te bir şifreyi veya kredi kartı numarasını bellekten silme yeteneğini nasıl sağlarız?

Bu yapılabilir tek yolu bazı dizeyi yerleştirmek olacaktır yerli bellek bloğu, olabilir o zaman diyoruz ZeroMemory. Yerel bellek nesnesi:

  • bir BSTR
  • bir HGLOBAL
  • CoTaskMem yönetilmeyen bellek

SecureString kayıp yeteneğini geri verir

.NET'te, işiniz bittiğinde Dizeler silinemez:

  • değişmezler; içeriklerinin üzerine yazamazsın
  • Disposeonlardan yapamazsın
  • temizliği çöp toplayıcının insafına

SecureString, dizelerin güvenliğini aşmanın ve gerektiğinde bunların temizlenmesini garanti edebilmenin bir yolu olarak mevcuttur.

Soruyu sordunuz:

neden sadece söyleyemem:

SecureString password = new SecureString("password");

Çünkü şimdi passwordhafızanız var; silmek için bir yol yok. CLR bu belleği yeniden kullanmaya karar verene kadar orada sıkışmış. Bizi başladığımız yere geri koydunuz; şifresini kaldıramadığımız ve bellek dökümü (veya İşlem Monitörü) şifreyi görebildiği çalışan bir uygulama.

SecureString, şifrelenmiş dizeyi bellekte depolamak için Veri Koruma API'sini kullanır; bu şekilde dize, takas dosyalarında, çökme dökümlerinde veya hatta yerel değişkenler penceresinde, bir meslektaşınızın sizin ihtiyacınıza bakmasıyla mevcut olmaz.

Şifreyi nasıl okurum?

O zaman soru şu: dize ile nasıl etkileşim kurabilirim? Kesinlikle yok bir yöntem gibi istiyorum:

String connectionString = secureConnectionString.ToString()

çünkü şimdi başladığınız yere geri döndünüz - kurtulamayacağınız bir şifre. Sen istediğiniz zorlamak doğru duyarlı dize işlemek için geliştiriciler - bu yüzden o olabilir bellekten silinmelidir.

Bu nedenle .NET, SecureString'i yönetilmeyen bir belleğe toplamak için üç kullanışlı yardımcı işlev sunar:

Dizeyi yönetilmeyen bir bellek blobuna dönüştürür, işler ve sonra yeniden silersiniz.

Bazı API'lar SecureStrings'i kabul eder . Örneğin, ADO.net 4.5'te SqlConnection.Credential bir set SqlCredential alır :

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

Ayrıca bir Bağlantı Dizesi içindeki parolayı da değiştirebilirsiniz:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

Ve .NET içinde uyumluluk amacıyla düz bir String'i kabul etmeye devam ettikleri, daha sonra hızlı bir şekilde bir SecureString'e dönüştürdüğü birçok yer var.

SecureString'e metin nasıl eklenir?

Bu hala sorunu bırakıyor:

İlk etapta SecureString'e nasıl şifre alabilirim?

Zor olan budur ama asıl mesele sizi güvenlik hakkında düşünmeye itmektir.

Bazen işlevsellik sizin için zaten sağlanmıştır. Örneğin, WPF PasswordBox denetimi size girilen şifreyi bir SecureString olarak doğrudan döndürebilir :

PasswordBox.SecurePassword Özelliği

Şu anda elinde bulunan şifreyi alır PasswordBox bir şekilde SecureString .

Bu yardımcı olur, çünkü ham bir dizenin etrafından geçtiğiniz her yerde, artık SecureString'in String ile uyumlu olmadığından şikayet eden tip sisteminiz var. SecureString'inizi normal dizeye dönüştürmek zorunda kalmadan önce olabildiğince uzun gitmek istersiniz.

Bir SecureString'i dönüştürmek yeterince kolaydır:

  • SecureStringToBSTR
  • PtrToStringBSTR

de olduğu gibi:

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

Sadece bunu yapmanı istemiyorlar.

Ancak bir dizeyi SecureString'e nasıl alabilirim? Yapmanız gereken ilk şey bir String'de bir parolaya sahip olmaktan vazgeçmektir . Başka bir şeye sahip olmanız gerekiyordu . Bir Char[]dizi bile yardımcı olacaktır.

İşte o zaman her karakteri ekleyebilir ve işiniz bittiğinde düz metni silebilirsiniz:

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

Parolanızı silebileceğiniz bir bellekte saklamanız gerekir. Buradan SecureString'e yükleyin.


tl; dr: ZeroMemory eşdeğerini sağlamak için SecureString var .

Bazı kişiler , bir cihaz kilitlendiğinde kullanıcının şifresini bellekten silmenin veya kimlik doğrulaması yapıldıktan sonra tuş vuruşlarını bellekten silmenin önemini görmez . Bu kişiler SecureString kullanmıyor.


14

Çerçevenin geçerli sürümünde SecureString'i mantıklı bir şekilde kullanabileceğiniz çok az senaryo vardır. Gerçekten sadece yönetilmeyen API'lerle etkileşim için kullanışlıdır - Marshal.SecureStringToGlobalAllocUnicode kullanarak bunu marshal yapabilirsiniz.

Bir System.String'e / sisteminden dönüştürdüğünüz anda amacını yendiniz.

MSDN örneği , konsol girişinden bir kerede SecureString karakteri oluşturur ve güvenli dizeyi yönetilmeyen bir API'ye geçirir. Oldukça kıvrık ve gerçekçi değil.

.NET'in gelecekteki sürümlerinin SecureString için daha kullanışlı olmasını sağlayacak daha fazla desteğe sahip olmasını bekleyebilirsiniz, örneğin:

  • SecureString Console.ReadLineSecure () veya benzeri, örnekteki tüm kıvrık kodlar olmadan bir SecureString'e konsol girişi okumaya benzer.

  • Şifrelerin güvenli bir şekilde girilebilmesi için TextBox.Text özelliğini güvenli bir dize olarak saklayan WinForms TextBox değiştirme.

  • Şifrelerin SecureString olarak geçirilmesine izin vermek için güvenlikle ilgili API'ların uzantıları.

Yukarıdakiler olmadan SecureString sınırlı bir değere sahip olacaktır.


12

Neden tek bir düz örnekleme yerine karakter ekleme yapmak zorunda inanıyorum çünkü arka planda SecureString yapıcısına "şifre" geçen bu "şifre" dizesini güvenli dize amacını yenerek bellek koyar çünkü.

Ekleyerek, bir anda sadece bir karakteri fiziksel olarak birbirine bitişik olmayacak şekilde orijinal karakter dizisini yeniden yapılandırmayı zorlaştırıyorsunuz. Burada yanlış olabilirdim ama bana bu şekilde açıklandı.

Sınıfın amacı, güvenli verilerin bir bellek dökümü veya benzeri bir araçla açığa çıkmasını önlemektir.


11

MS, sunucunun (masaüstü, ne olursa olsun) çökmesine neden olan belirli durumlarda, çalışma zamanı ortamının bellekte bulunanların içeriğini açığa çıkaracak bir bellek dökümü yapacağı zamanlar olduğunu buldu. Güvenli Dize, saldırganın dizenin içeriğini alabilmesini önlemek için bellekte şifreler.


5

SecureString'in en büyük avantajlarından biri, verilerinizin sayfa önbelleğe alınması nedeniyle diske kaydedilme olasılığından kaçınmasıdır. Bellekte bir parolanız varsa ve daha sonra büyük bir program veya veri kümesi yüklerseniz, programınız bellekte disk belleği dolarken parolanız takas dosyasına yazılabilir. Bir SecureString ile, en azından veriler diskinizde belirsiz metinlerde süresiz olarak durmayacaktır.


4

Sanırım dize güvenli olması gerekiyordu, yani bir hacker bunu okumak mümkün olmamalıdır. Bir dize ile başlatırsanız, bilgisayar korsanı orijinal dizeyi okuyabilir.


4

Açıklamanın belirttiği gibi, değer şifreli olarak saklanır, yani işleminizin bir bellek dökümü dizenin değerini ortaya çıkarmaz (oldukça ciddi bir çalışma olmadan).

O zaman çünkü sadece sabit dizesinden bir SecureString inşa edemez nedeni olur bellekte dizesinin şifrelenmemiş sürümü var. Dizeyi parçalar halinde oluşturmaya sınırlamak, tüm dizeyi aynı anda belleğe alma riskini azaltır.


2
Sabit bir dizeden yapıyı sınırlıyorlarsa, foreach ("password" içindeki char c .toCharArray ()) bunu yener, hayır? Pass.AppendChar ('p'); pass.AppendChar ( 'A'); vs?
Richard Morgan

Evet, SecureString'in size verdiği küçük korumayı kolayca atabilirsiniz. Kendinizi tamamen ayağa vurmayı zorlaştırmaya çalışıyorlar. Açıkçası, değeri bir SecureString'e girip çıkarmanın bir yolu olmalı, ya da hiçbir şey için kullanamazsınız.
Mark Bessey

1

Başka bir kullanım durumu, ödeme uygulamaları (POS) ile çalışırken ve dikkatli bir şekilde geliştirici olduğunuz için hassas verileri depolamak için değişmez veri yapılarını kullanamamanızdır . Örneğin: Hassas kart verilerini veya yetkilendirme meta verilerini değiştirilemez dizeye kaydedersem, bu verilerin atıldıktan sonra önemli bir süre boyunca bellekte kullanılabilir olacağı her zaman olurdu. Üzerine yazamıyorum. Bu hassas verilerin hafızada saklanmasının bir başka büyük avantajı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.