I. Giới thiệu về NoSQL
1. Khái niệm NoSQL
Khi nghe đến khái niệm NoSQL, nhiều người có thể thắc mắc về ý nghĩa thực sự của nó. Nếu SQL đã tồn tại từ lâu, lý do nào khiến NoSQL xuất hiện? Để hiểu rõ về NoSQL, chúng ta cần xem xét lại một số đặc điểm của ngôn ngữ SQL truyền thống.
Hệ quản trị cơ sở dữ liệu quan hệ (RDBMS) sử dụng ngôn ngữ SQL yêu cầu dữ liệu cần có cấu trúc rõ ràng (bao gồm bảng, dòng và cột), phù hợp với các ứng dụng có dữ liệu cấu trúc. Các cơ sở dữ liệu quan hệ thường phải mở rộng theo chiều dọc (vertical scaling), có nghĩa là nâng cấp phần cứng máy chủ (như CPU, RAM). Tuy nhiên, việc sử dụng SQL có thể gặp phải một số thách thức như:
- Nếu dữ liệu cần đưa vào cơ sở dữ liệu không có cấu trúc rõ ràng thì sao?
- Vấn đề về mở rộng - không phải lúc nào cũng tiện lợi khi phải nâng cấp phần cứng.
- Khi dữ liệu lớn và có tính chất phức tạp, các phép toán truy vấn có thể gây giảm hiệu suất.
Do đó, NoSQL đã ra đời để khắc phục các vấn đề này, cung cấp giải pháp cho hiệu năng, khả năng mở rộng và quản lý dữ liệu phi cấu trúc mà SQL không thể xử lý tốt trong một số tình huống hiện đại. Điều này cũng đã được phản ánh trong tên gọi "Non SQL" (phi SQL) hoặc "Non-relational SQL" (phi quan hệ SQL). Được giới thiệu đầu tiên bởi Carlo Strozzi vào năm 1998, NoSQL đã qua nhiều giai đoạn phát triển và thể hiện được những ưu điểm nổi bật, đặc biệt là trong các lĩnh vực sau:
Lưu trữ dữ liệu phi cấu trúc và bán cấu trúc
Ví dụ, các loại dữ liệu như tài liệu, key-value, cặp, hoặc đồ thị không thể dễ dàng được thể hiện trong mô hình quan hệ của SQL. Các hệ thống quản lý nội dung như blog, trang web hoặc ứng dụng tin tức thường bắt đầu với cấu trúc dữ liệu đơn giản nhưng sau này có thể phát triển phức tạp hơn.
- Với SQL, việc thay đổi cấu trúc bảng bằng cách thêm cột mới mỗi khi có sự thay đổi về cấu trúc dữ liệu có thể rất mất thời gian và khó duy trì.
- Với NoSQL như MongoDB, các tài liệu JSON (hoặc BSON) có thể lưu trữ dữ liệu một cách linh hoạt mà không cần thay đổi cấu trúc bảng khi có thêm trường mới như video, hình ảnh.
Khả năng mở rộng chiều ngang (horizontal scaling)
NoSQL cho phép bổ sung nhiều máy chủ vào hệ thống mà không cần nâng cấp phần cứng, điều này rất hữu ích đối với các ứng dụng yêu cầu xử lý khối lượng dữ liệu lớn như mạng xã hội hay thương mại điện tử.
- Các mạng xã hội như Instagram hay Facebook cần xử lý và lưu trữ hàng tỷ bài đăng, like, comment mỗi ngày từ hàng triệu người dùng.
- SQL gặp khó khăn khi phải mở rộng quy mô trên nhiều máy chủ, vì nó chủ yếu hỗ trợ mở rộng theo chiều dọc. Ngược lại, NoSQL với Cassandra giúp tăng cường khả năng mở rộng theo chiều ngang, cho phép thêm nhiều máy chủ để xử lý lưu lượng dữ liệu.
Tối ưu cho thao tác đọc/ghi nhanh
NoSQL rất thích hợp cho các ứng dụng yêu cầu hiệu suất cao như phân tích dữ liệu lớn và các trò chơi trực tuyến.
- Một ví dụ điển hình là trang thương mại điện tử lớn như Amazon, cần truy cập dữ liệu sản phẩm, giỏ hàng và trạng thái đơn hàng của hàng triệu người dùng trong thời gian thực.
- Trong khi SQL có thể gặp khó khăn khi phải truy vấn dữ liệu từ ổ cứng, NoSQL với Redis lưu trữ dữ liệu trong bộ nhớ RAM, giúp truy cập dữ liệu nhanh chóng, phù hợp với các ứng dụng yêu cầu phản hồi ngay lập tức như hệ thống cache.
2. Hệ quản trị cơ sở dữ liệu NoSQL: MongoDB
MongoDB là một trong những hệ quản trị cơ sở dữ liệu NoSQL phổ biến hiện nay, thuộc loại cơ sở dữ liệu dựa trên tài liệu (document-based). Trái ngược với việc lưu trữ dữ liệu như các bảng, dòng, và cột trong SQL, MongoDB lưu trữ dữ liệu dưới dạng tài liệu với cấu trúc linh hoạt, cụ thể là tài liệu JSON hoặc BSON (Binary JSON). Bài viết này sẽ tập trung vào các dạng tấn công NoSQL injection nhắm vào hệ quản trị cơ sở dữ liệu MongoDB.
II. Kiến thức cần chuẩn bị
1. Cấu trúc dữ liệu trong MongoDB
Dữ liệu trong MongoDB được tổ chức dưới dạng tài liệu JSON hoặc BSON. Mỗi tài liệu là một tập hợp của các cặp khóa-giá trị (key-value pairs), tương tự như các hàng trong bảng của cơ sở dữ liệu SQL, ví dụ:
json
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"username": "admin",
"email": "admin@example.com",
"roles": ["admin", "editor"],
"last_login": "2024-10-23T18:25:43.511Z"
}
Trường _id
là một trường mặc định trong MongoDB, giữ vai trò là khóa chính. Các trường khác như username
, email
, roles
cấu trúc linh hoạt, không yêu cầu mỗi tài liệu cần phải có cùng số lượng trường.
2. Truy vấn dữ liệu trong MongoDB
Truy vấn dữ liệu trong MongoDB được thực hiện bằng cách sử dụng các phương thức như find()
và findOne()
, ví dụ:
javascript
db.users.find({ username: "admin" });
Truy vấn này sẽ trả về tất cả các tài liệu trong cơ sở dữ liệu có trường username
là admin
. MongoDB cũng hỗ trợ nhiều toán tử để thực hiện các truy vấn phức tạp. Một số toán tử phổ biến bao gồm:
$eq
: So sánh bằng.$ne
: So sánh khác.$gt
,$lt
: So sánh lớn hơn, nhỏ hơn.$in
: Kiểm tra giá trị có nằm trong danh sách hay không.
3. Cách thức tấn công NoSQL injection
Với sự linh hoạt trong cấu trúc dữ liệu của MongoDB, việc bảo mật và kiểm tra dữ liệu đầu vào trở nên rất quan trọng. Khi người dùng nhập dữ liệu mà không được kiểm tra và xử lý đúng cách, kẻ tấn công có thể chèn vào các câu lệnh truy vấn hoặc dữ liệu độc hại dẫn đến lỗ hổng NoSQL injection. Chúng tôi sẽ đi sâu vào từng kỹ thuật tấn công trong các phần tiếp theo.