เพราะเหตุใดอักขระ char [] จึงนิยมใช้ String สำหรับรหัสผ่าน


3422

ใน Swing ฟิลด์รหัสผ่านมีเมธอดgetPassword()(return char[]) แทนที่จะเป็นเมธอดปกติgetText()(return String) ในทำนองเดียวกันฉันเจอข้อเสนอแนะที่จะไม่ใช้Stringในการจัดการรหัสผ่าน

เหตุใดจึงStringเป็นภัยคุกคามต่อความปลอดภัยเมื่อต้องใช้รหัสผ่าน char[]มันให้ความรู้สึกไม่สะดวกในการใช้งาน

คำตอบ:


4289

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

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

ใช่แล้วนี่เป็นข้อกังวลด้านความปลอดภัย - แต่ถึงแม้จะใช้char[]เพียงแค่ลดโอกาสในการโจมตีของผู้โจมตีและจะใช้สำหรับการโจมตีประเภทนี้โดยเฉพาะเท่านั้น

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


3
หากกระบวนการมีการเข้าถึงหน่วยความจำของแอปพลิเคชันของคุณนั่นเป็นการละเมิดความปลอดภัยแล้วใช่ไหม
Yeti

5
@Yeti: ใช่ แต่ไม่ใช่ว่าจะเป็นขาวดำ หากพวกเขาสามารถได้รับสแนปชอตของหน่วยความจำเท่านั้นคุณต้องการลดความเสียหายที่สแนปชอตสามารถทำได้หรือลดขนาดหน้าต่างในระหว่างที่สามารถสแนปชอตร้ายแรงได้
Jon Skeet

11
วิธีการโจมตีที่พบบ่อยคือการเรียกใช้กระบวนการที่จัดสรรหน่วยความจำจำนวนมากแล้วสแกนหาข้อมูลที่มีประโยชน์เช่นรหัสผ่าน กระบวนการไม่ต้องการการเข้าถึงเวทย์มนตร์ใด ๆ ในพื้นที่หน่วยความจำของกระบวนการอื่น มันอาศัยกระบวนการอื่นที่กำลังจะตายโดยไม่ต้องล้างข้อมูลที่มีความละเอียดอ่อนก่อนและระบบปฏิบัติการยังไม่ทำการล้างหน่วยความจำ (หรือบัฟเฟอร์หน้า) ก่อนที่จะทำให้กระบวนการใหม่พร้อมใช้งาน ล้างออกรหัสผ่านที่จัดเก็บไว้ในสถานที่ตัดออกจากสายของการโจมตีว่าบางสิ่งบางอย่างไปไม่ได้เมื่อใช้char[] String
Ted Hopp

หากระบบปฏิบัติการไม่ล้างหน่วยความจำก่อนที่จะให้กระบวนการอื่นระบบปฏิบัติการมีปัญหาด้านความปลอดภัยที่สำคัญ! อย่างไรก็ตามเทคนิคการล้างมักทำด้วยเทคนิคโหมดป้องกันและหาก CPU เสีย (เช่น Intel Meldown) คุณยังสามารถอ่านเนื้อหาหน่วยความจำเก่าได้
Mikko Rantalainen

1222

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

พิจารณาสิ่งนี้:

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

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

พิมพ์:

