Phát triển Java 2.0: Bảo đảm an toàn dữ liệu của ứng dụng Java trên điện toán đám mây

Sử dụng mã hóa khóa bí mật để bảo vệ dữ liệu đám mây

An toàn dữ liệu là một mối quan tâm thật sự đối với các tổ chức đang xem xét chọn dùng đám mây, nhưng trong nhiều trường hợp nó chưa phải là vấn đề. Trong bài đăng này về Phát triển Java 2.0, hãy học cách sử dụng mã hóa khóa bí mật và Tiêu chuẩn mã hóa nâng cao (Advanced Encryption Standard) để bảo vệ dữ liệu nhạy cảm của ứng dụng cho đám mây. Bạn cũng sẽ nhận được một hướng dẫn ngắn về chiến lược mã hóa, rất quan trọng để tối đa hóa hiệu quả của các tìm kiếm có điều kiện trên các kho dữ liệu đám mây phân tán.

Andrew Glover, Trưởng ban công nghệ, App47

Andrew GloverAndrew Glover là nhà phát triển, tác giả, diễn giả và nhà doanh nghiệp có niềm đam mê về phát triển theo hành vi, Tích hợp liên tục và phát triển phần mềm Agile. Ông là người sáng lập khung công tác BDD (Behavior-Driven Development - Phát triển theo hành vi) easyb và là đồng tác giả của ba cuốn sách: Continuous Integration (Tích hợp liên tục), Groovy in Action (Thực hành Groovy) và Java Testing Patterns (Các mẫu thử nghiệm Java). Bạn có thể xem blog và theo dõi ông trên Twitter.



05 03 2013

Đôi điều về loạt bài này

Bối cảnh phát triển Java đã thay đổi hoàn toàn kể từ khi công nghệ Java đã xuất hiện lần đầu tiên. Nhờ các khung công tác nguồn mở hoàn thiện và cơ sở hạ tầng triển khai cho thuê tin cậy mà ngày nay chúng ta có thể lắp ráp, thử nghiệm, chạy và duy trì các ứng dụng Java một cách nhanh chóng và không tốn kém. Trong loạt bài này, Andrew Glover khám phá một loạt các công nghệ và các công cụ làm cho hình mẫu phát triển Java mới này trở nên khả thi.

Chỉ trong một vài năm, các nền tảng và các dịch vụ điện toán đám mây đã thay đổi đáng kể bối cảnh về phát triển ứng dụng Java™. Chúng đã hạ thấp các rào cản liên quan đến bảo trì và cấu hình hệ thống và đồng thời đã làm giảm chi phítăng tốc độ đưa phần mềm ra thị trường. Về mặt khái niệm, điện toán đám mây có ý nghĩa là: các nhà quản lý kinh doanh thích lợi nhuận do đầu tư mang lại còn các nhà phát triển thích thoát khỏi mã cơ sở hạ tầng. Tuy nhiên, nhiều cơ sở vẫn đang phải vật lộn với việc nên hay không chuyển sang nền tảng đám mây.

An toàn dữ liệu là một trong những mối quan tâm chính đối với một tổ chức đang xem xét việc di chuyển hệ thống phần mềm của mình lên một cơ sở hạ tầng đám mây. Dữ liệu càng nhạy cảm thì càng có nhiều lý do để quan tâm. Là các nhà phát triển phần mềm, điều quan trọng là chúng ta hiểu các rủi ro an toàn thực sự của điện toán đám mây lẫn các cách tiếp cận thực tế để giải quyết ít nhất một số trong các mối quan tâm này.

Trong bài đăng này về Phát triển Java 2.0, tôi sẽ giải thích điều gì làm cho việc lưu trữ dữ liệu trong đám mây khác với việc lưu trữ nó trên một máy tính trung tâm. Sau đó tôi sẽ giới thiệu cho bạn cách sử dụng các tiêu chuẩn và các tiện ích mã hóa khóa bí mật dựng sẵn của nền tảng Java để giữ an toàn hợp lý cho dữ liệu của bạn, ngay cả khi nó được lưu trữ trên một kho dữ liệu đám mây phân tán. Cuối cùng, tôi sẽ trình bày một cách tiếp cận chiến lược về mã hóa, bằng cách sử dụng các điều kiện truy vấn làm một vạch chuẩn để bạn biết có nên hay không nên mã hóa dữ liệu của mình.

