Chuyển tới nội dung chính

Queryable Encryption

Queryable Encryption cho phép ứng dụng của bạn truy vấn dữ liệu đã mã hóa mà không để lộ giá trị gốc cho database. Các field vẫn ở dạng mã hóa trên mọi chặng bên ngoài ứng dụng, kể cả khi được lưu và truy vấn trên cluster. Ứng dụng của bạn mã hóa giá trị trước khi gửi tới MongoDB, và giải mã khi giá trị trả về. Nhờ vậy bạn tìm kiếm được trên field đã mã hóa mà dữ liệu nhạy cảm vẫn được bảo vệ.

So với các cơ chế mã hóa khác​

Mỗi cơ chế bảo vệ một giai đoạn khác nhau trong vòng đời dữ liệu.

Cơ chếBảo vệ dữ liệuServer thấy plaintext
SSL/TLSKhi truyền trên mạngCó
Mã hóa ổ đĩa (TDE)Khi lưu trên đĩaCó, server tự giải mã để xử lý
Queryable EncryptionKhi truyền, khi lưu và khi đang xử lýKhông

Ba cơ chế bổ sung cho nhau chứ không thay thế nhau. Chỉ cột cuối mới thay đổi được những gì một người có quyền đọc database nhìn thấy.

Cơ chế hoạt động​

Code ứng dụng không phải đổi hình dạng. Bạn ghi {ssn: "123-45-6789"} và đọc ra đúng như vậy, còn driver cùng thư viện crypt_shared lo mã hóa lúc gửi đi và giải mã lúc nhận về.

Luồng insert và find đã mã hóa giữa ứng dụng và cluster

Hàng giữa mới là thứ khiến cơ chế này vượt xa mã hóa phía client thông thường. Driver mã hóa cả filter tìm kiếm, nhờ vậy server so khớp ciphertext với ciphertext và trả về đúng bản ghi mà không biết bạn đã tìm gì.

Những truy vấn nào vẫn chạy được​

Việc so khớp chỉ hoạt động với query type bạn khai báo lúc tạo collection, và lựa chọn này là vĩnh viễn suốt vòng đời collection.

Query typeDùng choTrạng thái
equality$eq, $in, $neDùng được trong production từ MongoDB 7.0
range$lt, $lte, $gt, $gteDùng được trong production từ MongoDB 8.0
prefix, suffix, substringTìm theo tiền tố, hậu tố, chuỗi conDùng được trong production từ MongoDB 9.0
noneChỉ mã hóa, không truy vấnLuôn có
Điều này có nghĩa gì trên FPT Cloud

Phiên bản 8.0.20 cho bạn equality và range, còn 7.0.30 chỉ cho equality. Phiên bản 6.0.27 không dùng được, vì MongoDB 6.0 chỉ phát hành Queryable Encryption dưới dạng public preview và bản preview đó nay đã ngừng hỗ trợ.

Những giới hạn cần tính trước​

Mã hóa một field đồng nghĩa với thu hẹp những gì bạn làm được với nó. Hãy đối chiếu danh sách này với truy vấn thật của ứng dụng trước khi chốt schema.

Giới hạn đầu tiên chi phối mọi thứ còn lại. Bạn chỉ bật được Queryable Encryption trên collection mới và rỗng, và sau đó không đổi được danh sách field mã hóa. Chọn sai nghĩa là phải tạo collection mới rồi migrate dữ liệu.

Các giới hạn còn lại áp dụng cho chính những field đã mã hóa.

  • Một collection không dùng đồng thời Queryable Encryption và CSFLE.
  • Không hỗ trợ collation.
  • Không hỗ trợ updateMany và deleteMany, còn bulkWrite chỉ được một namespace.
  • Update trên field mã hóa chỉ nhận $set và $unset, không nhận $inc hay $push.
  • findOneAndUpdate và findOneAndReplace phải trả về bản ghi trước khi sửa (returnDocument: before) và không dùng được projection.
  • Truy vấn field mã hóa với null, với regex, hoặc so sánh với field mã hóa khác đều lỗi.
  • Trong aggregation, không dùng được $out và $merge, còn $group chỉ chạy trên field không mã hóa.
  • Không dùng được distinct.
  • Phải xóa collection bằng client đã bật Queryable Encryption, nếu không các collection metadata sẽ còn sót lại.

Danh sách đầy đủ có trong tài liệu Supported Operations for Queryable Encryption của MongoDB.

Các bước thiết lập​

Trình tự giống nhau ở mọi ngôn ngữ, và hai trang tiếp theo sẽ đi qua từng dòng code.

Năm bước thiết lập, từ tạo master key đến đọc ghi qua client đã bật mã hóa

Tạo master key​