String: Password
Array: [C@5829428e

40
@ voo แต่ฉันสงสัยว่าคุณจะเข้าสู่ระบบผ่านการเขียนโดยตรงเพื่อสตรีมและการต่อข้อมูล กรอบการเข้าสู่ระบบจะเปลี่ยนถ่าน [] เข้าออกดี
bestsss

41
@ Thr4wn เริ่มต้นการดำเนินการคือtoString หมายถึงส่วนที่เหลือเป็นรหัสแฮชเลขฐานสิบหก classname@hashcode[Cchar[]
Konrad Garus

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

37
ฉันจะเขียนPasswordคลาสสำหรับสิ่งนี้ มันคลุมเครือน้อยลงและยากที่จะผ่านไปที่ไหนสักแห่งโดยบังเอิญ
rightfold

8
ทำไมบางคนถึงคิดว่าอาร์เรย์ของถ่านกำลังจะถูกโยนเป็นวัตถุ? ฉันไม่แน่ใจว่าทำไมถึงชอบคำตอบนี้ สมมติว่าคุณทำสิ่งนี้: System.out.println ("รหัสผ่าน" .toCharArray ());
GC_

680

หากต้องการอ้างอิงเอกสารอย่างเป็นทางการคู่มือสถาปัตยกรรม Java Cryptographyกล่าวถึงเรื่องนี้char[]กับStringรหัสผ่าน (เกี่ยวกับการเข้ารหัสด้วยรหัสผ่าน แต่นี่เป็นเรื่องปกติเกี่ยวกับรหัสผ่านของหลักสูตร):

java.lang.Stringมันจะดูเหมือนตรรกะที่จะรวบรวมและจัดเก็บรหัสผ่านในวัตถุของการพิมพ์ อย่างไรก็ตามนี่คือ caveat: Objects ชนิดStringไม่เปลี่ยนรูปนั่นคือไม่มีวิธีการที่กำหนดไว้เพื่อให้คุณสามารถเปลี่ยน (เขียนทับ) หรือลบเนื้อหาของ a String หลังการใช้งาน คุณสมบัตินี้ทำให้Stringวัตถุไม่เหมาะสมสำหรับการจัดเก็บข้อมูลความปลอดภัยเช่นรหัสผ่านผู้ใช้ คุณควรรวบรวมและจัดเก็บข้อมูลที่สำคัญด้านความปลอดภัยใน charอาเรย์แทน

Guideline 2-2 ของแนวทางการเข้ารหัสที่ปลอดภัยสำหรับภาษาการเขียนโปรแกรม Java เวอร์ชัน 4.0ยังกล่าวถึงบางสิ่งที่คล้ายกัน (แม้ว่าจะอยู่ในบริบทของการบันทึก):

หลักเกณฑ์ 2-2: อย่าบันทึกข้อมูลที่มีความอ่อนไหวสูง

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

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


4
นี่เป็นข้ออ้างอิงที่ไม่สมบูรณ์ / เป็นการหลอกลวงที่ฉันพูดถึงด้านล่างคำตอบของ Jon มันเป็นแหล่งที่รู้จักกันดีจากคำวิจารณ์มากมาย
bestsss

37
@ bestass คุณสามารถอ้างอิงอ้างอิงได้หรือไม่?
user961954

10
@ bestass ขออภัย แต่Stringเป็นที่เข้าใจกันดีและวิธีการทำงานใน JVM ... มีเหตุผลที่ดีที่จะใช้char[]แทนStringเมื่อจัดการกับรหัสผ่านในลักษณะที่ปลอดภัย
SnakeDoc

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

3
@Dawesi - At which point- เป็นแอปพลิเคชั่นที่เฉพาะเจาะจง แต่กฎทั่วไปจะต้องทำทันทีที่คุณได้รับบางสิ่งบางอย่างที่ควรจะเป็นรหัสผ่าน (ธรรมดาหรืออย่างอื่น) คุณได้รับจากเบราว์เซอร์เป็นส่วนหนึ่งของคำขอ HTTP ตัวอย่างเช่น คุณไม่สามารถควบคุมการจัดส่งได้ แต่คุณสามารถควบคุมที่เก็บข้อมูลของคุณได้ทันทีที่คุณได้รับมันให้ใส่ถ่าน [] ทำสิ่งที่คุณต้องทำกับมันจากนั้นตั้งค่าทั้งหมดเป็น '0' และปล่อยให้ gc เรียกคืนมัน
luis.espinal

354

สามารถใช้อักขระอาร์เรย์ ( char[]) หลังจากการใช้งานโดยตั้งค่าอักขระแต่ละตัวให้เป็นศูนย์และไม่ใช้สตริง หากใครบางคนสามารถเห็นภาพหน่วยความจำพวกเขาสามารถเห็นรหัสผ่านในรูปแบบข้อความธรรมดาหากมีการใช้ Strings แต่ถ้าchar[]ใช้หลังจากล้างข้อมูลด้วย 0 แล้วรหัสผ่านจะปลอดภัย


13
ไม่ปลอดภัยตามค่าเริ่มต้น หากเราพูดถึงเว็บแอปพลิเคชันเว็บคอนเทนเนอร์ส่วนใหญ่จะส่งรหัสผ่านไปยังHttpServletRequestวัตถุในรูปแบบธรรมดา หากเวอร์ชั่น JVM เป็น 1.6 หรือต่ำกว่านั้นจะอยู่ในพื้นที่อนุญาต หากเป็น 1.7 มันจะยังคงสามารถอ่านได้จนกว่าจะได้รับการรวบรวม (เมื่อใดก็ตามที่เป็น.)
avgvstvs

4
@avgvstvs: สตริงจะไม่ถูกย้ายโดยอัตโนมัติไปยังพื้นที่ Permgen ที่ใช้เฉพาะกับสตริง Intern'ed นอกจากนั้นพื้นที่เพอร์เมนยังขึ้นอยู่กับการเก็บขยะด้วยอัตราที่ต่ำกว่า ปัญหาที่แท้จริงของพื้นที่ Permgen คือขนาดที่แน่นอนซึ่งเป็นเหตุผลว่าทำไมไม่มีใครควรโทรไปหาสายที่ไม่มีintern()กฎเกณฑ์ แต่คุณถูกต้องในStringกรณีที่มีอยู่ในสถานที่แรก (จนกระทั่งรวบรวม) และเปลี่ยนเป็นchar[]อาร์เรย์หลังจากนั้นจะไม่เปลี่ยนแปลง
Holger

3
@Holger ดูdocs.oracle.com/javase/specs/jvms/se6/html/ … "มิฉะนั้นอินสแตนซ์ใหม่ของคลาสสตริงจะถูกสร้างขึ้นซึ่งมีลำดับอักขระ Unicode ที่กำหนดโดยโครงสร้าง CONSTANT_String_info ซึ่งอินสแตนซ์ของคลาสนั้นเป็นผลลัพธ์ของ สตริงที่ได้มาตามตัวอักษรสุดท้ายวิธีการฝึกงานของอินสแตนซ์สตริงใหม่จะถูกเรียกใช้ " ใน 1.6 JVM จะโทรหาคุณเมื่อตรวจพบลำดับที่เหมือนกัน
avgvstvs

3
@Holger คุณถูกต้องผมแฟทต์สระว่ายน้ำสระว่ายน้ำคงที่และสตริง แต่มันก็ยังเป็นเท็จว่าพื้นที่ PermGen เพียงนำไปใช้กับสตริง interned ก่อนหน้า 1.7, constant_pool และ string_pool อาศัยอยู่ในพื้นที่ permgen นั่นหมายถึงคลาสเดียวของ Strings ที่จัดสรรให้กับฮีปเป็นอย่างที่คุณพูดnew String()หรือStringBuilder.toString() ฉันจัดการแอปพลิเคชั่นที่มีค่าคงที่สตริงจำนวนมากและเรามีจำนวนเรนเก้เพิ่มขึ้น จนถึง 1.7
avgvstvs

5
@avgvstvs: เอ่อค่าคงที่สตริงคือในขณะที่คำสั่ง JLS มักจะฝึกงานดังนั้นคำสั่งที่สตริงการฝึกงานสิ้นสุดลงในพื้นที่ Permgen ใช้กับค่าคงที่สตริงโดยปริยาย ความแตกต่างเพียงอย่างเดียวคือค่าคงที่สตริงถูกสร้างขึ้นในพื้นที่ Permgen ในสถานที่แรกในขณะที่การเรียกintern()ใช้สตริงโดยพลการอาจทำให้การจัดสรรสตริงที่เทียบเท่าในพื้นที่ Permgen หลังอาจได้รับ GC'ed หากไม่มีสตริงตัวอักษรของเนื้อหาเดียวกันที่ใช้ร่วมกันวัตถุที่ ...
Holger

220

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

ปรับปรุง

ขอบคุณความคิดเห็นที่ฉันต้องปรับปรุงคำตอบของฉัน เห็นได้ชัดว่ามีสองกรณีที่สิ่งนี้สามารถเพิ่มการปรับปรุงความปลอดภัยเล็กน้อย (มาก) เพราะจะช่วยลดเวลาที่รหัสผ่านสามารถลงบนฮาร์ดไดรฟ์ ยังฉันคิดว่ามัน overkill สำหรับกรณีใช้งานมากที่สุด

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

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


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

44
การเช็ดข้อมูลที่ไม่ได้เข้ารหัสจากหน่วยความจำทันทีที่คุณทำเสร็จก็ถือว่าเป็นการปฏิบัติที่ดีที่สุดไม่ใช่เพราะมันไม่สามารถป้องกันได้ (ไม่ใช่) แต่เนื่องจากมันช่วยลดระดับการสัมผัสภัยคุกคามของคุณ การโจมตีแบบเรียลไทม์จะไม่ถูกป้องกันโดยการทำเช่นนี้ แต่เนื่องจากมันทำหน้าที่เป็นเครื่องมือลดความเสียหายโดยลดจำนวนข้อมูลที่เปิดเผยในการโจมตีย้อนหลังบนภาพรวมหน่วยความจำ (เช่นสำเนาหน่วยความจำแอปของคุณที่เขียนไปยังไฟล์ swap หรืออ่านหน่วยความจำหมด จากเซิร์ฟเวอร์ที่ใช้งานอยู่และย้ายไปยังเซิร์ฟเวอร์อื่นก่อนที่สถานะจะล้มเหลว)
Dan Is Fiddling โดย Firelight

9
ฉันมักจะเห็นด้วยกับทัศนคติของคำตอบนี้ ฉันต้องการที่จะเสนอการละเมิดความปลอดภัยส่วนใหญ่ที่เกิดขึ้นในระดับที่สูงกว่านามธรรมมากกว่าบิตในหน่วยความจำ แน่นอนว่าอาจมีสถานการณ์ในระบบป้องกันความปลอดภัยสูงซึ่งอาจเป็นเรื่องที่น่ากังวลมาก แต่การคิดอย่างจริงจังในระดับนี้เกินความเป็นจริงสำหรับแอปพลิเคชั่น 99% ที่. NET หรือ Java ถูกยกระดับ (เนื่องจากเกี่ยวข้องกับการรวบรวมขยะ)
kingdango

10
หลังจาก Heartbleed การเจาะของหน่วยความจำเซิร์ฟเวอร์เปิดเผยรหัสผ่านฉันจะแทนที่สตริง "เพียงแค่การปรับปรุงความปลอดภัยเล็กน้อย" ด้วย "จำเป็นอย่างยิ่งที่จะไม่ใช้ String สำหรับรหัสผ่าน แต่ใช้อักขระ [] แทน"
Peter vdL

9
@PetervdL heartbleed อนุญาตให้อ่านเฉพาะชุดคอลเลกชันที่นำกลับมาใช้ใหม่ของบัฟเฟอร์ (ใช้สำหรับทั้งข้อมูลสำคัญด้านความปลอดภัยและเครือข่าย I / O โดยไม่ต้องล้างข้อมูลระหว่าง - สำหรับเหตุผลด้านประสิทธิภาพ) คุณไม่สามารถรวมสิ่งนี้กับ Java String ได้ . คุณไม่สามารถอ่านลงในหน่วยความจำแบบสุ่มโดยใช้ Java เพื่ออ่านเนื้อหาของสตริงได้ ปัญหาเกี่ยวกับภาษาและการออกแบบที่นำไปสู่ปัญหาในใจไม่สามารถทำได้ด้วย Java Strings
josefx

86

ฉันไม่คิดว่านี่เป็นข้อเสนอแนะที่ถูกต้อง แต่อย่างน้อยฉันก็สามารถเดาได้ด้วยเหตุผล

ฉันคิดว่าแรงจูงใจต้องการให้แน่ใจว่าคุณสามารถลบการติดตามทั้งหมดของรหัสผ่านในหน่วยความจำทันทีและด้วยความมั่นใจหลังจากใช้งาน ด้วยchar[]คุณสามารถเขียนทับแต่ละองค์ประกอบของอาร์เรย์ด้วยช่องว่างหรือบางสิ่งบางอย่างได้อย่างแน่นอน คุณไม่สามารถแก้ไขค่าภายในของStringวิธีนั้นได้

แต่นั่นไม่ใช่คำตอบที่ดีเพียงอย่างเดียว ทำไมไม่ให้แน่ใจว่ามีการอ้างอิงถึงchar[]หรือStringไม่หลบหนี จากนั้นไม่มีปัญหาด้านความปลอดภัย แต่สิ่งนี้คือStringวัตถุสามารถถูกแก้ไขได้intern()ในทางทฤษฎีและมีชีวิตอยู่ภายในสระว่ายน้ำคงที่ ฉันคิดว่าใช้char[]ความเป็นไปได้นี้ห้าม


4
ฉันจะไม่พูดว่าปัญหาคือการอ้างอิงของคุณจะหรือไม่ "หลบหนี" มันเป็นเพียงแค่ว่าสตริงจะยังคงไม่ได้แก้ไขในหน่วยความจำสำหรับเวลาเพิ่มเติมในขณะที่char[]สามารถแก้ไขได้และจากนั้นก็ไม่เกี่ยวข้องว่าจะรวบรวมหรือไม่ และเนื่องจากการฝึกงานสตริงต้องทำอย่างชัดเจนสำหรับผู้ที่ไม่ใช่ตัวอักษรมันเหมือนกับการบอกว่า a char[]สามารถถูกอ้างอิงโดยฟิลด์สแตติก
Groo

1
รหัสผ่านในหน่วยความจำไม่เป็นสตริงจากการโพสต์แบบฟอร์มใช่หรือไม่
Dawesi

66

ได้รับคำตอบแล้ว แต่ฉันต้องการแบ่งปันปัญหาที่ฉันค้นพบเมื่อเร็ว ๆ นี้กับไลบรารีมาตรฐาน Java ในขณะที่พวกเขาใช้ความระมัดระวังอย่างมากในการแทนที่สตริงรหัสผ่านด้วยchar[]ทุกที่ (ซึ่งแน่นอนว่าเป็นสิ่งที่ดี) ข้อมูลด้านความปลอดภัยที่สำคัญอื่น ๆ ดูเหมือนว่าจะถูกมองข้ามเมื่อพูดถึงการล้างข้อมูลออกจากหน่วยความจำ

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

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

นี่เป็นสถานการณ์ที่ไม่ดีเนื่องจากบทความนี้จะอธิบายถึงสถานการณ์ที่อาจถูกเอารัดเอาเปรียบ

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


วิธีหนึ่งในการทำเช่นนี้คือการใช้การดำเนินการPrivateKeyที่ไม่ได้โหลดเนื้อหาส่วนตัวในหน่วยความจำ: ผ่านโทเค็นฮาร์ดแวร์ PKCS # 11 บางทีการติดตั้งซอฟต์แวร์ของ PKCS # 11 อาจช่วยล้างหน่วยความจำด้วยตนเองได้ บางทีการใช้บางอย่างเช่นร้านค้า NSS (ซึ่งใช้งานส่วนใหญ่กับPKCS11ประเภทร้านค้าใน Java) จะดีกว่า KeychainStore(OSX keystore) โหลดเนื้อหาทั้งหมดของคีย์ส่วนตัวเป็นของPrivateKeyอินสแตนซ์ แต่ก็ไม่ควรที่จะต้อง (ไม่แน่ใจว่าWINDOWS-MYKeyStore ทำงานบน Windows ได้อย่างไร)
บรูโน่

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

อย่างแน่นอนฉันแค่สงสัยว่าการใช้งานซอฟต์แวร์บางอย่างเทียบเท่ากับ HSM อาจทำงานได้ดีขึ้นในแง่ของการทำความสะอาดหน่วยความจำ ตัวอย่างเช่นเมื่อใช้ไคลเอ็นต์การรับรองความถูกต้องกับ Safari / OSX กระบวนการ Safari ไม่เคยเห็นคีย์ส่วนตัวจริงห้องสมุด SSL พื้นฐานที่จัดทำโดยระบบปฏิบัติการพูดโดยตรงกับภูตความปลอดภัยซึ่งแจ้งให้ผู้ใช้สำหรับการใช้กุญแจจากพวงกุญแจ ในขณะที่ทั้งหมดนี้ทำในซอฟต์แวร์การแยกที่คล้ายกันนี้อาจช่วยได้ถ้าการเซ็นชื่อถูกมอบหมายให้กับเอนทิตีที่แตกต่างกัน
Bruno

@Bruno: ความคิดที่น่าสนใจเลเยอร์ทางอ้อมเพิ่มเติมที่ดูแลการล้างหน่วยความจำแน่นอนจะแก้ปัญหานี้อย่างโปร่งใส การเขียนตัวห่อหุ้ม PKCS # 11 สำหรับที่เก็บคีย์ซอฟต์แวร์สามารถทำเคล็ดลับได้หรือไม่?
นูน

51

ในฐานะที่เป็น Jon Skeet รัฐไม่มีทางยกเว้นโดยใช้การสะท้อน

อย่างไรก็ตามหากการสะท้อนกลับเป็นตัวเลือกสำหรับคุณคุณสามารถทำได้

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

เมื่อวิ่ง

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

หมายเหตุ: หากอักขระของ String [] ถูกคัดลอกเป็นส่วนหนึ่งของวงจร GC อาจมีโอกาสที่สำเนาก่อนหน้าจะอยู่ในหน่วยความจำ

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


2
'***********'ดีกว่าที่จะทำอะไรบางอย่างเพื่อป้องกันไม่ให้พิมพ์ยาวของรหัสผ่านของที่เราได้รับจาก
chux - Reinstate Monica

@ chux คุณสามารถใช้อักขระความกว้างเป็นศูนย์ได้ แต่นี่อาจทำให้สับสนมากกว่ามีประโยชน์ เป็นไปไม่ได้ที่จะเปลี่ยนความยาวของอาเรย์ถ่านโดยไม่ใช้ Unsafe ;)
Peter Lawrey

5
เนื่องจากการคัดลอก String ของ Java 8 ฉันคิดว่าการทำบางอย่างเช่นนั้นอาจทำลายล้างได้ ... คุณอาจจะล้างสตริงอื่น ๆ ในโปรแกรมของคุณโดยบังเอิญว่ามีค่ารหัสผ่านสตริงเท่ากัน ไม่แน่ แต่เป็นไปได้ ...
JAMP

1
@PeterLawrey ต้องเปิดใช้งานด้วยอาร์กิวเมนต์ JVM แต่มีอยู่ สามารถอ่านได้ที่นี่: blog.codecentric.de/en/2014/08/…
jamp

1
มีโอกาสสูงที่รหัสผ่านยังคงอยู่ในScannerบัฟเฟอร์ภายในและเนื่องจากคุณไม่ได้ใช้System.console().readPassword()ในรูปแบบที่อ่านได้ในหน้าต่างคอนโซล แต่สำหรับกรณีที่ใช้งานได้จริงส่วนใหญ่ระยะเวลาของusePasswordการเรียกใช้งานจะเป็นปัญหาจริง เช่นเมื่อทำการเชื่อมต่อกับเครื่องอื่นมันต้องใช้เวลาและบอกผู้โจมตีว่าเป็นเวลาที่เหมาะสมในการค้นหารหัสผ่านในฮีป ทางออกเดียวคือการป้องกันไม่ให้ผู้โจมตีอ่านหน่วยความจำฮีป…
ฮอลเกอร์

42

นี่คือเหตุผลทั้งหมดที่ควรเลือกอาร์เรย์[]แทนที่จะเป็นสตริงสำหรับรหัสผ่าน

1. Strings นั้นไม่เปลี่ยนรูปแบบใน Java ถ้าคุณเก็บรหัสผ่านเป็นข้อความธรรมดามันจะมีอยู่ในหน่วยความจำจนกว่า Garbage Collector จะล้างมันและเนื่องจาก String ถูกใช้ใน Pool pool เพื่อให้สามารถนำมาใช้ใหม่ได้ ยังคงอยู่ในหน่วยความจำเป็นเวลานานซึ่งก่อให้เกิดภัยคุกคามความปลอดภัย

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

2. Java แนะนำให้ใช้เมธอด getPassword () ของ JPasswordField ซึ่งส่งคืนอักขระ char [] แทนเมธอด getText () ที่เลิกใช้แล้วซึ่งส่งคืนรหัสผ่านในรูปแบบข้อความที่ชัดเจนด้วยเหตุผลด้านความปลอดภัย เป็นการดีที่จะทำตามคำแนะนำจากทีม Java และยึดมั่นในมาตรฐานแทนที่จะไปเทียบกับพวกเขา

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

String strPassword="Unknown";
char[] charPassword= new char[]{'U','n','k','w','o','n'};
System.out.println("String password: " + strPassword);
System.out.println("Character password: " + charPassword);

String password: Unknown
Character password: [C@110b053

อ้างอิงจากบล็อกนี้ ฉันหวังว่านี่จะช่วยได้.


10
นี่คือซ้ำซ้อน คำตอบนี้เป็นรุ่นที่ถูกต้องของคำตอบที่เขียนโดย @SrujanKumarGulla stackoverflow.com/a/14060804/1793718 โปรดอย่าคัดลอกวางหรือทำซ้ำคำตอบเดียวกันสองครั้ง
Lucky

ความแตกต่างระหว่าง 1. ) System.out.println ("รหัสผ่านตัวอักษร:" + charPassword); และ 2. ) System.out.println (charPassword); เพราะมันให้ "ไม่ทราบ" เหมือนเอาท์พุท
Vaibhav_Sharma

