SecureString ใช้งานได้จริงในแอปพลิเคชัน C # หรือไม่


224

อย่าลังเลที่จะแก้ไขฉันหากข้อสันนิษฐานของฉันผิดที่นี่ แต่ให้ฉันอธิบายว่าทำไมฉันถึงถาม

นำมาจาก MSDN, a SecureString:

แสดงถึงข้อความที่ควรเก็บเป็นความลับ ข้อความถูกเข้ารหัสเพื่อความเป็นส่วนตัวเมื่อมีการใช้งานและลบออกจากหน่วยความจำคอมพิวเตอร์เมื่อไม่ต้องการใช้อีกต่อไป

ฉันได้รับมันทำให้รู้สึกที่สมบูรณ์ในการจัดเก็บรหัสผ่านหรือข้อมูลส่วนตัวอื่น ๆ ในSecureStringช่วงเวลาSystem.Stringเพราะคุณสามารถควบคุมวิธีการและเวลาที่มันถูกเก็บไว้ในหน่วยความจำจริงเพราะSystem.String:

มีทั้งที่ไม่เปลี่ยนรูปและเมื่อไม่ต้องการอีกต่อไปจะไม่สามารถกำหนดตารางเวลาทางโปรแกรมสำหรับการรวบรวมขยะ นั่นคืออินสแตนซ์จะอ่านอย่างเดียวหลังจากที่มันถูกสร้างขึ้นและมันเป็นไปไม่ได้ที่จะทำนายเมื่ออินสแตนซ์จะถูกลบออกจากหน่วยความจำคอมพิวเตอร์ ดังนั้นหากวัตถุ String มีข้อมูลที่ละเอียดอ่อนเช่นรหัสผ่านหมายเลขบัตรเครดิตหรือข้อมูลส่วนบุคคลมีความเสี่ยงที่ข้อมูลจะถูกเปิดเผยหลังจากใช้งานเพราะแอปพลิเคชันของคุณไม่สามารถลบข้อมูลออกจากหน่วยความจำคอมพิวเตอร์

อย่างไรก็ตามในกรณีของโปรแกรม GUI (ตัวอย่างเช่นลูกค้า SSH) ที่จะต้องมีการสร้างขึ้นจากSecureString System.Stringทั้งหมดของการควบคุมข้อความที่ใช้สตริงเป็นชนิดข้อมูลพื้นฐาน

ดังนั้นนี่หมายความว่าทุกครั้งที่ผู้ใช้กดปุ่มสตริงเก่าที่ถูกทิ้งและสตริงใหม่จะถูกสร้างขึ้นเพื่อเป็นตัวแทนของค่าภายในกล่องข้อความแม้ว่าจะใช้หน้ากากรหัสผ่าน และเราไม่สามารถควบคุมได้ว่าค่าเหล่านั้นจะถูกทิ้งจากหน่วยความจำเมื่อใดหรือไม่

ถึงเวลาเข้าสู่ระบบเซิร์ฟเวอร์แล้ว คาดเดาอะไร คุณต้องผ่านสตริงผ่านการเชื่อมต่อสำหรับการรับรองความถูกต้อง งั้นเรามาแปลงSecureStringเป็น a System.String.... และตอนนี้เรามีสตริงบน heap โดยไม่มีทางบังคับให้ผ่านการรวบรวมขยะ (หรือเขียน 0 ไปยังบัฟเฟอร์)

จุดของฉันคือ : ไม่ว่าสิ่งที่คุณทำบางเส้นที่SecureStringจะไปที่จะถูกแปลงเป็นSystem.Stringความหมายมันจะมีอยู่ที่อย่างน้อยในกองในบางจุด (โดยไม่มีการรับประกันของการเก็บขยะใด ๆ )

จุดของฉันไม่ใช่ : มีวิธีหลีกเลี่ยงการส่งสตริงไปยังการเชื่อมต่อ ssh หรือหลีกเลี่ยงการมีตัวควบคุมเก็บสตริง (ทำการควบคุมแบบกำหนดเอง) สำหรับคำถามนี้คุณสามารถแทนที่ "การเชื่อมต่อ ssh" ด้วย "แบบฟอร์มการเข้าสู่ระบบ", "แบบฟอร์มการลงทะเบียน", "แบบฟอร์มการชำระเงิน", "อาหารที่คุณจะเลี้ยงด้วยรูปแบบลูกสุนัขของคุณ แต่ไม่ใช่แบบเด็ก" เป็นต้น

  • ดังนั้นการใช้SecureStringจริงกลายเป็นจริงในจุดใด
  • มันคุ้มค่ากับเวลาในการพัฒนาเพิ่มเติมหรือไม่ในการกำจัดการใช้System.Stringวัตถุโดยสิ้นเชิง?
  • เป็นจุดรวมของSecureStringการลดจำนวนเวลาเพียงแค่System.Stringในกอง (ลดความเสี่ยงของการย้ายไปที่ไฟล์แลกเปลี่ยนทางกายภาพ)?
  • หากผู้โจมตีมีวิธีในการตรวจสอบฮีปอยู่แล้วก็น่าจะเป็นอย่างนั้น (A) มีวิธีการอ่านการกดแป้นหรือ (B) มีเครื่องทางกายภาพอยู่แล้ว... ดังนั้นจะใช้SecureStringป้องกันไม่ให้เขาไปถึง ข้อมูลยังไง?
  • นี่เป็นเพียง "ความปลอดภัยผ่านความสับสน" หรือไม่?