Khi dùng thử, đây là file local chứa 96 byte ngẫu nhiên, đọc một lần lúc ứng dụng khởi động. Lên production thì thay bằng KMS (AWS, Azure, GCP hoặc KMIP).

Khai báo các field​

Mỗi field mã hóa cần path, bsonType và queryType. Chính queryType quyết định toán tử nào còn chạy được trên field đó, và không đổi được sau khi collection đã tạo.

Tạo collection​

Một lệnh làm ba việc. Driver sinh một Data Encryption Key cho từng field, server tạo collection, và server tạo thêm các collection metadata enxcol_.<COLLECTION>.esc và enxcol_.<COLLECTION>.ecoc bên cạnh. Collection phải chưa tồn tại.

Tạo client có mã hóa​

Đây là client thứ hai, tách khỏi client thường đã tạo collection. Nó nhận field map mà bước 3 trả về, lúc này đã có keyId do driver sinh ra. Bước 3 và 4 bắt buộc theo đúng thứ tự đó, vì field map chỉ có keyId sau khi collection đã được tạo.

Đọc và ghi​

Từ đây collection hoạt động như mọi collection khác. Driver mã hóa các field đã khai báo trước khi chúng rời tiến trình, và giải mã khi bản ghi trả về.

cảnh báo

Dữ liệu đã mã hóa bằng master key bị mất là không thể khôi phục. Hãy backup file khóa ở nơi tách biệt với database, và chuyển sang KMS trước khi đưa dữ liệu thật vào.

Kiểm tra xem đã chạy đúng chưa​

Sau khi script chạy xong, cùng một bản ghi sẽ hiện ra hoàn toàn khác nhau tùy client nào đọc nó.

  • Qua client đã bật mã hóa, ssn đọc ra là plaintext.
  • Qua client thường, hoặc mongosh không có khóa, ssn là BinData subtype 6 và bản ghi có thêm field __safeContent__.
  • Tìm theo giá trị plaintext qua client thường thì không trả về gì cả.

Lỗi thường gặp​

Bảng dưới ánh xạ từng thông báo lỗi sang nguyên nhân của nó.

LỗiNguyên nhân và cách xử lý
Không tìm thấy mongo_crypt_v1.soSai đường dẫn, hoặc chưa cài crypt_shared trên máy chạy ứng dụng
Lỗi phiên bản crypt_sharedcrypt_shared phải bằng hoặc mới hơn phiên bản server
Multi-document updates are not allowed with Queryable EncryptionCode đang gọi updateMany. Chuyển sang updateOne
Invalid match expression operator on encrypted fieldToán tử không được hỗ trợ cho query type của field đó, ví dụ regex hoặc $gt trên field equality
findAndModify with encryption only supports new: falseLệnh đang dùng returnDocument: after. Đổi sang before
Collection đã tồn tạiQueryable Encryption cần collection mới và rỗng. Hãy xóa nó bằng client đã bật mã hóa trước
Lỗi xác thực khi kết nốiPassword có ký tự đặc biệt chưa được URL-encode

Khuyến nghị cho production​

Các ví dụ dùng file khóa local cho đơn giản ở lần chạy đầu. Hãy đổi những điểm sau trước khi đưa dữ liệu thật vào.

  • Dùng KMS (AWS, Azure, GCP hoặc KMIP) thay cho khóa local. Khóa local nằm trong file, nên ai lấy được file đó là giải mã được toàn bộ dữ liệu mà nó bảo vệ.
  • Không ghi password vào source code. Hãy đọc từ biến môi trường hoặc secret manager.
  • Chỉ mã hóa những field thật sự cần. Mỗi field mã hóa phải trả giá bằng sự linh hoạt khi truy vấn và bằng dung lượng, qua các collection metadata enxcol_.* và field __safeContent__.
  • Quyết định field nào cần truy vấn trước khi tạo collection, vì lựa chọn đó về sau không sửa được.
  • Kiểm thử truy vấn thật của ứng dụng với danh sách toán tử được hỗ trợ trước khi triển khai, đừng để sau mới thử.

Tài liệu tham khảo​

Các trang dưới đây là tài liệu chính thức của MongoDB, đi sâu hơn những gì hướng dẫn này nói.

  • Queryable Encryption là trang gốc của tính năng trong tài liệu MongoDB.
  • Compatibility liệt kê phiên bản server, driver, bản phát hành và kiểu cluster nào hỗ trợ tính năng này.
  • Limitations có danh sách giới hạn đầy đủ, gồm cả những điều trang này không nhắc lại.
  • Supported operations bao quát mọi lệnh và toán tử chạy được trên field đã mã hóa.
  • Quick start là hướng dẫn thực hành của chính MongoDB, có cho nhiều ngôn ngữ.
  • Automatic Encryption Shared Library hướng dẫn cách tải và cấu hình crypt_shared.

Bước tiếp theo​