Bảo đảm an toàn dữ liệu đám mây

Điện toán đám mây chính xác không giới thiệu các vấn đề bảo mật dữ liệu mới; trong hầu hết các trường hợp, nó chỉ khuếch trương các vấn đề này thôi. Việc đưa dữ liệu lên đám mây có khả năng trưng nó ra cho một đối tượng người dùng lớn hơn, đó thường là một điều tốt. Nhưng nếu dữ liệu được trưng ra có ý nghĩa riêng tư hoặc chỉ được truy cập khi có quyền, thì việc đưa dữ liệu lên đám mây có thể là thảm họa. Vấn đề cơ bản với điện toán đám mây là nó tách dữ liệu được giao phó khỏi một nhà phát triển hoặc khỏi việc kiểm soát trực tiếp của quản trị viên hệ thống (sys-admin). Thay vì được lưu trữ và quản lý cục bộ, dữ liệu trong các đám mây được lưu trữ trên các thiết bị phân tán, có thể được đặt ở bất kỳ đâu và bất cứ ai đều có thể truy cập được.

Tính riêng tư của dữ liệu trong Liên minh châu Âu (EU)

Cách tiếp cận của EU về tính riêng tư dữ liệu trên nền tảng điện toán đám mây chặt chẽ hơn nhiều so với của Hoa Kỳ: dữ liệu cá nhân thuộc về một công dân của EU (chẳng hạn như hồ sơ y tế của một công dân Pháp) phải lưu trú trên các máy chủ nằm bên trong EU.

Thậm chí nếu trong thực tế công ty của bạn có thể sử dụng kho dữ liệu ở xa, không tập trung, thì bạn cũng muốn các ứng dụng của mình trong đám mây cần bắt đầu có một chút về an toàn dữ liệu. Khi bạn bắt đầu suy nghĩ về an toàn dữ liệu, có hai câu hỏi quan trọng nảy sinh:

  • Dữ liệu có an toàn trong quá trình hoạt động không?
  • Dữ liệu có an toàn trong lúc không hoạt động không?

Dữ liệu trong quá trình hoạt động liên quan đến cách dữ liệu truyền đi từ một vị trí này tới vị trí khác, đó là công nghệ và cơ sở hạ tầng truyền thông mà bạn đang sử dụng. Dữ liệu không hoạt động liên quan đến việc bạn lưu trữ dữ liệu như thế nào — và tốt ra sao. Ví dụ, nếu bạn lưu trữ các tên và mật khẩu người dùng trong một cơ sở dữ liệu không mã hóa chúng, thì dữ liệu không hoạt động của bạn sẽ không an toàn.

Để bảo vệ dữ liệu trong quá trình chuyển tiếp qua web, hầu hết đều sử dụng HTTPS. Đây là định dạng HTTP có mã hóa dữ liệu chuyển đi từ các trình duyệt đến các khách hàng. Một ưu điểm khác của HTTPS là nó có mặt ở khắp nơi: Hầu hết các nhà phát triển đã cấu hình Apache, Tomcat, Jetty để sử dụng HTTPS.

Mã hóa cũng là cơ chế chung để bảo vệ dữ liệu ở phần còn lại và điện toán đám mây không thay đổi điều đó. Trong khi mã hóa có thể là bí mật, bạn chỉ cần biết một số điều cơ bản về mã hóa để giữ an toàn hợp lý cho dữ liệu của ứng dụng của mình. Và một khi dữ liệu của bạn đã an toàn, thì dù bạn dùng dữ liệu cục bộ hay qua một nền tảng điện toán đám mây hoặc kho dữ liệu đều không thành vấn đề.