ขออภัยถ้าฉันวางคำถามบนหนาเกินไปอยากรู้อยากเห็นก็ดีขึ้นของฉัน รู้สึกอิสระที่จะตอบคำถามใด ๆ หรือทั้งหมดของฉัน (หรือบอกฉันว่าสมมติฐานของฉันผิดทั้งหมด) :)


25
โปรดทราบว่าSecureStringไม่ใช่สตริงที่ปลอดภัยจริงๆ เป็นเพียงวิธีลดช่วงเวลาที่ใครบางคนสามารถตรวจสอบหน่วยความจำของคุณและรับข้อมูลที่ละเอียดอ่อนได้สำเร็จ นี่ไม่ใช่กระสุนและมันไม่ได้ตั้งใจจะเป็น แต่คะแนนที่คุณเพิ่มนั้นถูกต้องมาก ที่เกี่ยวข้อง: stackoverflow.com/questions/14449579/…
Theodoros Chatzigiannakis

1
@TheodorosChatzigiannakis ใช่นั่นเป็นสิ่งที่ฉันคิดได้ ฉันเพิ่งใช้เวลาทั้งวันในการรวบรวมสมองของฉันพยายามหาวิธีที่ปลอดภัยในการจัดเก็บรหัสผ่านสำหรับอายุการใช้งานของแอปพลิเคชันและมันทำให้ฉันสงสัยว่ามันคุ้มค่าหรือไม่
Steven Jeffries

1
อาจไม่คุ้มค่า แต่จากนั้นอีกครั้งคุณอาจจะป้องกันสถานการณ์ที่รุนแรง สุดขีดไม่ได้อยู่ในแง่ที่ว่ามีใครบางคนที่เข้าถึงคอมพิวเตอร์ของคุณแบบนี้ไม่ได้ แต่ในแง่ที่ว่าถ้าใครบางคนเข้าถึงคอมพิวเตอร์ประเภทนี้ได้คอมพิวเตอร์จะถูกพิจารณาว่าเป็นอันตราย (สำหรับทุกเจตนาและวัตถุประสงค์) ไม่คิดว่าจะมีภาษาหรือเทคนิคใด ๆ ที่คุณสามารถใช้เพื่อป้องกันสิ่งนี้ได้อย่างสมบูรณ์
Theodoros Chatzigiannakis

8
มันป้องกันรหัสผ่านที่มองเห็นได้นานหลายปีหลังจากที่ทุกคนหยุดคิดว่าอาจมีปัญหา บนฮาร์ดไดรฟ์ที่ทิ้งแล้วจะถูกเก็บไว้ในไฟล์เก็บเพจ การขัดหน่วยความจำเพื่อให้โอกาสสำหรับสิ่งนี้ต่ำเป็นสิ่งสำคัญไม่สามารถทำได้ด้วย System.String เนื่องจากมันไม่เปลี่ยนรูป มันต้องการโครงสร้างพื้นฐานที่โปรแกรมไม่กี่วันนี้สามารถเข้าถึง, interop กับรหัสพื้นเมืองดังนั้นวิธี Marshal.SecureStringXxx () มีประโยชน์คือการหายาก หรือเป็นวิธีที่ดีที่จะแจ้งให้ผู้ใช้สำหรับมัน :)
ฮันส์ Passant

1
SecureString เป็นเทคนิคเชิงลึกด้านความปลอดภัย การแลกเปลี่ยนระหว่างต้นทุนการพัฒนากับการได้รับความปลอดภัยนั้นไม่เอื้ออำนวยในสถานการณ์ส่วนใหญ่ มีกรณีการใช้งานที่ดีน้อยมาก
usr

คำตอบ:


237

SecureStringมีการใช้จริงในทางปฏิบัติมากของการเป็น

