Câu hỏi Tại sao char [] được ưu tiên hơn String cho mật khẩu?


Trong Swing, trường mật khẩu có một getPassword() (trả về char[]) thay vì thông thường getText() (trả về String) phương pháp. Tương tự, tôi đã gặp một đề xuất không sử dụng String để xử lý mật khẩu.

Tại sao String đặt ra một mối đe dọa đến an ninh khi nói đến mật khẩu? Nó cảm thấy bất tiện khi sử dụng char[].


2901
2018-01-16 14:20


gốc


Bạn có thể trích dẫn nguồn gốc của đề xuất không? Tôi thực sự không thể nghĩ ra một lý do char[] an toàn hơn ngoại trừ các mối đe dọa nghiệp dư nhất. - Viruzzo
@Viruzzo hãy xem các javadocs. Các getText() phương pháp của JPasswordField không được ủng hộ getPassword() "vì lý do bảo mật". - dogbane
char []? Tại sao không phải byte []? Trong bất kỳ trình phân tích dump / heap thread nào, bạn có thể thấy các chuỗi, và rất dễ nhận thấy nếu có chứa một mật khẩu. Đối với char [] nó khó hơn, nhưng vẫn còn, nếu bộ phân tích heap của bạn sẽ hiển thị ký tự từng cái một, nó sẽ giống như String. Đối với byte [] thì không thể. - mvmn
Chỉ để cung cấp một kịch bản hợp lệ, điều này có thể quan trọng, đó là một thực tế phổ biến trong pháp y để đảm bảo rằng không có máy tính yêu cầu điều tra nào bị tắt trước khi bộ nhớ của chúng bị đổ, trong trường hợp chúng đang sử dụng mã hóa phần mềm. Ngay cả hệ thống mã hóa tốt nhất cũng cần duy trì trạng thái có khả năng giải mã. - Zenexer
@scottlafoy Cả hai char[] và String các đối tượng sẽ được truyền qua tham chiếu; cá nhân char giá trị sẽ không được. Đó là khi vật thể được truyền qua giá trị, không tham chiếu, nhiều bản sao được tạo. Nói chung, đây không phải là một vấn đề, vì các đối tượng như vậy thường được thiết kế nhỏ. - Zenexer


Các câu trả lời:


Các chuỗi không thay đổi được. Điều đó có nghĩa là khi bạn đã tạo String, nếu một quá trình khác có thể hủy bộ nhớ, không có cách nào (ngoài sự phản chiếu) bạn có thể loại bỏ dữ liệu trước thu gom rác thải đá vào.

Với một mảng, bạn có thể xóa sạch dữ liệu một cách rõ ràng sau khi bạn đã hoàn thành nó. Bạn có thể ghi đè lên mảng bằng bất cứ thứ gì bạn thích và mật khẩu sẽ không có mặt ở bất kỳ nơi nào trong hệ thống, ngay cả trước khi thu thập rác.

Vì vậy, có, điều này  mối quan tâm về bảo mật - nhưng thậm chí sử dụng char[] chỉ làm giảm cửa sổ cơ hội cho kẻ tấn công và nó chỉ dành cho loại tấn công cụ thể này.

Như đã lưu ý trong các nhận xét, có thể các mảng được di chuyển bởi bộ thu gom rác sẽ để lại các bản sao đi lạc của dữ liệu trong bộ nhớ. Tôi tin rằng đây là triển khai cụ thể - bộ thu gom rác có thể xóa tất cả bộ nhớ khi nó đi, để tránh điều này. Ngay cả khi nó có, vẫn còn thời gian trong đó char[] chứa các ký tự thực tế như một cửa sổ tấn công.


3720
2018-01-16 14:26