Mã hóa khóa bí mật

Mã hóa là quá trình chuyển đổi văn bản thuần, dễ đọc đơn giản với mọi người thành văn bản khó đọc. Bạn mã hóa bằng một thuật toán mật mã, còn được gọi là cipher. Văn bản đã mã hóa được giải mã trở lại thành văn bản dễ đọc qua một khóa, về cơ bản đó là một dạng mật khẩu. Mã hóa giữ an toàn thông tin bằng cách làm cho thông tin trở nên khó đọc với bất cứ ai không có khóa.

Người ta thường sử dụng hai kiểu mã hóa dựa trên khóa trong điện toán: mã hóa khóa công khai (dùng chung) và mã hóa khóa bí mật (dùng riêng). Mã hóa khóa công khai là kỹ thuật phổ biến nhất để giữ an toàn dữ liệu trong quá trình chuyển tiếp; trên thực tế, đó là kiến trúc bên dưới của tầng bảo mật giao dịch HTTPS. Dạng mã hóa này đòi hỏi phải có hai khóa trong một bộ khóa công khai-bí mật. Khóa công khai mã hóa dữ liệu còn khóa bí mật được sử dụng để giải mã dữ liệu đó. Trong mã hóa khóa công khai, người ta có thể phân phối khóa công khai một cách an toàn, trong khi khóa bí mật phải chịu sự kiểm soát của một quản trị viên. Mã hóa khóa công khai làm cho việc chia sẻ thông tin mã hóa trở nên dễ dàng.

Các khóa bí mật và tính riêng tư

Bất kể bạn sử dụng thuật toán mã hóa nào, bạn phải bảo đảm rằng khóa bí mật của bạn phải an toàn. Cụm mật khẩu của bạn phải đáp ứng các tiêu chuẩn an toàn cao và không bao giờ được lưu trữ dưới dạng văn bản thuần — đặc biệt là trong đám mây. May mắn thay, cơ sở hạ tầng bảo mật của nền tảng Java tạo ra các khóa khá phức tạp và cho phép bạn giữ chúng an toàn trong kho khóa của nền tảng Java.

Trong mã hóa khóa bí mật, người ta thường sử dụng một khóa bí mật duy nhất để mã hóa và giải mã dữ liệu. Kiểu mã hóa này làm cho việc chia sẻ dữ liệu mã hóa với một bên thứ ba trở nên khó khăn vì cả người gửi lẫn người nhận đều phải sử dụng chung một khóa. Nếu khóa đó bị lộ, thì tất cả các thông tin được mã hóa cũng bị lộ. Mã hóa khóa bí mật có hiệu quả cao nếu không cần phải chia sẻ dữ liệu mã hóa với các bên khác, sao cho có thể lưu giữ khóa dưới sự kiểm soát chặt chẽ mọi lúc.

Mã hóa khóa bí mật là một phương tiện hiệu quả để bảo đảm an toàn dữ liệu ứng dụng, khi lưu trữ và truyền dữ liệu đó qua một cơ sở hạ tầng điện toán đám mây. Do khóa mã hóa vẫn còn nằm dưới sự kiểm soát của một quản trị viên hoặc người tạo ra ứng dụng, nên các nhà cung cấp đám mây và những kẻ nghe lén tiềm ẩn khác đều không có quyền truy cập tự do vào dữ liệu đó.


Mã hóa một ứng dụng Java

Bạn có thể chọn từ một loạt các tùy chọn để bảo đảm an toàn cho các ứng dụng Java, bao gồm các thư viện nền tảng Java tiêu chuẩn. Bạn cũng có một loạt các tiêu chuẩn mã hóa và các gói để lựa chọn. Với các ví dụ sau đây, tôi sẽ sử dụng các thư viện Java lõi và Advanced Encryption Standard (Tiêu chuẩn mã hóa nâng cao) hoặc viết tắt là AES. Tôi sẽ sử dụng một khóa bí mật vừa để mã hóa văn bản thuần vừa để giải mã văn bản mã hóa, là văn bản thuần đã được mã hóa. Tôi thích AES bởi vì nó đã được Cơ quan an ninh quốc gia phê duyệt và được chính phủ Hoa Kỳ tiêu chuẩn hóa.