คุณรู้ไหมว่าฉันเคยเห็นสถานการณ์แบบนี้มากี่ครั้งแล้ว? (คำตอบคือ: จำนวนมาก!):

  • รหัสผ่านจะปรากฏในไฟล์บันทึกโดยไม่ตั้งใจ
  • รหัสผ่านจะปรากฏที่ใดที่หนึ่ง - เมื่อ GUI แสดงบรรทัดคำสั่งของแอปพลิเคชันที่กำลังทำงานและบรรทัดคำสั่งประกอบด้วยรหัสผ่าน อุ่ย
  • การใช้ตัวสร้างโปรไฟล์หน่วยความจำในซอฟต์แวร์โปรไฟล์กับเพื่อนร่วมงานของคุณ เพื่อนร่วมงานเห็นรหัสผ่านของคุณในหน่วยความจำ ฟังดูไม่จริงเหรอ? ไม่ใช่เลย.
  • ฉันเคยใช้RedGateซอฟต์แวร์ที่สามารถจับ "ค่า" ของตัวแปรท้องถิ่นในกรณีที่มีข้อยกเว้นซึ่งมีประโยชน์อย่างน่าอัศจรรย์ แม้ว่าฉันสามารถจินตนาการได้ว่ามันจะเข้าสู่ระบบ "รหัสผ่านสตริง" โดยไม่ตั้งใจ
  • ดัมพ์ข้อผิดพลาดที่มีรหัสผ่านสตริง

คุณรู้วิธีหลีกเลี่ยงปัญหาเหล่านี้หรือไม่? SecureString. โดยทั่วไปแล้วจะทำให้แน่ใจว่าคุณจะไม่ทำผิดพลาดอย่างโง่เขลาเช่นนี้ มันหลีกเลี่ยงได้อย่างไร ด้วยการทำให้แน่ใจว่ารหัสผ่านนั้นถูกเข้ารหัสในหน่วยความจำที่ไม่มีการจัดการและมูลค่าที่แท้จริงสามารถเข้าถึงได้เมื่อคุณ 90% แน่ใจว่าสิ่งที่คุณกำลังทำ

ในความรู้สึกใช้SecureStringงานได้ง่าย:

1) ทุกอย่างถูกเข้ารหัส

2) การโทรของผู้ใช้ AppendChar

3) ถอดรหัสทุกอย่างในหน่วยความจำ UNMANAGED และเพิ่มตัวละคร

4) เข้ารหัสทุกอย่างอีกครั้งใน UNMANAGED MEMORY

เกิดอะไรขึ้นถ้าผู้ใช้มีการเข้าถึงคอมพิวเตอร์ของคุณ? ไวรัสจะสามารถเข้าถึงทุกคนได้SecureStringsหรือไม่? ใช่. สิ่งที่คุณต้องทำก็แค่เชื่อมต่อตัวเองRtlEncryptMemoryเมื่อหน่วยความจำถูกถอดรหัสคุณจะได้ตำแหน่งของที่อยู่หน่วยความจำที่ไม่ได้เข้ารหัสและอ่านมันออกมา Voila! ในความเป็นจริงคุณสามารถสร้างไวรัสที่จะสแกนหาการใช้งานSecureStringและบันทึกกิจกรรมทั้งหมดด้วย ฉันไม่ได้บอกว่ามันจะเป็นงานง่าย แต่ก็สามารถทำได้ อย่างที่คุณเห็น "พลัง" ของSecureStringหายไปอย่างสมบูรณ์เมื่อมีผู้ใช้ / ไวรัสในระบบของคุณ

คุณมีคะแนนสองสามคะแนนในโพสต์ของคุณ แน่นอนว่าถ้าคุณใช้การควบคุม UI บางอย่างที่เก็บ "รหัสผ่านสตริง" ไว้ภายในการใช้งานจริงSecureStringจะไม่มีประโยชน์ แม้ว่าจะยังสามารถป้องกันความโง่เขลาบางอย่างที่ฉันได้ระบุไว้ข้างต้น

นอกจากนี้ตามที่คนอื่น ๆ ระบุไว้ WPF รองรับ PasswordBox ซึ่งใช้SecureStringภายในผ่านคุณสมบัติSecurePassword

บรรทัดล่างคือ; ถ้าคุณมีความไวต่อข้อมูล (รหัสผ่านเครดิตการ์ด .. ), SecureStringการใช้งาน นี่คือสิ่งที่ C # Framework กำลังติดตาม ยกตัวอย่างเช่นระดับเก็บรหัสผ่านเป็นNetworkCredential SecureStringถ้าคุณดูที่สิ่งนี้คุณสามารถดูประเพณีมากกว่า 80 ในกรอบ NET SecureString.

มีหลายกรณีเมื่อคุณต้องแปลงSecureStringเป็นสตริงเนื่องจาก API บางตัวคาดหวัง

ปัญหาปกติคือ:

  1. API คือ GENERIC ไม่ทราบว่ามีข้อมูลที่ละเอียดอ่อน
  2. API รู้ว่ามันเกี่ยวข้องกับข้อมูลที่ละเอียดอ่อนและใช้ "string" - นั่นเป็นเพียงการออกแบบที่ไม่ดี

คุณยกระดับจุดดี: เกิดอะไรขึ้นเมื่อSecureStringถูกแปลงเป็นstring? สิ่งนี้สามารถเกิดขึ้นได้เนื่องจากจุดแรก เช่น API ไม่ทราบว่าเป็นข้อมูลที่ละเอียดอ่อน ฉันไม่ได้เห็นสิ่งนั้นเกิดขึ้นเป็นการส่วนตัว การรับสายจาก SecureString นั้นไม่ง่าย

