Làm cách nào để bảo vệ tên người dùng và mật khẩu MySQL khỏi bị dịch ngược?


87

Các .classtệp Java có thể được dịch ngược khá dễ dàng. Làm cách nào để bảo vệ cơ sở dữ liệu của mình nếu tôi phải sử dụng dữ liệu đăng nhập trong mã?


Tôi hy vọng bạn không phiền Tôi đã đánh dấu lại câu hỏi của bạn. Tôi đã xóa "tên người dùng" và "mật khẩu" và thêm "thiết kế ngược" và "dịch ngược". Tôi nghĩ rằng chúng mang tính mô tả nhiều hơn so với bản gốc. Nhân tiện, câu hỏi tuyệt vời về các nguyên tắc cơ bản!
William Brendel

6
Lưu ý rằng thực tế là bạn đang sử dụng Java không thực sự phù hợp ở đây. Việc có mật khẩu được mã hóa cứng bằng bất kỳ ngôn ngữ nào cũng có vấn đề theo cùng một cách ("string thebinary" cũng hiển thị hằng số chuỗi trong các chương trình C).
Joachim Sauer

@saua: Đúng, nhưng có lẽ ai đó sẽ đăng một số mã mẫu về cách tách tên người dùng và mật khẩu trong Java. Tôi thậm chí có thể tự mình làm điều đó nếu tôi có thời gian.
William Brendel

1
Tôi nhận thấy rằng nhiều câu trả lời cho rằng bạn đang cố ẩn tên người dùng / mật khẩu khỏi người dùng trái phép trong khi người dùng đang chạy ứng dụng vẫn ổn. Tôi tin rằng bạn muốn ẩn mật khẩu khỏi MỌI NGƯỜI . Hãy làm rõ điều này trong câu hỏi.
pek

1
Ví dụ: giả sử rằng thông tin xác thực được sử dụng để kết nối với một máy chủ mà không ai khác ngoài ứng dụng có thể đăng nhập.
pek

Câu trả lời:


123

Không bao giờ mã hóa mật khẩu cứng vào mã của bạn. Điều này gần đây đã được đưa ra trong Top 25 sai lầm lập trình nguy hiểm nhất :

Mã hóa cứng một tài khoản và mật khẩu bí mật vào phần mềm của bạn cực kỳ thuận tiện - đối với các kỹ sư đảo ngược có tay nghề cao. Nếu mật khẩu giống nhau trên tất cả phần mềm của bạn, thì mọi khách hàng sẽ dễ bị tấn công khi mật khẩu đó chắc chắn bị biết. Và bởi vì nó được mã hóa khó, đó là một vấn đề rất lớn để sửa chữa.

Bạn nên lưu trữ thông tin cấu hình, bao gồm cả mật khẩu, trong một tệp riêng biệt mà ứng dụng sẽ đọc khi khởi động. Đó là cách thực sự duy nhất để ngăn mật khẩu bị rò rỉ do dịch ngược (không bao giờ biên dịch nó thành nhị phân để bắt đầu).

Để biết thêm thông tin về lỗi phổ biến này, bạn có thể đọc bài viết CWE-259 . Bài viết có định nghĩa kỹ lưỡng hơn, ví dụ và nhiều thông tin khác về vấn đề.

Trong Java, một trong những cách dễ nhất để làm điều này là sử dụng lớp Preferences. Nó được thiết kế để lưu trữ tất cả các loại cài đặt chương trình, một số cài đặt có thể bao gồm tên người dùng và mật khẩu.

import java.util.prefs.Preferences;

public class DemoApplication {
  Preferences preferences = 
      Preferences.userNodeForPackage(DemoApplication.class);

  public void setCredentials(String username, String password) {
    preferences.put("db_username", username);
    preferences.put("db_password", password);
  }

  public String getUsername() {
    return preferences.get("db_username", null);
  }

  public String getPassword() {
    return preferences.get("db_password", null);
  }

  // your code here
}