Để linh hoạt tối đa và dễ dàng kiểm tra, tôi sẽ tạo ra một số giao diện mã hóa và các lớp thực hiện liên quan để chỉ cần bao bọc các lớp Java lõi. Sau đó tôi sẽ hướng dẫn bạn cách sử dụng các lớp này để duy trì một cách an toàn và thậm chí để truy vấn dữ liệu trong các kho dữ liệu đám mây như SimpleDB của Amazon hoặc ngay cả MongoDB của MongoHQ nữa.

Trong Liệt kê 1, tôi định nghĩa một giao diện mã hóa tổng quát đơn giản, định nghĩa hai phương thức để mã hóa và giải mã dữ liệu. Giao diện này sẽ dùng như một bình phong cho các thuật toán khác nhau; có nghĩa là, các lớp thực hiện của tôi sẽ sử dụng một thuật toán mã hóa cụ thể là AES.

Liệt kê 1. Một giao diện mã hóa
package com.b50.crypto;

public interface Cryptographical {
 String encrypt(String plaintext);
 String decrypt(String ciphertext);
}

Với giao diện Cryptographical của mình, tôi có thể hoặc mã hóa văn bản hoặc giải mã văn bản mã hóa. Tiếp theo trong Liệt kê 2, tôi sẽ sử dụng API bảo mật Java (Java Security API) để tạo ra một giao diện khác đại diện cho một khóa:

Liệt kê 2. Một giao diện khóa
package com.b50.crypto;

import java.security.Key;

public interface CryptoKeyable {
  Key getKey();
}

Như bạn có thể thấy, giao diện CryptoKeyable của tôi chỉ dùng như một trình bao bọc (wrapper) cho kiểu Key (Khóa) cốt lõi của nền tảng Java.

Nếu bạn đang sử dụng mã hóa AES, khi bạn mã hóa văn bản thuần bạn sẽ cần mã hóa các ký tự nhị phân được tạo ra theo base-64 — hoặc ít nhất là như vậy nếu bạn muốn sử dụng chúng trong các yêu cầu web (ví dụ, với các miền SimpleDB). Vì vậy, tôi sẽ mã hóa tất cả các chuỗi ký tự cần mã hóa và giải mã bất kỳ chuỗi ký tự nào cần giải mã.

Lớp thực hiện Cryptographical của tôi với AES, được hiển thị trong Liệt kê 3, không chỉ xử lý mã hóa AES mà còn mã hóa và giải mã theo base-64:

Liệt kê 3. Thực thi AES trong interface Cryptographical của tôi
package com.b50.crypto;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

public class AESCryptoImpl implements Cryptographical {

 private Key key;
 private Cipher ecipher;
 private Cipher dcipher;

 private AESCryptoImpl(Key key) throws NoSuchAlgorithmException,
   NoSuchPaddingException, InvalidKeyException {
  this.key = key;
  this.ecipher = Cipher.getInstance("AES");
  this.dcipher = Cipher.getInstance("AES");
  this.ecipher.init(Cipher.ENCRYPT_MODE, key);
  this.dcipher.init(Cipher.DECRYPT_MODE, key);
 }

 public static Cryptographical initialize(CryptoKeyable key) throws CryptoException {
  try {
   return new AESCryptoImpl(key.getKey());
  } catch (NoSuchAlgorithmException e) {
   throw new CryptoException(e);
  } catch (NoSuchPaddingException e) {
   throw new CryptoException(e);
  } catch (InvalidKeyException e) {
   throw new CryptoException(e);
  }
 }

 public String encrypt(String plaintext) {
  try {
   return new BASE64Encoder().encode(ecipher.doFinal(plaintext.getBytes("UTF8")));
  } catch (Exception e) {
   throw new RuntimeException(e);
  }
 }