Sự hiểu biết của tôi là với số lượng sắp xếp lại bộ nhớ có thể được thực hiện với thời gian chạy, các bản sao thậm chí là char[] có thể còn lại trong bộ nhớ và không bao giờ bị xóa cho đến khi bộ nhớ đó được sử dụng lại. Tôi không biết một nguồn xác nhận rằng mặc dù. Dù bằng cách nào tôi nghi ngờ String sẽ được xóa tại GC thời gian nhưng đúng vào thời điểm một đối tượng được phân bổ lại trên bộ nhớ đó. - Mark Peters
@Mark Peters: Nếu GC đá vào, trước khi bạn làm trống mảng char, bạn có một bản sao khác. Nhưng bản sao này ít nhất là trong một khu vực được sử dụng nặng nề của bộ nhớ, vì vậy cơ hội cao nó được ghi đè nhanh chóng. - Mnementh
@ Xeon06: Trong .NET có SecureString đó là tốt hơn - nhưng tương đối đau đớn để sử dụng. - Jon Skeet
Có phải chúng ta đang chú ý đến thực tế rằng một thực thể độc hại có thể đọc dữ liệu này đã có quyền truy cập bộ nhớ đọc vào hệ thống của bạn không? Có vẻ như đó sẽ là một mối quan tâm lớn hơn khi thực hiện điều này char [] thay vì String chỉ có vẻ như đang nổi giận trong đại dương. - corsiKa
@corsika: Như tôi đã đề cập trong câu trả lời, nó giảm một vector tấn công cụ thể, đó là tất cả. Nó làm cho mọi thứ khó khăn hơn cho kẻ tấn công. - Jon Skeet


Trong khi các đề xuất khác ở đây có vẻ hợp lệ, có một lý do chính đáng khác. Với đồng bằng String bạn có nhiều cơ hội cao hơn vô tình in mật khẩu vào nhật ký, màn hình hoặc một số nơi không an toàn khác. char[] ít bị tổn thương hơn.

Xem xét điều này:

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

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

Bản in:

String: Password
Array: [C@5829428e

1080
2018-01-16 19:37



@ voo, nhưng tôi nghi ngờ bạn sẽ đăng nhập thông qua văn bản trực tiếp để dòng và nối. khung khai thác gỗ sẽ biến đổi char [] thành đầu ra tốt - bestsss
@ Thr4wn Mặc định thực hiện toString Là classname@hashcode. [C đại diện char[], phần còn lại là mã băm thập lục phân. - Konrad Garus
Ý tưởng thú vị. Tôi muốn chỉ ra rằng điều này không chuyển sang Scala mà có một toString có ý nghĩa cho mảng. - mauhiz
Tôi viết một Password loại lớp cho việc này. Nó ít mơ hồ, và khó hơn để vô tình vượt qua một nơi nào đó. - rightfold
Tại sao một người nào đó giả định mảng char sẽ được chọn làm Object? Tôi không chắc mình có lý do tại sao mọi người đều thích câu trả lời này. Giả sử bạn đã làm điều này: System.out.println ("Mật khẩu". ToCharArray ()); - GC_


Để trích dẫn một tài liệu chính thức, Hướng dẫn Kiến trúc Mật mã Java nói điều này về char[] so với String mật khẩu (về mã hóa dựa trên mật khẩu, nhưng điều này nói chung là về mật khẩu của khóa học):

Nó có vẻ hợp lý để thu thập và lưu trữ mật khẩu trong một đối tượng   loại java.lang.String. Tuy nhiên, đây là báo trước: Objectcủa   kiểu String không thay đổi, tức là, không có phương pháp nào được xác định   cho phép bạn thay đổi (ghi đè) hoặc không ra nội dung của String   sau khi sử dụng. Tính năng này làm cho String đối tượng không phù hợp cho   lưu trữ thông tin nhạy cảm bảo mật như mật khẩu người dùng. Bạn   nên luôn thu thập và lưu trữ thông tin nhạy cảm về bảo mật trong    char thay vào đó.

Hướng dẫn 2-2 của Hướng dẫn mã hóa an toàn cho Ngôn ngữ lập trình Java, Phiên bản 4.0 cũng nói một cái gì đó tương tự (mặc dù nó là ban đầu trong bối cảnh khai thác gỗ):

Hướng dẫn 2-2: Không đăng nhập thông tin nhạy cảm cao

Một số thông tin, chẳng hạn như số An Sinh Xã Hội (SSN) và   mật khẩu, rất nhạy cảm. Không nên giữ thông tin này   lâu hơn cần thiết và cũng không thể nhìn thấy ở đâu, thậm chí bằng   quản trị viên. Ví dụ: không nên gửi tệp nhật ký và   sự hiện diện của nó không nên được phát hiện thông qua tìm kiếm. Một số thoáng qua   dữ liệu có thể được lưu giữ trong cấu trúc dữ liệu có thể thay đổi, chẳng hạn như mảng char và   xóa ngay lập tức sau khi sử dụng. Xóa cấu trúc dữ liệu đã giảm   hiệu quả trên các hệ thống thời gian chạy Java điển hình khi các đối tượng được di chuyển trong   bộ nhớ minh bạch cho lập trình viên.

Hướng dẫn này cũng có ý nghĩa cho việc thực hiện và sử dụng   thư viện cấp thấp hơn không có kiến ​​thức ngữ nghĩa về dữ liệu   họ đang xử lý. Ví dụ: phân tích cú pháp chuỗi cấp thấp   thư viện có thể đăng nhập văn bản mà nó hoạt động. Ứng dụng có thể phân tích cú pháp SSN   với thư viện. Điều này tạo ra một tình huống mà các SSN là   có sẵn cho quản trị viên có quyền truy cập vào tệp nhật ký.


610
2018-01-17 03:20



đây chính xác là tham chiếu sai lầm / không có thật, tôi nói về câu trả lời dưới đây của Jon, đó là một nguồn nổi tiếng với rất nhiều lời chỉ trích. - bestsss
@bestass bạn có thể vui lòng trích dẫn một tài liệu tham khảo? - user961954
@bestass xin lỗi, nhưng String được hiểu khá rõ và cách nó hoạt động trong JVM ... có những lý do tốt để sử dụng char[] thay cho String khi xử lý mật khẩu một cách an toàn. - SnakeDoc
một lần nữa mặc dù mật khẩu được thông qua như là một chuỗi từ trình duyệt để yêu cầu như 'chuỗi' không char? vì vậy không có vấn đề gì bạn làm nó là một chuỗi, tại thời điểm đó nó nên được hành động và loại bỏ, không bao giờ được lưu trữ trong bộ nhớ? - Dawesi


Mảng ký tự (char[]) có thể được xóa sau khi sử dụng bằng cách đặt từng ký tự thành 0 và không phải chuỗi. Nếu ai đó bằng cách nào đó có thể nhìn thấy hình ảnh bộ nhớ, họ có thể thấy mật khẩu ở dạng văn bản thuần túy nếu Chuỗi được sử dụng, nhưng nếu char[] được sử dụng, sau khi xóa dữ liệu bằng 0, mật khẩu được bảo mật.


305
2018-01-16 14:27



Không an toàn theo mặc định. Nếu chúng ta đang nói một ứng dụng web, hầu hết các thùng chứa web sẽ chuyển mật khẩu vào HttpServletRequest đối tượng trong bản rõ. Nếu phiên bản JVM là 1.6 hoặc thấp hơn, nó sẽ ở trong không gian permgen. Nếu nó là 1,7, nó vẫn sẽ có thể đọc được cho đến khi nó được thu thập. (Bất cứ khi nào có.) - avgvstvs
@avgvstvs: chuỗi không được tự động di chuyển đến không gian permgen, chỉ áp dụng cho các chuỗi nội bộ. Bên cạnh đó, không gian permgen cũng phụ thuộc vào việc thu gom rác thải, chỉ với tốc độ thấp hơn. Vấn đề thực sự với không gian permgen là kích thước cố định, đó chính xác là lý do tại sao không ai nên vô tình gọi intern() trên các chuỗi tùy ý. Nhưng bạn ở ngay trong đó String trường hợp tồn tại ở nơi đầu tiên (cho đến khi thu thập được) và biến chúng thành char[] mảng sau đó không thay đổi. - Holger
@Holger xem docs.oracle.com/javase/specs/jvms/se6/html/…  "Nếu không, một thể hiện mới của chuỗi lớp được tạo ra có chứa chuỗi các ký tự Unicode được đưa ra bởi cấu trúc CONSTANT_String_info; thể hiện lớp đó là kết quả của dẫn xuất chuỗi ký tự. Cuối cùng, phương thức thực thi của cá thể String mới được gọi." Trong 1.6, JVM sẽ gọi thực tập cho bạn khi nó phát hiện các chuỗi giống hệt nhau. - avgvstvs
@ Holger, bạn là chính xác tôi conflated hồ bơi liên tục và chuỗi hồ bơi, nhưng nó cũng sai rằng permgen không gian chỉ có áp dụng cho chuỗi nội bộ. Trước 1.7, cả constant_pool và string_pool đều nằm trong không gian permgen. Điều đó có nghĩa là lớp duy nhất của các chuỗi được phân bổ cho vùng heap như bạn đã nói, new String() hoặc là StringBuilder.toString()  Tôi quản lý các ứng dụng với nhiều hằng số chuỗi và chúng tôi đã có rất nhiều kết quả trùng lặp. Cho đến 1,7. - avgvstvs
@avgvstvs: tốt, chuỗi hằng số, như là nhiệm vụ JLS, luôn luôn tập trung, do đó tuyên bố rằng chuỗi interned kết thúc trong không gian permgen, áp dụng cho các hằng số chuỗi ngầm. Sự khác biệt duy nhất là hằng số chuỗi được tạo ra trong không gian permgen ở vị trí đầu tiên, trong khi gọi intern() trên một chuỗi tùy ý có thể gây ra việc phân bổ một chuỗi tương đương trong không gian permgen. Người thứ hai có thể nhận được GC, nếu không có chuỗi chữ của cùng một nội dung chia sẻ đối tượng đó… - Holger


Một số người tin rằng bạn phải ghi đè lên bộ nhớ được sử dụng để lưu trữ mật khẩu khi bạn không còn cần đến nó nữa. Điều này làm giảm cửa sổ thời gian kẻ tấn công phải đọc mật khẩu từ hệ thống của bạn và hoàn toàn bỏ qua thực tế là kẻ tấn công đã cần đủ quyền truy cập để chiếm quyền điều khiển bộ nhớ JVM để thực hiện việc này. Kẻ tấn công có nhiều quyền truy cập có thể nắm bắt các sự kiện quan trọng của bạn khiến việc này hoàn toàn vô dụng (AFAIK, vì vậy hãy sửa tôi nếu tôi sai).

Cập nhật

Nhờ có ý kiến ​​tôi phải cập nhật câu trả lời của mình. Rõ ràng có hai trường hợp mà điều này có thể thêm một (rất) bảo mật nhỏ cải thiện vì nó làm giảm thời gian một mật khẩu có thể hạ cánh trên ổ đĩa cứng. Tuy nhiên tôi nghĩ rằng nó quá mức cần thiết cho hầu hết các trường hợp sử dụng.

  • Hệ thống đích của bạn có thể bị cấu hình sai hoặc bạn phải giả định nó và bạn phải hoang tưởng về các bãi lõi (có thể hợp lệ nếu các hệ thống không được quản trị viên quản lý).
  • Phần mềm của bạn phải quá hoang tưởng để ngăn chặn rò rỉ dữ liệu với kẻ tấn công có quyền truy cập vào phần cứng - sử dụng những thứ như TrueCrypt (ngừng), VeraCrypt, hoặc là CipherShed.

Nếu có thể, vô hiệu hóa các bãi lõi và tệp hoán đổi sẽ xử lý cả hai vấn đề. Tuy nhiên, họ sẽ yêu cầu quyền quản trị và có thể giảm chức năng (ít bộ nhớ hơn để sử dụng) và kéo RAM từ hệ thống đang chạy vẫn sẽ là mối quan tâm hợp lệ.


183
2018-01-16 14:32



Tôi sẽ thay thế "hoàn toàn vô dụng" bằng "chỉ là một cải tiến an ninh nhỏ". Ví dụ, bạn có thể truy cập vào một bãi chứa bộ nhớ nếu bạn tình cờ có quyền truy cập đọc vào một thư mục tmp, một máy cấu hình bị hỏng và một sự cố trong ứng dụng của bạn. Trong trường hợp đó, bạn sẽ không thể cài đặt keylogger, nhưng bạn có thể phân tích kết xuất lõi. - Joachim Sauer
Xóa dữ liệu không được mã hóa khỏi bộ nhớ ngay sau khi bạn hoàn thành nó được coi là một cách thực hành tốt nhất không phải vì nó rất dễ (không phải); nhưng bởi vì nó làm giảm mức độ tiếp xúc với mối đe dọa của bạn. Các cuộc tấn công thời gian thực không được ngăn cản bằng cách thực hiện điều này; nhưng vì nó phục vụ một công cụ giảm thiểu thiệt hại bằng cách giảm đáng kể lượng dữ liệu được phơi bày trong một cuộc tấn công hồi tố trên ảnh chụp bộ nhớ (ví dụ: bản sao bộ nhớ ứng dụng của bạn được ghi vào tập tin hoán đổi hoặc đã đọc hết bộ nhớ từ một máy chủ đang chạy và được chuyển đến một máy chủ khác trước khi trạng thái của nó bị lỗi). - Dan Neely
Tôi có xu hướng đồng ý với thái độ của phản ứng này. Tôi muốn mạo hiểm để đề xuất hầu hết các vi phạm an ninh hậu quả xảy ra ở mức trừu tượng cao hơn nhiều so với các bit trong bộ nhớ. Chắc chắn, có thể có các tình huống trong các hệ thống phòng thủ siêu an toàn, điều này có thể đáng lo ngại nhưng nghiêm túc ở mức này là quá 99% các ứng dụng .NET hoặc Java đang được thừa hưởng (vì nó liên quan đến việc thu gom rác). - kingdango
Sau khi thâm nhập vào bộ nhớ máy chủ, tiết lộ mật khẩu, tôi sẽ thay thế chuỗi "chỉ là một cải tiến bảo mật nhỏ" với "hoàn toàn cần thiết để không sử dụng String cho mật khẩu, nhưng thay vào đó sử dụng char []." - Peter vdL
@PetervdL heartbleed chỉ cho phép đọc một bộ sưu tập các bộ đệm được sử dụng cho cả dữ liệu quan trọng bảo mật và I / O mạng mà không xóa ở giữa - vì lý do hiệu năng, bạn không thể kết hợp điều này với một chuỗi Java vì chúng được thiết kế không thể tái sử dụng . Bạn cũng không thể đọc vào bộ nhớ ngẫu nhiên bằng cách sử dụng Java để lấy nội dung của một chuỗi. Các vấn đề về ngôn ngữ và thiết kế dẫn đến heartbleed chỉ là không thể với Java Strings. - josefx


  1. Các chuỗi không thay đổi được trong Java nếu bạn lưu trữ mật khẩu dưới dạng văn bản thuần, nó sẽ có sẵn trong bộ nhớ cho đến khi bộ gom Garbage xóa nó và vì các chuỗi được sử dụng trong nhóm String để có khả năng sử dụng lại, có khả năng nó sẽ vẫn còn trong bộ nhớ trong một thời gian dài đặt ra một mối đe dọa an ninh. Vì bất kỳ ai có quyền truy cập vào kết xuất bộ nhớ đều có thể tìm thấy mật khẩu trong văn bản rõ ràng
  2. Đề xuất Java sử dụng getPassword() phương thức của JPasswordField trả về một char [] và không được dùng nữa getText() phương thức trả về mật khẩu trong văn bản rõ ràng nêu rõ lý do bảo mật.
  3. toString () luôn luôn có nguy cơ in văn bản thuần trong tệp nhật ký hoặc bảng điều khiển nhưng nếu sử dụng Mảng bạn sẽ không in nội dung của mảng thay vì vị trí bộ nhớ của nó được in.

    String strPwd = "passwd";
    char[] charPwd = new char[]{'p','a','s','s','w','d'};
    System.out.println("String password: " + strPwd );
    System.out.println("Character password: " + charPwd );
    

    Mật khẩu chuỗi: passwd

    Mật khẩu ký tự: [C @ 110b2345

Suy nghĩ cuối cùng: Mặc dù việc sử dụng char [] không chỉ đủ để bạn xóa nội dung an toàn hơn. Tôi cũng khuyên bạn nên làm việc với mật khẩu băm hoặc mã hóa thay vì văn bản thuần tuý và xóa nó khỏi bộ nhớ ngay sau khi xác thực hoàn tất.


117
2017-12-27 20:22



"Vì bất kỳ ai có quyền truy cập vào bộ nhớ dump có thể tìm thấy mật khẩu trong văn bản rõ ràng" Làm thế nào tôi có thể truy cập vào bộ nhớ dump và tìm thấy một mật khẩu ở đó? - Mohit Kanwar
@MohitKanwar Kích hoạt kết xuất bộ nhớ thông qua jmap hoặc jvisualvm. Nếu heapdumponoutofmemoryerror được thiết lập, bạn có thể kích hoạt vùng heap heap từ xa bằng cách gây ra tình trạng bộ nhớ. Một khi bạn có bãi chứa, bạn có thể đi qua nó với jvisualvm hoặc công cụ phân tích bộ nhớ eclipse hoặc các công cụ dòng lệnh và kiểm tra tất cả các đối tượng và giá trị của chúng. - Ryan


Tôi không nghĩ đây là một gợi ý hợp lệ, nhưng, ít nhất tôi cũng có thể đoán được lý do.

Tôi nghĩ rằng động cơ là muốn đảm bảo rằng bạn có thể xóa tất cả dấu vết của mật khẩu trong bộ nhớ ngay lập tức và chắc chắn sau khi nó được sử dụng. Với một char[] bạn có thể ghi đè mỗi phần tử của mảng bằng một ô trống hoặc một cái gì đó chắc chắn. Bạn không thể chỉnh sửa giá trị nội bộ của một String theo cách đó.

Nhưng điều đó một mình không phải là một câu trả lời hay; tại sao không chỉ đảm bảo tham chiếu đến char[] hoặc là String không trốn thoát? Sau đó, không có vấn đề bảo mật. Nhưng điều đó là String đối tượng có thể là intern()ed trong lý thuyết và giữ sống bên trong hồ bơi không đổi. Tôi cho rằng sử dụng char[] cấm khả năng này.


72
2018-01-16 14:27



Tôi sẽ không nói rằng vấn đề là tài liệu tham khảo của bạn sẽ hoặc sẽ không "thoát". Chỉ là các chuỗi sẽ không được sửa đổi trong bộ nhớ cho một số thời gian bổ sung, trong khi char[] có thể được sửa đổi, và sau đó nó không liên quan cho dù nó được thu thập hay không. Và vì việc thực hiện chuỗi ký tự cần phải được thực hiện một cách rõ ràng cho các chữ không phải chữ, nó giống như việc nói rằng char[] có thể được tham chiếu bởi một trường tĩnh. - Groo
không phải mật khẩu trong bộ nhớ dưới dạng chuỗi từ bài đăng biểu mẫu? - Dawesi