Trong đoạn mã trên, bạn có thể gọi setCredentialsphương thức sau khi hiển thị hộp thoại yêu cầu tên người dùng và mật khẩu. Khi bạn cần kết nối với cơ sở dữ liệu, bạn chỉ có thể sử dụng các phương thức getUsernamegetPasswordđể truy xuất các giá trị được lưu trữ. Thông tin đăng nhập sẽ không được mã hóa cứng thành các tệp nhị phân của bạn, vì vậy việc dịch ngược sẽ không gây ra rủi ro bảo mật.

Lưu ý quan trọng: Tệp ưu tiên chỉ là tệp XML văn bản thuần túy. Đảm bảo bạn thực hiện các bước thích hợp để ngăn người dùng trái phép xem các tệp thô (quyền UNIX, quyền Windows, v.v.). Trong Linux, ít nhất, đây không phải là vấn đề, vì việc gọi Preferences.userNodeForPackagesẽ tạo tệp XML trong thư mục chính của người dùng hiện tại, mà người dùng khác không thể đọc được. Trong Windows, tình hình có thể khác.

Lưu ý quan trọng hơn: Đã có rất nhiều cuộc thảo luận trong các nhận xét của câu trả lời này và những người khác về kiến ​​trúc chính xác cho tình huống này. Câu hỏi ban đầu không thực sự đề cập đến bối cảnh mà ứng dụng đang được sử dụng, vì vậy tôi sẽ nói về hai tình huống mà tôi có thể nghĩ đến. Đầu tiên là trường hợp người sử dụng chương trình đã biết (và được phép biết) thông tin xác thực cơ sở dữ liệu. Thứ hai là trường hợp bạn, nhà phát triển, đang cố gắng giữ bí mật thông tin xác thực cơ sở dữ liệu với người sử dụng chương trình.

Trường hợp đầu tiên: Người dùng được phép biết thông tin đăng nhập cơ sở dữ liệu

Trong trường hợp này, giải pháp tôi đã đề cập ở trên sẽ hoạt động. Lớp Java Preferencesẽ lưu trữ tên người dùng và mật khẩu dưới dạng văn bản thuần túy, nhưng tệp tùy chọn sẽ chỉ có người dùng được ủy quyền mới có thể đọc được. Người dùng có thể chỉ cần mở tệp XML tùy chọn và đọc thông tin đăng nhập, nhưng đó không phải là một rủi ro bảo mật vì người dùng đã biết thông tin đăng nhập để bắt đầu.

Trường hợp thứ hai: Cố gắng ẩn thông tin đăng nhập khỏi người dùng

Đây là trường hợp phức tạp hơn: người dùng không nên biết thông tin đăng nhập nhưng vẫn cần truy cập vào cơ sở dữ liệu. Trong trường hợp này, người dùng đang chạy ứng dụng có quyền truy cập trực tiếp vào cơ sở dữ liệu, có nghĩa là chương trình cần biết thông tin đăng nhập trước thời hạn. Giải pháp tôi đề cập ở trên không thích hợp cho trường hợp này. Bạn có thể lưu trữ thông tin đăng nhập cơ sở dữ liệu trong một tệp tùy chọn, nhưng người dùng sẽ có thể đọc tệp đó, vì họ sẽ là chủ sở hữu. Trên thực tế, không có cách nào tốt để sử dụng trường hợp này một cách an toàn.

Trường hợp đúng: Sử dụng kiến ​​trúc nhiều tầng

Cách chính xác để làm điều đó là có một lớp giữa, ở giữa máy chủ cơ sở dữ liệu và ứng dụng khách của bạn, xác thực từng người dùng và cho phép thực hiện một số hoạt động hạn chế. Mỗi người dùng sẽ có thông tin đăng nhập của riêng họ, nhưng không phải cho máy chủ cơ sở dữ liệu. Thông tin xác thực sẽ cho phép truy cập vào lớp giữa (tầng logic nghiệp vụ) và sẽ khác nhau đối với mỗi người dùng.

Mỗi người dùng sẽ có tên người dùng và mật khẩu của riêng họ, có thể được lưu trữ cục bộ trong tệp tùy chọn mà không có bất kỳ rủi ro bảo mật nào. Đây được gọi là kiến trúc ba tầng (các tầng là máy chủ cơ sở dữ liệu, máy chủ logic nghiệp vụ và ứng dụng khách của bạn). Nó phức tạp hơn, nhưng nó thực sự là cách an toàn nhất để thực hiện loại việc này.