มันไม่ได้ง่าย ๆ ด้วยเหตุผลง่ายๆ ; ไม่เคยตั้งใจจะให้ผู้ใช้แปลง SecureString เป็นสายอักขระตามที่คุณระบุไว้: GC จะเตะเข้ามาหากคุณเห็นว่าคุณกำลังทำสิ่งนั้นอยู่คุณต้องถอยกลับและถามตัวเองว่า: ทำไมฉันถึงทำอย่างนั้นหรือฉันต้องการจริงๆ ทำไม?

มีสิ่งหนึ่งที่น่าสนใจที่ฉันเห็น คือฟังก์ชั่นใช้เวลา LogonUser Winapi LPTSTR SecureStringToGlobalAllocUnicodeเป็นรหัสผ่านซึ่งหมายความว่าคุณต้องโทร โดยทั่วไปจะให้รหัสผ่านที่ไม่ได้เข้ารหัสซึ่งอาศัยอยู่ในหน่วยความจำที่ไม่มีการจัดการ คุณต้องกำจัดมันทันทีที่ทำเสร็จ:

// Marshal the SecureString to unmanaged memory.
IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
   //...snip...
}
finally 
{
   // Zero-out and free the unmanaged string reference.
   Marshal.ZeroFreeGlobalAllocUnicode(rawPassword);
}

คุณสามารถขยายSecureStringคลาสได้ตลอดเวลาด้วยวิธีการขยายเช่นToEncryptedString(__SERVER__PUBLIC_KEY)ซึ่งให้stringอินสแตนซ์ของการSecureStringเข้ารหัสโดยใช้คีย์สาธารณะของเซิร์ฟเวอร์ เซิร์ฟเวอร์เท่านั้นจึงสามารถถอดรหัสได้ แก้ไขปัญหา: การรวบรวมขยะจะไม่เห็นสตริง "ดั้งเดิม" เนื่องจากคุณไม่เคยเปิดเผยในหน่วยความจำที่มีการจัดการ นี่คือสิ่งที่กำลังดำเนินการในPSRemotingCryptoHelper( EncryptSecureStringCore(SecureString secureString))

และเป็นสิ่งที่เกือบจะเกี่ยวข้อง: Mono SecureString ไม่เข้ารหัสเลย การดำเนินการได้รับการแสดงความคิดเห็นเพราะ .. รอให้มัน .. "มันทำให้เกิดการทดสอบแตก nunit"ซึ่งนำมาถึงจุดสุดท้ายของฉัน:

SecureStringไม่รองรับทุกที่ หากแพลตฟอร์ม / สถาปัตยกรรมไม่รองรับSecureStringคุณจะได้รับการยกเว้น มีรายการแพลตฟอร์มที่รองรับในเอกสารประกอบ


ฉันคิดว่าSecureStringน่าจะเป็นประโยชน์ได้มากกว่านี้หากมีกลไกในการเข้าถึงตัวละครแต่ละตัว ประสิทธิภาพสำหรับกลไกดังกล่าวสามารถเพิ่มประสิทธิภาพได้โดยมีประเภทของโครงสร้างSecureStringTempBufferโดยไม่มีสมาชิกสาธารณะใด ๆ ซึ่งสามารถส่งผ่านrefไปยังวิธีSecureString"read one character" [หากการเข้ารหัส / ถอดรหัสถูกประมวลผลในกลุ่มแปดอักขระโครงสร้างดังกล่าวสามารถเก็บข้อมูล สำหรับอักขระ 8 ตัวและตำแหน่งของอักขระตัวแรกของตัวละครเหล่านั้นภายในSecureString]
supercat

2
ฉันตั้งค่าสถานะนี้ในทุกการตรวจสอบซอร์สโค้ดที่ฉันดำเนินการ ควรซ้ำอีกครั้งว่าถ้าคุณใช้SecureStringจะต้องมีการใช้งานตลอดทางผ่านกอง
Casey

1
ฉันใช้ส่วนขยาย. ToEncryptedString () แต่ใช้ใบรับรอง คุณช่วยลองดูและแจ้งให้เราทราบหากฉันทำผิดทั้งหมด ฉันหวังว่ามันจะปลอดภัย gist.github.com/sturlath/99cfbfff26e0ebd12ea73316d0843330
Sturla

@Sturla - EngineSettingsวิธีการขยายของคุณคืออะไร?
InteXX


15

มีปัญหาเล็กน้อยในสมมติฐานของคุณ

อันดับแรกของคลาส SecureString ทั้งหมดไม่มีตัวสร้างสตริง เพื่อสร้างสิ่งหนึ่งคุณจัดสรรวัตถุแล้วผนวกตัวอักษร