3
@ Lucky โชคไม่ดีที่คำตอบเก่าที่คุณเชื่อมโยงไปถูกคัดลอกมาจากบล็อกเดียวกันกับคำตอบนี้และถูกลบ ดูmeta.stackoverflow.com/questions/389144/... คำตอบนี้ตัดและวางจากบล็อกเดียวกันและไม่ได้เพิ่มอะไรเลยดังนั้นควรมีเพียงความคิดเห็นที่ลิงก์ไปยังแหล่งต้นฉบับ
skomisa

41

แก้ไข:กลับมาที่คำตอบนี้หลังจากหนึ่งปีของการวิจัยความปลอดภัยฉันรู้ว่ามันทำให้เกิดความโชคร้ายที่คุณจะเปรียบเทียบรหัสผ่านธรรมดา โปรดอย่า ใช้กัญชาหนึ่งในวิธีการที่ปลอดภัยด้วยเกลือและจำนวนที่เหมาะสมของการทำซ้ำ ลองใช้ห้องสมุด: สิ่งนี้ยากที่จะเข้าใจ!

คำตอบเดิม:อะไรคือความจริงที่ว่า String.equals () ใช้การประเมินผลแบบลัดวงจรและดังนั้นจึงมีความเสี่ยงต่อการถูกโจมตีด้วยจังหวะ? อาจไม่น่าเป็นไปได้ แต่คุณสามารถใช้เวลาในการเปรียบเทียบรหัสผ่านในทางทฤษฎีเพื่อกำหนดลำดับอักขระที่ถูกต้อง

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // Quits here if Strings are different lengths.
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // Quits here at first different character.
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