Thứ tự hoạt động cơ bản là:

  1. Khách hàng xác thực với cấp logic nghiệp vụ bằng tên người dùng / mật khẩu cá nhân của người dùng. Tên người dùng và mật khẩu được người dùng biết và không liên quan đến thông tin đăng nhập cơ sở dữ liệu theo bất kỳ cách nào.
  2. Nếu xác thực thành công, máy khách đưa ra yêu cầu đối với tầng logic nghiệp vụ yêu cầu một số thông tin từ cơ sở dữ liệu. Ví dụ, một kho sản phẩm. Lưu ý rằng yêu cầu của khách hàng không phải là một truy vấn SQL; nó là một cuộc gọi thủ tục từ xa chẳng hạn như getInventoryList.
  3. Tầng logic nghiệp vụ kết nối với cơ sở dữ liệu và truy xuất thông tin được yêu cầu. Tầng logic nghiệp vụ chịu trách nhiệm hình thành một truy vấn SQL an toàn dựa trên yêu cầu của người dùng. Bất kỳ tham số nào của truy vấn SQL phải được làm sạch để ngăn chặn các cuộc tấn công chèn vào SQL.
  4. Tầng logic nghiệp vụ gửi lại danh sách hàng tồn kho cho ứng dụng khách.
  5. Máy khách hiển thị danh sách hàng tồn kho cho người dùng.

Lưu ý rằng trong toàn bộ quy trình, ứng dụng khách không bao giờ kết nối trực tiếp với cơ sở dữ liệu . Tầng logic nghiệp vụ nhận yêu cầu từ người dùng đã xác thực, xử lý yêu cầu của khách hàng về danh sách khoảng không quảng cáo và chỉ sau đó thực thi truy vấn SQL.


4
Làm thế nào chính xác điều này ngăn ai đó lấy tên người dùng / mật khẩu? Sau đó bạn không thể đọc nó từ tệp sao?
Joe Phillips

Như tôi đã nói trong câu trả lời của mình, nếu quyền tệp của bạn được đặt đúng cách, chỉ người dùng đang chạy chương trình mới có quyền truy cập đọc vào tệp tùy chọn đó. Trong môi trường UNIX, điều này được thực hiện tự động. Windows có thể yêu cầu các bước bổ sung (tôi thực sự không chắc, vì tôi không sử dụng Windows nhiều).
William Brendel

Tôi nghĩ rằng ý tưởng là người dùng đang chạy ứng dụng không phải là người bạn đang cố gắng giữ nó lại. Nếu đúng như vậy, thì bạn phải mã hóa nó.
Michael Haren

Đúng, Michael đúng. Về cơ bản, ý tưởng là bạn đã biết tên người dùng / mật khẩu, vì vậy không cần phải giấu nó với chính bạn. Tuy nhiên, nó sẽ bị ẩn khỏi những người dùng khác thông qua quyền đối với tệp.
William Brendel

7
Nếu bạn đang triển khai (ví dụ) một ứng dụng chỉnh sửa DB cho người dùng và bạn không muốn họ biết tên người dùng và mật khẩu cơ sở dữ liệu, thì bạn đã cấu trúc sai giải pháp và phần mềm máy khách sẽ giao tiếp với máy chủ (thông qua ví dụ, một dịch vụ web) thực hiện nội dung DB.
JeeBee

15

Đặt mật khẩu vào một tệp mà ứng dụng sẽ đọc. KHÔNG BAO GIỜ nhúng mật khẩu vào tệp nguồn. Giai đoạn = Stage.

Ruby có một mô-đun ít được biết đến được gọi là DBI :: DBRC để sử dụng như vậy. Tôi không nghi ngờ rằng Java có một tương đương. Dù sao, nó không khó để viết một.


7
Mặc dù điều đó làm cho việc thay đổi mật khẩu sau này dễ dàng hơn một chút, nhưng nó không giải quyết được vấn đề bảo mật cơ bản.
Brian Knoblauch