ในกรณีของ GUI หรือคอนโซลคุณสามารถส่งผ่านแต่ละคีย์ที่กดไปยังสตริงที่ปลอดภัยได้อย่างง่ายดาย

คลาสได้รับการออกแบบในแบบที่คุณไม่สามารถเข้าถึงค่าที่เก็บไว้โดยไม่ได้ตั้งใจ ซึ่งหมายความว่าคุณไม่สามารถรับstringรหัสผ่านได้โดยตรง

ดังนั้นสำหรับการใช้งานตัวอย่างเช่นในการตรวจสอบความถูกต้องของเว็บคุณจะต้องใช้คลาสที่เหมาะสมและปลอดภัย

ใน. NET Framework คุณมีคลาสที่สามารถใช้ SecureString

  • การควบคุม PasswordBox ของ WPF จะเก็บรหัสผ่านเป็น SecureString ภายใน
  • คุณสมบัติรหัสผ่านของ System.Diagnostics.ProcessInfo คือ SecureString
  • ตัวสร้างสำหรับ X509Certificate2 ใช้ SecureString สำหรับรหัสผ่าน

(มากกว่า)

เพื่อสรุปคลาส SecureString มีประโยชน์ แต่ต้องการความสนใจจากนักพัฒนามากขึ้น

ทั้งหมดนี้มีตัวอย่างอธิบายไว้ในเอกสารSecureStringของ MSDN


10
ฉันไม่เข้าใจคำตอบของคุณในย่อหน้าที่สามถึงสุดท้าย วิธีที่คุณจะโอนSecureStringผ่านเครือข่ายโดยไม่ต้อง serializing มันกลายเป็นstring? ฉันคิดว่าประเด็นของ OP คือต้องมีเวลาที่คุณต้องการใช้ค่าที่เก็บไว้อย่างปลอดภัยใน a SecureStringซึ่งหมายความว่าคุณต้องเอามันออกไปจากที่นั่นและมันก็ไม่ปลอดภัยอีกต่อไป ใน Library Class Framework ทั้งหมดไม่มีวิธีใดที่ยอมรับSecureStringอินพุตโดยตรง (เท่าที่ฉันเห็น) ดังนั้นอะไรคือจุดสำคัญในการเก็บค่าไว้SecureStringในตอนแรก
stakx - ไม่สนับสนุน

@ DamianLeszczyński-Vash ฉันรู้ว่า SecureString ไม่มีตัวสร้างสตริง แต่จุดของฉันคือคุณ (A) สร้าง SecureString และผนวกอักขระแต่ละตัวจากสตริงไปยังหรือ (B) ในบางจุดจำเป็นต้องใช้ ค่าที่เก็บใน SecureString เป็นสตริงเพื่อส่งผ่านไปยัง API บางตัว อย่างที่บอกไปแล้วว่าคุณได้คะแนนที่ดีและฉันเห็นได้ว่าในบางกรณีมันมีประโยชน์อย่างไร
Steven Jeffries

1
@stakx คุณจะส่งมันผ่านเครือข่ายโดยการรับchar[]และส่งcharผ่านซ็อกเก็ต - การสร้างไม่มีวัตถุที่รวบรวมขยะในกระบวนการ
user253751

Char [] สามารถรวบรวมได้และเปลี่ยนแปลงได้ดังนั้นจึงสามารถเขียนทับได้
Peter Ritchie

แน่นอนว่ามันง่ายที่จะลืมเขียนทับอาร์เรย์นั้น ไม่ว่าจะด้วยวิธีใด API ที่คุณส่งให้ตัวอักษรอาจทำสำเนาได้เช่นกัน
cHao

10

SecureString มีประโยชน์ถ้า:

  • คุณสร้างตัวละครโดยตัวละคร (เช่นจากอินพุตคอนโซล) หรือรับจาก API ที่ไม่มีการจัดการ

  • คุณใช้มันโดยส่งผ่านไปยัง API ที่ไม่ได้รับการจัดการ (SecureStringToBSTR)

หากคุณแปลงเป็นสตริงที่มีการจัดการคุณจะต้องเสียวัตถุประสงค์

อัปเดตเพื่อตอบกลับความคิดเห็น

... หรือ BSTR เหมือนกับที่คุณพูดถึงซึ่งดูเหมือนจะไม่ปลอดภัยกว่านี้อีกแล้ว

หลังจากถูกแปลงเป็น BSTR คอมโพเนนต์ที่ไม่มีการจัดการที่ใช้ BSTR จะสามารถเป็นศูนย์หน่วยความจำ หน่วยความจำที่ไม่มีการจัดการมีความปลอดภัยมากกว่าในแง่ที่สามารถรีเซ็ตได้ด้วยวิธีนี้

อย่างไรก็ตามมี API น้อยมากใน. NET Framework ที่รองรับ SecureString ดังนั้นคุณถูกต้องที่จะบอกว่ามันมีค่า จำกัด ในทุกวันนี้

