คำตอบสั้น ๆ
ทำไมฉันไม่สามารถพูดได้:
SecureString password = new SecureString("password");
เพราะตอนนี้คุณมีpassword
ความทรงจำแล้ว มีวิธีที่จะเช็ดมัน - ซึ่งตรงจุดSecureString
คำตอบยาว ๆ
เหตุผลว่าทำไม SecureStringนั้นมีอยู่เพราะคุณไม่สามารถใช้ZeroMemoryเพื่อล้างข้อมูลที่ละเอียดอ่อนเมื่อคุณทำเสร็จแล้ว มีอยู่เพื่อแก้ไขปัญหาที่เกิดขึ้นเนื่องจาก CLR
ในปกติพื้นเมืองของแอพลิเคชันที่คุณจะเรียกSecureZeroMemory
:
เติมบล็อกของหน่วยความจำด้วยเลขศูนย์
หมายเหตุ : SecureZeroMemory จะเป็นเหมือนZeroMemory
, ยกเว้นคอมไพเลอร์จะไม่เพิ่มประสิทธิภาพของมันออกไป
ปัญหาคือคุณไม่สามารถโทรZeroMemory
หรือSecureZeroMemory
เข้าไปใน. NET และใน. NET strings จะไม่เปลี่ยนรูป คุณไม่สามารถเขียนทับเนื้อหาของสตริงเหมือนที่คุณสามารถทำได้ในภาษาอื่น:
//Wipe out the password
for (int i=0; i<password.Length; i++)
password[i] = \0;
แล้วคุณจะทำอย่างไร เราจะให้ความสามารถใน. NET ในการล้างรหัสผ่านหรือหมายเลขบัตรเครดิตจากหน่วยความจำเมื่อเราทำมันได้อย่างไร
วิธีเดียวที่สามารถทำได้คือการวางสตริงในบล็อกหน่วยความจำดั้งเดิมบางที่ซึ่งคุณสามารถโทรZeroMemory
ได้ วัตถุหน่วยความจำดั้งเดิมเช่น:
- BSTR
- HGLOBAL
- หน่วยความจำที่ไม่มีการจัดการ CoTaskMem
SecureString ให้ความสามารถที่หายไปกลับมา
ใน. NET สตริงไม่สามารถเช็ดได้เมื่อคุณทำเสร็จแล้ว:
- พวกมันไม่เปลี่ยนรูป คุณไม่สามารถเขียนทับเนื้อหาของพวกเขา
- คุณไม่สามารถ
Dispose
ของพวกเขา
- การทำความสะอาดของพวกเขาอยู่ที่ความเมตตาของนักสะสมขยะ
SecureString มีอยู่เป็นวิธีในการส่งผ่านความปลอดภัยของสายอักขระและสามารถรับประกันการล้างข้อมูลได้เมื่อคุณต้องการ
คุณถามคำถาม:
ทำไมฉันไม่สามารถพูดได้:
SecureString password = new SecureString("password");
เพราะตอนนี้คุณมีpassword
ความทรงจำแล้ว ไม่มีวิธีการเช็ด มันติดอยู่ที่นั่นจนกระทั่ง CLR เกิดขึ้นเพื่อตัดสินใจใช้หน่วยความจำนั้นอีกครั้ง คุณทำให้เราย้อนกลับไปเมื่อเราเริ่ม; แอปพลิเคชันที่ทำงานด้วยรหัสผ่านที่เราไม่สามารถกำจัดได้และที่ที่หน่วยความจำดัมพ์ (หรือการตรวจสอบกระบวนการ) สามารถดูรหัสผ่าน
SecureString ใช้ Data Protection API เพื่อจัดเก็บสตริงที่เข้ารหัสในหน่วยความจำ วิธีนั้นสตริงจะไม่มีอยู่ใน swapfiles, crash dumps หรือแม้แต่ในหน้าต่างตัวแปรโลคัลที่มีเพื่อนร่วมงานมองหาคุณควร
ฉันจะอ่านรหัสผ่านได้อย่างไร
จากนั้นเป็นคำถาม: ฉันจะโต้ตอบกับสตริงได้อย่างไร คุณไม่ต้องการวิธีเช่น:
String connectionString = secureConnectionString.ToString()
เพราะตอนนี้คุณกลับมาแล้วตรงจุดที่คุณเริ่ม - รหัสผ่านที่คุณไม่สามารถกำจัดได้ คุณต้องการบังคับให้นักพัฒนาจัดการสายอักขระที่ละเอียดอ่อนได้อย่างถูกต้องเพื่อให้สามารถลบออกจากหน่วยความจำได้
นั่นคือเหตุผลที่. NET ได้จัดให้มีสามฟังก์ชั่นตัวช่วยที่มีประโยชน์ในการจัดการ SecureString ในหน่วยความจำที่ไม่มีการจัดการ
คุณแปลงสายอักขระเป็นหน่วยความจำที่ไม่มีการจัดการ blob จัดการกับมันแล้วเช็ดอีกครั้ง
APIs บางคนยอมรับSecureStrings ตัวอย่างเช่นใน ADO.net 4.5 SqlConnection.CredentialรับชุดSqlCredential :
SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();
คุณสามารถเปลี่ยนรหัสผ่านภายในสตริงการเชื่อมต่อ:
SqlConnection.ChangePassword(connectionString, cred, newPassword);
และมีหลายสถานที่ใน. NET ที่พวกเขายังคงยอมรับสตริงธรรมดาสำหรับวัตถุประสงค์ในการทำงานร่วมกันได้อย่างรวดเร็วจากนั้นจึงหันไปใส่ SecureString
จะใส่ข้อความลงใน SecureString ได้อย่างไร
สิ่งนี้ยังทำให้เกิดปัญหา:
ฉันจะรับรหัสผ่านใน SecureString ตั้งแต่แรกได้อย่างไร
นี่คือความท้าทาย แต่ประเด็นคือเพื่อให้คุณคิดถึงความปลอดภัย
บางครั้งฟังก์ชั่นนี้มีให้สำหรับคุณแล้ว ตัวอย่างเช่นตัวควบคุมWPF PasswordBoxสามารถส่งคืนรหัสผ่านที่คุณป้อนเป็นSecureStringโดยตรง:
ได้รับรหัสผ่านที่จัดขึ้นในขณะนี้โดยPasswordBoxเป็นSecureString
สิ่งนี้มีประโยชน์เพราะทุกที่ที่คุณเคยผ่านสายอักขระดิบคุณก็มีระบบประเภทที่บ่นว่า SecureString เข้ากันไม่ได้กับ String คุณต้องการไปให้นานที่สุดก่อนที่จะแปลง SecureString ของคุณกลับเป็นสตริงปกติ
การแปลง SecureString นั้นง่ายพอ:
- SecureStringToBSTR
- PtrToStringBSTR
ในขณะที่:
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;
}
พวกเขาไม่ต้องการให้คุณทำ
แต่ฉันจะรับสตริงเข้าสู่ SecureString ได้อย่างไร สิ่งที่คุณต้องทำก็คือหยุดมีรหัสผ่านในStringในตอนแรก คุณต้องมีมันในอย่างอื่น แม้แต่Char[]
อาเรย์ก็มีประโยชน์
นั่นคือเมื่อคุณสามารถผนวกอักขระแต่ละตัวและล้างข้อความธรรมดาเมื่อเสร็จแล้ว:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
คุณต้องใช้รหัสผ่านของคุณเก็บไว้ในหน่วยความจำบางส่วนที่คุณสามารถเช็ด โหลดลงใน SecureString จากตรงนั้น
TL; DR: SecureStringที่มีอยู่เพื่อให้เทียบเท่าของZeroMemory
บางคนไม่เห็นจุดในการลบรหัสผ่านของผู้ใช้จากหน่วยความจำเมื่ออุปกรณ์ถูกล็อคหรือเช็ดการกดแป้นพิมพ์จากหน่วยความจำหลังจากที่ได้รับการรับรองความถูกต้องแล้ว คนเหล่านั้นไม่ใช้ SecureString
SecureString
ให้พัฒนาใหม่อีกต่อไป: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md