Đúng vậy. Xem thêm câu trả lời từ William Brendel.
Keltia

1
Phương pháp mà Keltia và tôi đã chỉ ra là cách được chấp nhận để giải quyết vấn đề này. Tách thông tin đăng nhập khỏi mã đã biên dịch của bạn là một trong những phương pháp bảo mật phần mềm cơ bản nhất. Đặt thông tin đăng nhập vào một tệp riêng biệt là một cách hiệu quả để đạt được điều này.
William Brendel

Hơn nữa, thực tế là thông tin cấu hình của bạn nằm trong tệp bản rõ nên bị hủy bỏ bởi các hạn chế của hệ điều hành. Ví dụ, trong UNIX, tệp bản rõ phải thuộc sở hữu của người dùng đang chạy chương trình và có quyền 0600, vì vậy chỉ chủ sở hữu mới có thể đọc được.
William Brendel

4
OK, vì vậy tệp chỉ có thể được đọc bởi người dùng đang chạy chương trình. Tuyệt quá. Điều đó không giải quyết được gì. :-) Tôi, người dùng chúng ta đang cố gắng giữ bí mật mật khẩu từ, có thể đọc nó dễ dàng như các ứng dụng ...
Brian Knoblauch

3

Bạn đang viết một ứng dụng web? Nếu vậy, hãy sử dụng JNDI để cấu hình nó bên ngoài ứng dụng. Tổng quan có sẵn tại đây :

JNDI cung cấp một cách thống nhất cho ứng dụng để tìm và truy cập các dịch vụ từ xa qua mạng. Dịch vụ từ xa có thể là bất kỳ dịch vụ doanh nghiệp nào, bao gồm dịch vụ nhắn tin hoặc dịch vụ dành riêng cho ứng dụng, nhưng tất nhiên, ứng dụng JDBC chủ yếu quan tâm đến dịch vụ cơ sở dữ liệu. Sau khi một đối tượng DataSource được tạo và đăng ký với dịch vụ đặt tên JNDI, một ứng dụng có thể sử dụng API JNDI để truy cập vào đối tượng DataSource đó, đối tượng này sau đó có thể được sử dụng để kết nối với nguồn dữ liệu mà nó đại diện.


liên kết được cung cấp là xấu
James Oravec

2

Bất kể bạn làm gì, thông tin nhạy cảm sẽ được lưu trữ trong một số tệp ở đâu đó. Mục tiêu của bạn là làm cho nó càng khó càng tốt. Bạn có thể đạt được bao nhiêu điều này phụ thuộc vào dự án, nhu cầu và độ dày của ví tiền của công ty bạn.

Cách tốt nhất là không lưu trữ bất kỳ mật khẩu nào ở bất kỳ đâu. Điều này đạt được bằng cách sử dụng các hàm băm để tạo và lưu trữ các băm mật khẩu:

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

Các thuật toán băm là một hàm một chiều. Chúng biến bất kỳ lượng dữ liệu nào thành một "dấu vân tay" có độ dài cố định không thể đảo ngược. Chúng cũng có đặc tính là nếu đầu vào thay đổi dù chỉ một bit nhỏ, thì hàm băm kết quả sẽ hoàn toàn khác (xem ví dụ ở trên). Điều này rất tốt để bảo vệ mật khẩu, vì chúng tôi muốn lưu trữ mật khẩu ở dạng bảo vệ chúng ngay cả khi bản thân tệp mật khẩu bị xâm phạm, nhưng đồng thời, chúng tôi cần xác minh rằng mật khẩu của người dùng là chính xác.

Lưu ý không liên quan: Ngày xưa của Internet, khi bạn nhấp vào liên kết 'quên mật khẩu của tôi', các trang web sẽ gửi cho bạn mật khẩu văn bản thuần túy của bạn qua email. Họ có thể đang lưu trữ chúng trong cơ sở dữ liệu ở đâu đó. Khi tin tặc có quyền truy cập vào cơ sở dữ liệu của họ, họ sẽ có quyền truy cập vào tất cả các mật khẩu. Vì nhiều người dùng sẽ sử dụng cùng một mật khẩu trong nhiều trang web, đây là một vấn đề bảo mật lớn. May mắn thay, ngày nay đây không phải là một thực tế phổ biến.