กรณีการใช้งานหลักที่ฉันเห็นอยู่ในแอปพลิเคชันไคลเอนต์ที่ต้องการให้ผู้ใช้ป้อนรหัสหรือรหัสผ่านที่มีความอ่อนไหวสูง อินพุตผู้ใช้สามารถใช้อักขระเป็นตัวอักษรเพื่อสร้าง SecureString จากนั้นสิ่งนี้อาจถูกส่งผ่านไปยัง API ที่ไม่มีการจัดการซึ่งเป็นศูนย์ BSTR ที่ได้รับหลังจากใช้งาน ดัมพ์หน่วยความจำที่ตามมาใด ๆ จะไม่มีสตริงที่ละเอียดอ่อน

ในแอปพลิเคชันเซิร์ฟเวอร์มันยากที่จะดูว่ามันจะมีประโยชน์

อัพเดท 2

ตัวอย่างหนึ่งของ API NET ที่ยอมรับ SecureString เป็นคอนสตรัคนี้สำหรับชั้น X509Certificate หากคุณ spelunk ด้วย ILSpy หรือคล้ายกันคุณจะเห็นว่า SecureString ถูกแปลงภายในเป็นบัฟเฟอร์ที่ไม่มีการจัดการ ( Marshal.SecureStringToGlobalAllocUnicode) ซึ่งจะถูกทำให้เป็นศูนย์เมื่อเสร็จสิ้นด้วย ( Marshal.ZeroFreeGlobalAllocUnicode)


1
+1 แต่คุณจะไม่ท้าย "เอาชนะจุดประสงค์" หรือไม่ ระบุว่าแทบจะไม่มีวิธีใดใน Framework Class Library ที่ยอมรับSecureStringว่าเป็นอินพุตสิ่งที่พวกเขาจะใช้คืออะไร คุณต้องแปลงกลับเป็นstringไม่ช้าก็เร็ว (หรือBSTRเหมือนที่คุณพูดถึงซึ่งดูไม่ปลอดภัยกว่า) เหตุใดจึงต้องกังวลกับSecureStringทั้งหมด?
stakx - ไม่ได้มีส่วนร่วมใน

5

Microsoft ไม่แนะนำให้ใช้SecureStringสำหรับรหัสใหม่

จากเอกสารของSecureString Class :

สำคัญ

เราไม่แนะนำให้คุณใช้SecureStringคลาสสำหรับการพัฒนาใหม่ สำหรับข้อมูลเพิ่มเติมดูSecureStringไม่ควรใช้

สิ่งที่แนะนำ:

อย่าใช้SecureStringรหัสใหม่ เมื่อทำการย้ายรหัสไปยัง. NET Core ให้พิจารณาว่าเนื้อหาของอาร์เรย์นั้นไม่ได้เข้ารหัสในหน่วยความจำ

วิธีการทั่วไปในการจัดการกับข้อมูลรับรองคือการหลีกเลี่ยงและใช้วิธีการอื่นในการตรวจสอบความถูกต้องเช่นใบรับรองหรือการตรวจสอบ Windows บน GitHub


4

ตามที่คุณได้ระบุอย่างถูกต้องSecureStringนำเสนอข้อได้เปรียบเฉพาะอย่างหนึ่งอย่างมากกว่าstring: การลบแบบกำหนดเอง มีสองปัญหาเกี่ยวกับข้อเท็จจริงนี้:

  1. ดังที่คนอื่น ๆ พูดถึงและเมื่อคุณสังเกตเห็นด้วยตัวเองสิ่งนี้ก็ไม่เพียงพอ คุณต้องให้แน่ใจว่าขั้นตอนของกระบวนการ (รวมถึงการดึงของการป้อนข้อมูลการก่อสร้างของสตริง, การใช้งาน, การลบ, การขนส่ง, ฯลฯ ) SecureStringทุกเกิดขึ้นโดยการเอาชนะจุดประสงค์ของการใช้ ซึ่งหมายความว่าคุณจะต้องระมัดระวังที่จะไม่สร้างประชาคมที่มีการจัดการที่ไม่เปลี่ยนรูปstringหรือบัฟเฟอร์อื่น ๆ ที่จะจัดเก็บข้อมูลที่สำคัญ (หรือคุณจะต้องติดตามว่าเช่นกัน) ในทางปฏิบัติการทำเช่นนี้ไม่ใช่เรื่องง่ายเสมอไปเพราะ API จำนวนมากเสนอวิธีการทำงานร่วมกับstringไม่ใช่SecureStringเท่านั้น และแม้ว่าคุณจะจัดการทุกอย่างถูกต้อง ...
  2. SecureStringป้องกันการโจมตีที่เฉพาะเจาะจงมาก ๆ (และสำหรับบางคนมันไม่ได้น่าเชื่อถือ) ตัวอย่างเช่นSecureStringช่วยให้คุณลดขนาดเวลาที่ผู้โจมตีสามารถถ่ายโอนข้อมูลหน่วยความจำของกระบวนการของคุณและดึงข้อมูลที่สำคัญได้สำเร็จ (อีกครั้งตามที่คุณชี้อย่างถูกต้อง) แต่หวังว่าหน้าต่างนั้นเล็กเกินไปสำหรับผู้โจมตี ถ่ายภาพความทรงจำของคุณไม่ถือว่าเป็นการรักษาความปลอดภัยเลย