แหล่งข้อมูลเพิ่มเติมเกี่ยวกับการโจมตีเวลา:


แต่นั่นอาจจะมีในการเปรียบเทียบ [] เช่นกันที่ไหนสักแห่งที่เราจะทำเช่นเดียวกันในการตรวจสอบรหัสผ่านด้วย ดังนั้น char [] ดีกว่าสตริงอย่างไร
Mohit Kanwar

3
คุณถูกต้องอย่างแน่นอนความผิดพลาดสามารถทำได้ทั้งทาง การรู้เกี่ยวกับปัญหาเป็นสิ่งที่สำคัญที่สุดที่นี่เนื่องจากไม่มีวิธีการเปรียบเทียบรหัสผ่านอย่างชัดเจนใน Java สำหรับรหัสผ่านแบบใช้สตริงหรือแบบถ่าน [] ฉันว่าสิ่งล่อใจที่จะใช้การเปรียบเทียบ () สำหรับสตริงเป็นเหตุผลที่ดีที่จะไปกับถ่าน [] อย่างน้อยคุณก็สามารถควบคุมวิธีการเปรียบเทียบได้ (โดยไม่ต้องขยาย String ซึ่งเป็นอาการเจ็บปวด)
ทฤษฎีกราฟ

นอกจากนี้การเปรียบเทียบรหัสผ่านธรรมดาไม่ได้เป็นสิ่งที่ถูกต้องแล้วในการทดลองที่จะใช้Arrays.equalsสำหรับเป็นที่สูงที่สุดเท่าสำหรับchar[] String.equalsหากใครสนใจก็มีคลาสคีย์เฉพาะที่ห่อหุ้มรหัสผ่านจริงและดูแลปัญหา - รอเดี๋ยวก่อนแพคเกจความปลอดภัยที่แท้จริงมีคลาสคีย์เฉพาะคำถาม & คำตอบนี้เกี่ยวกับนิสัยนอกพวกเขาเช่นJPasswordFieldใช้char[]แทนString(ซึ่งอัลกอริทึมที่แท้จริงใช้byte[]อยู่แล้ว)
Holger