 public String decrypt(String ciphertext) {
  try {
   return new String(dcipher.doFinal(new BASE64Decoder().decodeBuffer(ciphertext)), 
     "UTF8");
  } catch (Exception e) {
   throw new RuntimeException(e);
  }
 }
}

Kho khóa của Java

Tiếp theo, chúng ta hãy suy nghĩ về khóa mã hóa. Có thể sử dụng các thư viện lõi của nền tảng Java để tạo ra các khóa mã hóa mạnh; tuy nhiên, những phương thức này sẽ luôn sản xuất một khóa mới được tạo ra ngẫu nhiên. Vì vậy, nếu bạn tạo một khóa sử dụng các lớp KeyGenerator của Java, bạn sẽ cần phải lưu trữ khóa đó để sử dụng sau này (có nghĩa là, cho đến khi bạn quyết định giải mã văn bản đã mã hóa bằng khóa đó). Về vấn đề này, bạn có thể sử dụng tiện ích KeyStore của nền tảng Java và các lớp tương ứng.

Tiện ích KeyStore chứa một tập hợp các lớp cho phép bạn lưu một khóa vào một tệp nhị phân được bảo vệ bằng mật khẩu, được gọi là một keystore (kho khóa). Tôi có thể kiểm tra các khóa trong Java bằng một vài trường hợp kiểm tra. Trước tiên, tôi tạo ra hai cá thể của một Key và cho thấy rằng mỗi String được mã hóa tương ứng của một khóa là khác nhau, như trong Liệt kê 4:

Liệt kê 4. Mã hóa đơn giản bằng cách sử dụng hai khóa khác nhau
@Test
public void testEncryptRandomKey() throws Exception {
 SecretKey key = KeyGenerator.getInstance("AES").generateKey();
 Cryptographical crypto = AESCryptoImpl.initialize(new AESCryptoKey(key));
 String enc = crypto.encrypt("Andy");
 Assert.assertEquals("Andy", crypto.decrypt(enc));

 SecretKey anotherKey = KeyGenerator.getInstance("AES").generateKey();
 Cryptographical anotherInst = AESCryptoImpl.initialize(new AESCryptoKey(anotherKey));
 String anotherEncrypt = anotherInst.encrypt("Andy");
 Assert.assertEquals("Andy", anotherInst.decrypt(anotherEncrypt));

 Assert.assertFalse(anotherEncrypt.equals(enc));
}

Tiếp theo, trong Liệt kê 5, tôi chứng minh rằng một cá thể khóa cụ thể luôn mang đến cùng một văn bản mã hóa chính xác cho một String tương ứng:

Liệt kê 5. Một khóa bí mật tương ứng với một chuỗi duy nhất
@Test
public void testEncrypt() throws Exception {
 SecretKey key = KeyGenerator.getInstance("AES").generateKey();

 KeyStore ks = KeyStore.getInstance("JCEKS");
 ks.load(null, null);
 KeyStore.SecretKeyEntry skEntry = new KeyStore.SecretKeyEntry(key);
 ks.setEntry("mykey", skEntry, 
   new KeyStore.PasswordProtection("mykeypassword".toCharArray()));
 FileOutputStream fos = new FileOutputStream("agb50.keystore");
 ks.store(fos, "somepassword".toCharArray());
 fos.close();

 Cryptographical crypto = AESCryptoImpl.initialize(new AESCryptoKey(key));
 String enc = crypto.encrypt("Andy");
 Assert.assertEquals("Andy", crypto.decrypt(enc));

 //alternatively, read the keystore file itself to obtain the key

 Cryptographical anotherInst = AESCryptoImpl.initialize(new AESCryptoKey(key));
 String anotherEncrypt = anotherInst.encrypt("Andy");
 Assert.assertEquals("Andy", anotherInst.decrypt(anotherEncrypt));

 Assert.assertTrue(anotherEncrypt.equals(enc));
}