Bây giờ đến câu hỏi: cách tốt nhất để lưu trữ mật khẩu là gì? Tôi sẽ coi giải pháp này (dịch vụ xác thực và quản lý người dùng của stormpath) là một giải pháp khá lý tưởng:

  1. Người dùng của bạn nhập thông tin đăng nhập và thông tin này được xác thực dựa trên băm mật khẩu
  2. Mật khẩu băm được tạo và lưu trữ, không phải mật khẩu
  3. Hàm băm được thực hiện nhiều lần
  4. Hàm băm được tạo bằng cách sử dụng muối được tạo ngẫu nhiên
  5. Hàm băm được mã hóa bằng khóa riêng tư
  6. Khóa cá nhân được lưu trữ ở một nơi thực tế khác với mã băm
  7. Khóa cá nhân được cập nhật theo thời gian
  8. Hàm băm được mã hóa được chia thành nhiều phần
  9. Các khối này được lưu trữ ở các vị trí vật lý riêng biệt

Rõ ràng bạn không phải là google hay ngân hàng, vì vậy đây là một giải pháp quá mức cần thiết cho bạn. Nhưng sau đó là câu hỏi: Dự án của bạn yêu cầu bao nhiêu bảo mật, bạn có bao nhiêu thời gian và tiền bạc?

Đối với nhiều ứng dụng, mặc dù không được khuyến khích, lưu trữ mật khẩu được mã hóa cứng trong mã có thể là một giải pháp đủ tốt. Tuy nhiên, bằng cách dễ dàng thêm một số bước bảo mật bổ sung từ danh sách trên, bạn có thể làm cho ứng dụng của mình an toàn hơn nhiều.

Ví dụ: giả sử bước 1 không phải là một giải pháp chấp nhận được cho dự án của bạn. Bạn không muốn người dùng nhập mật khẩu mọi lúc, hoặc bạn thậm chí không muốn / cần người dùng biết mật khẩu. Bạn vẫn có thông tin nhạy cảm ở đâu đó và bạn muốn bảo vệ thông tin này. Bạn có một ứng dụng đơn giản, không có máy chủ để lưu trữ các tệp của bạn hoặc điều này quá phức tạp cho dự án của bạn. Ứng dụng của bạn chạy trên các môi trường không thể lưu trữ các tệp một cách an toàn. Đây là một trong những trường hợp xấu nhất, nhưng vẫn với một số biện pháp bảo mật bổ sung, bạn có thể có giải pháp an toàn hơn nhiều. Ví dụ: bạn có thể lưu trữ thông tin nhạy cảm trong một tệp và bạn có thể mã hóa tệp. Bạn có thể có khóa cá nhân mã hóa cứng được mã hóa trong mã. Bạn có thể làm xáo trộn mã, vì vậy bạn sẽ khiến ai đó khó bẻ khóa nó hơn một chút.liên kết này . (Tôi muốn cảnh báo bạn một lần nữa rằng điều này không an toàn 100%. Một hacker thông minh có kiến ​​thức và công cụ phù hợp có thể hack được. Nhưng dựa trên yêu cầu và nhu cầu của bạn, đây có thể là một giải pháp đủ tốt cho bạn).



0

MD5 là một thuật toán băm, không phải là một thuật toán mã hóa, trong ngắn hạn bạn không thể lấy lại được bởi vì bạn đã băm, bạn chỉ có thể so sánh. Lý tưởng nhất là nó nên được sử dụng khi lưu trữ thông tin xác thực người dùng chứ không phải db tên người dùng và mật khẩu. Tên người dùng db và pwd phải được mã hóa và lưu giữ trong một tệp cấu hình, để thực hiện ít nhất.


Tôi đã nghe nói về những người tạo ra tất cả các kết hợp chuỗi có thể có và lưu trữ các hàm băm MD5 tương ứng của họ. Vì vậy, khi họ tìm thấy mã băm MD5 của ai đó, họ chỉ cần tìm mã băm mà họ đã lưu trữ và họ nhận được chuỗi tương ứng.
Nav
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.