ดังนั้นเมื่อใดที่คุณควรใช้ เฉพาะเมื่อคุณทำงานกับบางสิ่งบางอย่างที่ช่วยให้คุณสามารถทำงานกับSecureStringทุกความต้องการของคุณและคุณก็ควรที่จะใส่ใจว่าสิ่งนี้จะปลอดภัยในสถานการณ์เฉพาะเท่านั้น


2
คุณกำลังสมมติว่าผู้โจมตีสามารถจับภาพความทรงจำของกระบวนการได้ตลอดเวลา ไม่จำเป็นต้องเป็นอย่างนั้น พิจารณาดัมพ์ข้อผิดพลาด หากความผิดพลาดเกิดขึ้นหลังจากที่แอปพลิเคชันทำกับข้อมูลที่สำคัญการถ่ายโอนข้อมูลความผิดพลาดไม่ควรมีข้อมูลที่ละเอียดอ่อน

4

ข้อความด้านล่างจะถูกคัดลอกจากเครื่องวิเคราะห์รหัสคงที่ของ HP Fortify

บทคัดย่อ: วิธี PassString () ใน PassGenerator.cs เก็บข้อมูลที่ละเอียดอ่อนในลักษณะที่ไม่ปลอดภัย (เช่นในสตริง) ทำให้สามารถดึงข้อมูลผ่านการตรวจสอบฮีปได้

คำอธิบาย: ข้อมูลที่ละเอียดอ่อน (เช่นรหัสผ่านหมายเลขประกันสังคมหมายเลขบัตรเครดิตและอื่น ๆ ) ที่เก็บไว้ในหน่วยความจำสามารถรั่วไหลออกมาได้หากถูกจัดเก็บไว้ในวัตถุ String ที่จัดการ วัตถุสตริงไม่ได้ถูกตรึงดังนั้นตัวรวบรวมข้อมูลขยะสามารถย้ายวัตถุเหล่านี้ได้ตามต้องการและเก็บสำเนาไว้ในหน่วยความจำจำนวนมาก วัตถุเหล่านี้ไม่ได้ถูกเข้ารหัสโดยค่าเริ่มต้นดังนั้นใครก็ตามที่สามารถอ่านหน่วยความจำของกระบวนการจะสามารถดูเนื้อหาได้ นอกจากนี้หากหน่วยความจำของกระบวนการถูกสลับไปยังดิสก์เนื้อหาที่ไม่ได้เข้ารหัสของสตริงจะถูกเขียนลงในไฟล์สลับ สุดท้ายเนื่องจากวัตถุ String เปลี่ยนรูปไม่ได้การลบค่าของ String ออกจากหน่วยความจำสามารถทำได้โดยตัวรวบรวมขยะ CLR เท่านั้น ตัวรวบรวมขยะไม่จำเป็นต้องเรียกใช้ยกเว้นว่า CLR มีหน่วยความจำเหลือน้อยดังนั้นจึงไม่รับประกันว่าจะมีการรวบรวมขยะเมื่อใด

คำแนะนำ: แทนที่จะเก็บข้อมูลที่มีความละเอียดอ่อนไว้ในวัตถุเช่น Strings ให้เก็บไว้ในวัตถุ SecureString แต่ละวัตถุเก็บเนื้อหาในรูปแบบที่เข้ารหัสในหน่วยความจำตลอดเวลา


3

ฉันต้องการพูดถึงประเด็นนี้:

หากผู้โจมตีมีวิธีการตรวจสอบฮีปอยู่แล้วพวกเขาก็มักจะ (A) มีวิธีในการอ่านการกดแป้นหรือ (B) มีเครื่องทางกายภาพอยู่แล้ว... ดังนั้นจะใช้การSecureStringป้องกันไม่ให้พวกเขาไปถึง ข้อมูลยังไง?

ผู้โจมตีอาจไม่สามารถเข้าถึงคอมพิวเตอร์และแอปพลิเคชันได้อย่างสมบูรณ์ แต่สามารถมีวิธีการเข้าถึงบางส่วนของหน่วยความจำของกระบวนการ มันมักจะเกิดจากข้อบกพร่องเช่น buffer overrun เมื่ออินพุตที่สร้างขึ้นเป็นพิเศษสามารถทำให้แอปพลิเคชันเปิดเผยหรือเขียนทับส่วนหนึ่งของหน่วยความจำ

HeartBleed หน่วยความจำรั่ว