Những gì tôi mã hóa bằng một khóa cụ thể, thì tôi cần giải mã bằng đúng khóa đó. Việc sử dụng tiện ích KeyStore của Java là một cách thuận tiện và an toàn để lưu trữ các khóa của tôi.

Các lưu ý về keystore

Các mã trong các Liệt kê 45 là mẫu mã soạn sẵn, nhưng có thể dùng nó để chứng minh một vài điều:

  • Keystore có một tên.
  • Tệp đã lưu được bảo vệ bằng mật khẩu.
  • Keystore có thể lưu trữ nhiều hơn một khóa.
  • Mỗi khóa bên trong kho có một mật khẩu kèm theo.

Với trường hợp kiểm tra này, tôi đã quyết định tạo ra một keystore mới mỗi khi tôi chạy bài kiểm tra đó. Tôi đã có thể dễ dàng mở một keystore hiện có với mỗi bài kiểm tra mới. Nếu tôi muốn sử dụng một keystore hiện có, tôi cần phải biết mật khẩu của nó, cũng như mật khẩu để truy cập vào một khóa cụ thể.

Khóa là tất cả khi nói đến mã hóa. Mật mã bên dưới mạnh ra sao không quan trọng; nếu khóa của tôi bị lộ, thì dữ liệu của tôi cũng bị lộ. Điều này cũng có nghĩa là phải bảo đảm keystore và các cụm từ mật khẩu kèm theo keystore luôn an toàn. (Ví dụ, trong một ứng dụng sản xuất, tôi sẽ không mã cố định (hardcode) các mật khẩu như tôi đã làm để minh họa trong các Liệt kê 45.)


Mã hóa đám mây

Khi bạn mã hóa dữ liệu, bạn thay đổi các thuộc tính của nó. Về cơ bản, điều này có nghĩa là một số nguyên được mã hóa thì sẽ không so sánh được với số nguyên nữa. Do đó, điều quan trọng cần suy nghĩ thấu đáo xem rốt cuộc bạn sẽ truy vấn dữ liệu đã lưu trữ trong đám mây như thế nào, tại sao và trong hoàn cảnh nào. Tin tức tốt là trong nhiều trường hợp, dữ liệu mà bạn muốn giữ riêng tư sẽ thường có giá trị nghiệp vụ khác so với dữ liệu mà bạn muốn thao tác: mã hóa một tên của một tài khoản hoặc một số thông tin cá nhân về chủ tài khoản có ý nghĩa, nhưng mã hóa một số dư tài khoản có thể không có ý nghĩa gì (vì có ai sẽ quan tâm đến một số dư tài khoản mà không gắn với một người không?).

Truy vấn với mã hóa

Người ta có thể dễ dàng tìm kiếm dữ liệu mã hóa khi thực hiện các so khớp chính xác như: "Tìm cho tôi tất cả các tài khoản có tên là 'foo' (ở đây 'foo' được mã hóa)." Nhưng việc tìm kiếm đó vốn dĩ không làm việc với các truy vấn có điều kiện như: "Tìm cho tôi tất cả các tài khoản có số dư quá hạn lớn hơn $ 450 trong đó $ 450 được mã hóa".