ซอฟต์แวร์ที่เกี่ยวข้องกับความปลอดภัยควรทำอะไรบางอย่างเช่นsleep(secureRandom.nextInt())ก่อนที่จะปฏิเสธความพยายามในการเข้าสู่ระบบซึ่งไม่เพียง แต่กำจัดความเป็นไปได้ของการโจมตีเวลาเท่านั้น
Holger

36

ไม่มีสิ่งใดที่อาร์เรย์ char จะให้คุณเทียบกับสตริงเว้นแต่คุณจะล้างข้อมูลด้วยตนเองหลังการใช้งานและฉันไม่ได้เห็นใครทำเช่นนั้น ดังนั้นสำหรับฉันความชอบของ char [] vs String นั้นค่อนข้างโอ้อวดไปหน่อย

ลองดูที่ห้องสมุด Spring Security ที่ใช้กันอย่างแพร่หลายที่นี่และถามตัวเอง - รหัสผ่านของ Spring Security ไม่สามารถใช้งานได้หรือใช้รหัสผ่าน [ถ่าน] ก็ไม่สมเหตุสมผล เมื่อแฮกเกอร์ที่น่ารังเกียจบางคนคว้าหน่วยความจำของแรมของคุณให้แน่ใจว่าเขา / เธอจะได้รับรหัสผ่านทั้งหมดแม้ว่าคุณจะใช้วิธีการที่ซับซ้อนเพื่อซ่อนพวกเขา