ยกตัวอย่าง Heartbleed คำขอที่สร้างขึ้นเป็นพิเศษสามารถทำให้โค้ดเปิดเผยส่วนที่สุ่มของหน่วยความจำของกระบวนการต่อผู้โจมตี ผู้โจมตีสามารถดึงใบรับรอง SSL ออกจากหน่วยความจำได้ แต่สิ่งเดียวที่เขาต้องการคือใช้คำขอที่มีรูปแบบไม่ถูกต้อง

ในโลกของรหัสที่ได้รับการจัดการบัฟเฟอร์ที่ overruns กลายเป็นปัญหาน้อยกว่าบ่อยครั้ง และในกรณีของ WinForms ข้อมูลจะถูกจัดเก็บในลักษณะที่ไม่ปลอดภัยและคุณไม่สามารถทำอะไรกับมันได้ การป้องกันนี้ทำให้SecureStringไร้ประโยชน์มาก

อย่างไรก็ตาม GUI สามารถตั้งโปรแกรมให้ใช้งานSecureStringได้และในกรณีเช่นนี้การลดหน้าต่างของความพร้อมใช้งานของรหัสผ่านในหน่วยความจำอาจคุ้มค่า ยกตัวอย่างเช่นPasswordBox.SecurePasswordจาก WPF SecureStringเป็นประเภท


อืมน่าสนใจที่จะรู้แน่นอน ในความซื่อสัตย์ทั้งหมดฉันไม่ได้กังวลเกี่ยวกับประเภทของการโจมตีที่จำเป็นในการได้รับ System.String คุณค่าจากความทรงจำของฉันฉันแค่ขอจากความอยากรู้บริสุทธิ์ อย่างไรก็ตามขอบคุณสำหรับข้อมูล! :)
Steven Jeffries

2

บางครั้งก่อนหน้านี้ฉันต้องสร้างส่วนต่อประสาน ac # กับเกตเวย์การชำระเงินด้วยบัตรเครดิต java และจำเป็นต้องใช้การเข้ารหัสคีย์การสื่อสารที่ปลอดภัยที่เข้ากันได้ เนื่องจากการใช้งานจาวาค่อนข้างเฉพาะเจาะจงและฉันต้องทำงานกับข้อมูลที่ได้รับการปกป้องตามวิธีที่กำหนด

ฉันพบว่าการออกแบบนี้ใช้งานง่ายและง่ายกว่าการทำงานกับ SecureString ... สำหรับผู้ที่ชอบใช้ ... รู้สึกฟรีไม่มีข้อ จำกัด ทางกฎหมาย :-) โปรดทราบว่าคลาสเหล่านี้เป็นแบบภายในคุณอาจต้องทำให้เป็นแบบสาธารณะ

namespace Cardinity.Infrastructure
{
    using System.Security.Cryptography;
    using System;
    enum EncryptionMethods
    {
        None=0,
        HMACSHA1,
        HMACSHA256,
        HMACSHA384,
        HMACSHA512,
        HMACMD5
    }


internal class Protected
{
    private  Byte[] salt = Guid.NewGuid().ToByteArray();

    protected byte[] Protect(byte[] data)
    {
        try
        {
            return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }

    protected byte[] Unprotect(byte[] data)
    {
        try
        {
            return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }
}


    internal class SecretKeySpec:Protected,IDisposable
    {
        readonly EncryptionMethods _method;

        private byte[] _secretKey;
        public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
        {
            _secretKey = Protect(secretKey);
            _method = encryptionMethod;
        }

        public EncryptionMethods Method => _method;
        public byte[] SecretKey => Unprotect( _secretKey);

        public void Dispose()
        {
            if (_secretKey == null)
                return;
            //overwrite array memory
            for (int i = 0; i < _secretKey.Length; i++)
            {
                _secretKey[i] = 0;
            }

            //set-null
            _secretKey = null;
        }
        ~SecretKeySpec()
        {
            Dispose();
        }
    }

    internal class Mac : Protected,IDisposable
    {
        byte[] rawHmac;
        HMAC mac;
        public Mac(SecretKeySpec key, string data)
        {

            switch (key.Method)
            {
                case EncryptionMethods.HMACMD5:
                    mac = new HMACMD5(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA512:
                    mac = new HMACSHA512(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA384:
                    mac = new HMACSHA384(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA256:
                    mac = new HMACSHA256(key.SecretKey);

                break;
                case EncryptionMethods.HMACSHA1:
                    mac = new HMACSHA1(key.SecretKey);
                    break;

                default:                    
                    throw new NotSupportedException("not supported HMAC");
            }
            rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));            

        }

        public string AsBase64()
        {
            return System.Convert.ToBase64String(Unprotect(rawHmac));
        }

        public void Dispose()
        {
            if (rawHmac != null)
            {
                //overwrite memory address
                for (int i = 0; i < rawHmac.Length; i++)
                {
                    rawHmac[i] = 0;
                }

                //release memory now
                rawHmac = null;

            }
            mac?.Dispose();
            mac = null;

        }
        ~Mac()
        {
            Dispose();
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.