Ví dụ, hãy tưởng tượng rằng tôi sử dụng một thuật toán mã hóa đơn giản để đảo ngược thứ tự ký tự và thêm ký tự i vào cuối cùng của một chuỗi ký tự. Trong trường hợp này, chuỗi foo sẽ trở thành oofi450 sẽ trở thành 054i. Nếu đã mã hóa giá trị tên trong bảng bằng cách sử dụng thuật toán mã hóa đơn giản này, tôi có thể dễ dàng truy vấn bằng các so khớp chính xác, chẳng hạn như "select * from table where name = 'oofi'" (chọn * từ bảng có name = 'oofi' "). Tuy nhiên, so sánh giá trị mã hóa là 450 sẽ rất khó khăn: "select * from table where amount > 054i" hoàn toàn không giống như "select * from table where amount > 450".

Để so sánh dữ liệu trong trường hợp này, tôi có thể thực hiện một số hoạt động giải mã trong ứng dụng — có nghĩa là, tôi cần phải chọn tất cả dữ liệu của một bảng, giải mã trường amount rồi thực hiện so sánh. Việc không thể dựa vào kho dữ liệu bên dưới cho hoạt động này có nghĩa là tôi không thể lọc nhanh như khi lọc với kho dữ liệu. Do tôi muốn tối đa hóa hiệu quả, nên tôi cần suy nghĩ thấu đáo xem tôi muốn mã hóa dữ liệu nào và tôi muốn mã hóa nó ra sao. Việc luôn suy nghĩ mã hóa với các truy vấn sắp tới là một cách tốt để nâng cao hiệu quả chung của chương trình.

Thật dễ mã hóa một tên tài khoản bằng MongoDB và tìm kiếm theo tên đã mã hóa của nó, như thể hiện trong Liệt kê 6:

Liệt kê 6. Mã hóa với MongoDB
@Test
public void encryptMongoDBRecords() throws Exception {
 KeyStore.SecretKeyEntry pkEntry = getKeyStoreEntry();
 Cryptographical crypto = 
   AESCryptoImpl.initialize(new AESCryptoKey(pkEntry.getSecretKey()));

 DB db = getMongoConnection();
 DBCollection coll = db.getCollection("accounts");

 BasicDBObject encryptedDoc = new BasicDBObject();
 encryptedDoc.put("name", crypto.encrypt("Acme Life, LLC"));
 coll.insert(encryptedDoc);


 BasicDBObject encryptedQuery = new BasicDBObject();
 encryptedQuery.put("name", crypto.encrypt("Acme Life, LLC"));

 DBObject result = coll.findOne(encryptedQuery);
 String value = result.get("name").toString();
 Assert.assertEquals("Acme Life, LLC", crypto.decrypt(value));
}

Trong Listing 6, đầu tiên tôi sử dụng phương thức getKeyStoreEntry để đọc một kho khóa hiện có. Tiếp theo tôi thu được một kết nối đến một cá thể của MongoDB, trong trường hợp này cá thể đó ngẫu nhiên lưu trú trong đám mây của MongoHQ. Sau đó tôi cố nắm giữ một liên kết đến bộ sưu tập các tài khoản (một lập trình viên RDBMS thường gọi nó là bảng các tài khoản) và tiến hành chèn một bản ghi tài khoản mới có tên tương ứng đã mã hóa của nó. Cuối cùng, tôi tìm kiếm bản ghi tương tự đó bằng cách mã hóa chuỗi ký tự tìm kiếm của mình (ở đây name tương ứng với "Acme Life, LLC" đã mã hóa).

Bản ghi này theo MongoDB sẽ trông đại loại như những gì hiển thị trong Liệt kê 7. (Lưu ý rằng chuỗi "Acme Life, LLC" đã mã hóa của bạn sẽ khác với chuỗi đã mã hóa của tôi vì bạn sẽ có một khóa khác).

Liệt kê 7. Một trường hợp thử nghiệm mã hóa của MongoDB
{
 _id : "4ee0c541300484530bf9c6fa",
 name : "f0wJxYyVhfH0UkkTLKGZng=="
}

Tôi đã quên chưa mã hóa khóa thực tế (name) trong tài liệu, nhưng cũng có thể tôi đã mã hóa nó rồi. Nếu tôi đã mã hóa, tôi sẽ chỉ cần phản ánh sự thay đổi trong các truy vấn tương ứng của mình. Tôi cũng có thể đã mã hóa tên bộ sưu tập. Các so sánh String trực tiếp sẽ hoạt động bất kể chúng được mã hóa hay không.

Chiến lược này không chỉ dùng riêng cho việc thực hiện của MongoDB. Ví dụ, tôi có thể thực hiện gần đúng một trường hợp kiểm tra tương tự với SimpleDB, như trong Liệt kê 8:

Liệt kê 8. Một trường hợp kiểm tra mã hóa của SimpleDB
@Test
public void testSimpleDBEncryptInsert() throws Exception {

 KeyStore.SecretKeyEntry pkEntry = getKeyStoreEntry();
 Cryptographical crypto = 
   AESCryptoImpl.initialize(new AESCryptoKey(pkEntry.getSecretKey()));

 AmazonSimpleDB sdb = getSimpleDB();
 String domain = "accounts";
 sdb.createDomain(new CreateDomainRequest(domain));

 List<ReplaceableItem> data = new ArrayList<ReplaceableItem>();

 String encryptedName = crypto.encrypt("Acme Life, LLC");

 data.add(new ReplaceableItem().withName("account_02").withAttributes(
  new ReplaceableAttribute().withName("name").withValue(encryptedName)));

 sdb.batchPutAttributes(new BatchPutAttributesRequest(domain, data));

 String qry = "select * from " + SimpleDBUtils.quoteName(domain) 
   + " where name = '" + encryptedName + "'";

 SelectRequest selectRequest = new SelectRequest(qry);
 for (Item item : sdb.select(selectRequest).getItems()) {
  Assert.assertEquals("account_02", item.getName());
 }
}

Ở đây tôi đã làm theo các bước tương tự trong ví dụ MongoDB của mình: Tôi đọc từ một kho khóa hiện có, thu được một kết nối đến SimpleDB của Amazon rồi chèn một bản ghi tài khoản có thuộc tính name đã được mã hóa. Cuối cùng, tôi tra tìm tài khoản theo tên, bằng cách sử dụng giá trị mã hóa làm khóa của nó.


Kết luận

Trong khi điện toán đám mây hứa hẹn sẽ làm cho dữ liệu của bạn có thể truy cập tới một đối tượng người dùng rộng lớn, bạn có thể làm rất nhiều việc để bảo vệ dữ liệu nhạy cảm. Trong bài viết này, tôi đã giới thiệu cho bạn cách sử dụng các thư viện nền tảng Java để bảo đảm an toàn cho dữ liệu ở phần còn lại trên một cơ sở hạ tầng đám mây như MongoDB hoặc SimpleDB. Mã hóa khóa bí mật giữ an toàn dữ liệu trong tay của người quản trị dữ liệu đó. Và việc bạn lưu trữ các khóa bí mật bằng tiện ích KeyStore Java sẽ giúp quản lý chúng dễ dàng và an toàn. Chỉ cần một mật khẩu duy nhất để truy cập một khóa bí mật và bạn không bao giờ muốn làm một điều là lưu trữ mật khẩu đó dưới dạng văn bản rõ ở bất cứ nơi nào gần đám mây.

Dữ liệu lưu trữ đã mã hóa vận hành khác với dữ liệu văn bản rõ trong bối cảnh của một số tìm kiếm. Việc so khớp chính xác sẽ hoạt động tốt, nhưng có thể là một vấn đề hóc búa với các truy vấn điều kiện có liên quan đến các so khớp không chính xác. Giải pháp ở đây tùy thuộc vào cách bạn xử lý (hoặc không xử lý) so sánh đó. Luôn suy nghĩ thấu đáo xem bạn sẽ mã hóa cái gì bằng các truy vấn dự kiến của mình trong tâm trí. Việc mã hóa tất cả dữ liệu của bạn có thể là quá nhiều, do đó, hãy cân nhắc xem truy vấn cái gì và truy vấn như thế nào.

Tài nguyên

Học tập

Thảo luận

  • Hãy tham gia vào cộng đồng My developerWorks. Kết nối với những người sử dụng developerWorks khác trong khi khám phá các blog, các diễn đàn, các nhóm và các wiki theo hướng nhà phát triển.

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Công nghệ Java, Cloud computing
ArticleID=860451
ArticleTitle=Phát triển Java 2.0: Bảo đảm an toàn dữ liệu của ứng dụng Java trên điện toán đám mây
publish-date=03052013