อย่างไรก็ตาม Java เปลี่ยนแปลงตลอดเวลาและคุณสมบัติบางอย่างที่น่ากลัวเช่นคุณลักษณะการคัดลอกสตริงของ Java 8อาจฝึกงานกับวัตถุสตริงโดยที่คุณไม่ทราบ แต่นั่นคือการสนทนาที่แตกต่างกัน


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

@ โฮลเกอร์อะไรก็ตามที่อยู่นอกเหนือการควบคุมของคุณเป็นความเสี่ยงที่อาจเกิดขึ้น ... ตัวอย่างเช่นหากผู้ใช้สองคนมีรหัสผ่านเดียวกันฟีเจอร์ที่ยอดเยี่ยมนี้จะเก็บทั้งสองอย่างไว้ในถ่านเดียว [] ทำให้เห็นได้ชัดว่าพวกเขาเหมือนกัน เป็นความเสี่ยงที่ยิ่งใหญ่ แต่ก็ยัง
Oleg Mikheev

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

@ โฮลเกอร์เพื่อยืนยันรหัสผ่านจะต้องเป็นข้อความที่ชัดเจนในหน่วยความจำระยะเวลา 10 มิลลิวินาทีแม้ว่ามันจะเป็นเพียงการสร้างแฮ็คเค็มจากมัน จากนั้นหากเกิดขึ้นว่ามีรหัสผ่านที่เหมือนกันสองรหัสเก็บไว้ในหน่วยความจำแม้กระทั่ง 10 มิลลิวินาทีการขจัดข้อมูลซ้ำซ้อนอาจเข้ามาเล่น ถ้ามันฝึกงานสตริงพวกเขาจะถูกเก็บไว้ในหน่วยความจำเป็นเวลานาน ระบบที่ไม่รีสตาร์ทเป็นเวลาหลายเดือนจะรวบรวมสิ่งเหล่านี้มากมาย เพียงแค่สร้างทฤษฎี
Oleg Mikheev

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

30

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

ในอีกด้านหนึ่งอักขระ char [] สามารถเปลี่ยนแปลงได้ทันทีที่การตรวจสอบสิทธิ์เสร็จสิ้นคุณสามารถเขียนทับอักขระเหล่านั้นด้วยอักขระใด ๆ เช่น M หรือแบ็กสแลชทั้งหมด ตอนนี้แม้ว่าบางคนใช้การถ่ายโอนข้อมูลฮีปเขาอาจไม่สามารถรับรหัสผ่านที่ไม่ได้ใช้งานอยู่ในปัจจุบัน สิ่งนี้ช่วยให้คุณสามารถควบคุมได้มากขึ้นเช่นการล้างเนื้อหาวัตถุด้วยตัวคุณเองและรอให้ GC ทำเช่นนั้น


พวกเขาจะเป็น GC'd หาก JVM ที่เป็นปัญหานั้น> 1.6 ก่อนหน้า 1.7, สตริงทั้งหมดถูกเก็บไว้ใน Permgen
avgvstvs

@avgvstvs:“ สตริงทั้งหมดถูกเก็บไว้ใน permgen” เป็นเพียงความผิดพลาด มีการจัดเก็บสตริงที่ใช้ภายในเท่านั้นและหากไม่ได้มาจากตัวอักษรสตริงที่อ้างอิงด้วยรหัสพวกเขายังคงเก็บรวบรวมขยะ แค่คิดเกี่ยวกับมัน หากสตริงไม่ปกติ GCed ใน JVMs ก่อน 1.7, แอปพลิเคชัน Java ใดสามารถอยู่รอดได้ในเวลาไม่กี่นาที?
Holger

@ Holger นี้เป็นเท็จ Interned stringsและStringpool (พูลของสตริงที่ใช้ก่อนหน้านี้) ทั้งสองถูกเก็บไว้ใน Permgen ก่อน 1.7 ดูที่หัวข้อ 5.1: docs.oracle.com/javase/specs/jvms/se6/html/ ...... JVM จะตรวจสอบเสมอStringsเพื่อดูว่าเป็นค่าอ้างอิงเดียวกันหรือไม่และจะโทรหาString.intern()คุณ ผลที่ได้คือทุกครั้งที่ JVM ตรวจพบ Strings ที่เหมือนกันในconstant_poolheap หรือ heap มันจะย้ายมันไปยัง permgen และฉันทำงานกับแอพพลิเคชั่นหลายตัวด้วย "creeping permgen" จนถึง 1.7 มันเป็นปัญหาที่แท้จริง
avgvstvs

ดังนั้นการสรุป: จนถึง 1.7, สตริงเริ่มต้นในกองเมื่อพวกเขาถูกนำมาใช้พวกเขาถูกใส่เข้าไปในconstant_poolที่ตั้งอยู่ใน permgen และจากนั้นถ้าใช้สตริงมากกว่าหนึ่งครั้งมันจะถูก interned
avgvstvs

@avgvstvs: ไม่มี“ กลุ่มของสตริงที่ใช้ก่อนหน้านี้” คุณกำลังขว้างปาสิ่งต่าง ๆ ทั้งหมดเข้าด้วยกัน มีกลุ่มสตริงรันไทม์หนึ่งตัวที่มีตัวอักษรสตริงและสตริงฝึกงานอย่างชัดเจน แต่ไม่มีอื่น ๆ และแต่ละคลาสมีพูลคงที่ที่มีค่าคงที่เวลาคอมไพล์ สตริงเหล่านี้จะมีการเพิ่มโดยอัตโนมัติไปยังสระว่ายน้ำรันไทม์ แต่เพียงเหล่านี้ไม่ได้ทุกสตริง
Holger

21

สตริงไม่เปลี่ยนรูปและมันจะไปที่สระว่ายน้ำสตริง เมื่อเขียนแล้วจะไม่สามารถเขียนทับได้

char[] เป็นอาร์เรย์ที่คุณควรเขียนทับเมื่อคุณใช้รหัสผ่านและนี่คือวิธีการทำ:

char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
 allowUser = true;
 cleanPassword(passw);
 cleanPassword(dbPassword);
 passw=null;
}

private static void cleanPassword (char[] pass) {

Arrays.fill(pass, '0');
}

สถานการณ์หนึ่งที่ผู้โจมตีสามารถใช้งานได้คือ crashdump - เมื่อ JVM หยุดทำงานและสร้างการถ่ายโอนข้อมูลหน่วยความจำ - คุณจะสามารถดูรหัสผ่านได้

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


2
ch = null; คุณไม่สามารถทำเช่นนี้ได้
Yugerten

แต่ยังไม่request.getPassword()ได้สร้างสตริงและเพิ่มลงในพูลหรือไม่
Tvde1

1
ch = '0'การเปลี่ยนแปลงตัวแปรท้องถิ่นch; มันไม่มีผลกับอาเรย์ และตัวอย่างของคุณไม่มีจุดหมายอยู่แล้วคุณเริ่มต้นด้วยสตริงอินสแตนซ์ที่คุณเรียกtoCharArray()ใช้สร้างอาร์เรย์ใหม่และแม้กระทั่งเมื่อคุณเขียนทับอาร์เรย์ใหม่อย่างถูกต้องมันจะไม่เปลี่ยนอินสแตนซ์ของสตริงดังนั้นจึงไม่มีข้อได้เปรียบเหนือการใช้สตริง ตัวอย่าง.
Holger

@ Holger ขอบคุณ แก้ไขรหัสการล้างข้อมูลอาร์เรย์ char
ACV

3
คุณสามารถใช้Arrays.fill(pass, '0');
Holger

18

คำตอบสั้น ๆ และตรงไปตรงมาอาจเป็นเพราะchar[]ไม่แน่นอนในขณะที่Stringวัตถุไม่ได้

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

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

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


@fallenidol ไม่เลย อ่านอย่างละเอียดแล้วคุณจะพบความแตกต่าง
Pritam Banerjee

12

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

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

เนื่องจากข้อกังวลด้านความปลอดภัยจึงเป็นการดีกว่าที่จะเก็บรหัสผ่านเป็นอาเรย์อักขระ


2

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

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

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

จากสถานการณ์ข้างต้นเราสามารถทราบได้ว่าจะไปกับ String หรือไปกับ Char [] สำหรับความต้องการของพวกเขา


0

สตริงตัวเรือน:

    String password = "ill stay in StringPool after Death !!!";
    // some long code goes
    // ...Now I want to remove traces of password
    password = null;
    password = "";
    // above attempts wil change value of password
    // but the actual password can be traced from String pool through memory dump, if not garbage collected

เคส CHAR ARRAY:

    char[] passArray = {'p','a','s','s','w','o','r','d'};
    // some long code goes
    // ...Now I want to remove traces of password
    for (int i=0; i<passArray.length;i++){
        passArray[i] = 'x';
    }
    // Now you ACTUALLY DESTROYED traces of password form memory
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.