MySQL - SQL Injection

Xin chào các bạn đang học lập trình! Hôm nay, chúng ta sẽ cùng nhau khám phá một chủ đề rất quan trọng trong thế giới bảo mật cơ sở dữ liệu: SQL Injection. Là một giáo viên máy tính gần gũi, tôi sẽ hướng dẫn các bạn hiểu rõ khái niệm này, ngay cả khi bạn chưa từng viết một dòng mã trước đây. Hãy cùng nhau nhâm nhi một tách cà phê ảo và bắt đầu hành trình thú vị này nhé!

MySQL - SQL Injection

SQL Injection là gì?

Trước khi đi sâu vào chi tiết, hãy bắt đầu từ những điều cơ bản. SQL Injection giống như một kẻ trộm lén lút tries vào nhà bạn bằng cách lợi dụng một lỗ hổng trong ổ khóa cửa trước. Trong thế giới số hóa, nó là một kỹ thuật được các χρή dụng xấu sử dụng để操纵 cơ sở dữ liệu của bạn bằng cách chèn mã SQL độc hại vào các truy vấn của ứng dụng.

Một ví dụ đơn giản

Hãy tưởng tượng bạn có một hộp ma thuật (cơ sở dữ liệu của bạn) chỉ phản hồi với các lệnh viết trên giấy. Bạn, với tư cách là chủ sở hữu hợp pháp, viết "Hiển thị tất cả các đồng tiền vàng" trên một mảnh giấy, và hộp sẽ tuân theo lệnh của bạn. Bây giờ, hãy tưởng tượng một kẻ trộm khéo léo tìm cách thêm lệnh của riêng họ vào mảnh giấy của bạn, chẳng hạn như "...và đưa chúng cho tôi!" Đó chính xác là điều mà SQL Injection làm - nó lừa cơ sở dữ liệu của bạn thực hiện các lệnh không được ủy quyền.

Cách SQL Injection hoạt động

Hãy phân tích điều này với một ví dụ thực tế. Giả sử chúng ta có một biểu mẫu đăng nhập đơn giản trên một trang web kiểm tra thông tin người dùng với cơ sở dữ liệu.

Mã dễ bị tấn công

Dưới đây là một đoạn mã PHP dễ bị tấn công:

$username = $_POST['username'];
$password = $_POST['password'];

$query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
$result = mysqli_query($connection, $query);

if(mysqli_num_rows($result) > 0) {
echo "Đăng nhập thành công!";
} else {
echo "Đăng nhập thất bại!";
}

Đoạn mã này có vẻ vô hại, phải không? Nó lấy tên người dùng và mật khẩu từ biểu mẫu, tạo một truy vấn SQL, và kiểm tra xem có người dùng phù hợp trong cơ sở dữ liệu hay không. Nhưng đây là nơi mà mọi thứ trở nên phức tạp.

Tấn công

Bây giờ, hãy giả sử một người dùng xấu nhập vào tên người dùng sau:

admin' --

Truy vấn của chúng ta nay trở thành:

SELECT * FROM users WHERE username='admin' -- ' AND password=''

Thấy điều gì đó rồi chứ? Dấu -- là một nhận xét trong SQL, có nghĩa là mọi thứ sau nó sẽ bị bỏ qua. Kiểm tra mật khẩu ban đầu của chúng ta đã bị bypass hoàn toàn! Truy vấn nay thực chất chỉ kiểm tra xem có người dùng tên là 'admin' hay không, không liên quan đến mật khẩu.

Phòng ngừa SQL Injection

Bây giờ chúng ta đã thấy SQL Injection có thể nguy hiểm như thế nào, hãy thảo luận về cách phòng ngừa. Có nhiều phương pháp chúng ta có thể sử dụng để加固 mã chống lại các cuộc tấn công này.

1. Sử dụng Truy vấn Parameterized

Truy vấn Parameterized giống như sử dụng một két an toàn thay vì hộp ma thuật mà chúng ta đã nói ở trên. Chúng tách biệt lệnh SQL khỏi dữ liệu, làm cho kẻ tấn công几乎无法 chèn mã độc hại.

Dưới đây là cách chúng ta có thể viết lại ví dụ trước đó bằng cách sử dụng truy vấn Parameterized trong PHP:

$username = $_POST['username'];
$password = $_POST['password'];

$stmt = $connection->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();

if($result->num_rows > 0) {
echo "Đăng nhập thành công!";
} else {
echo "Đăng nhập thất bại!";
}

Trong phiên bản này, chúng ta sử dụng prepare() để tạo một mẫu cho truy vấn của mình, và sau đó bind_param() để an toàn chèn các biến. Đó giống như đặt dữ liệu của chúng ta vào một phong bì an toàn trước khi gửi đến cơ sở dữ liệu.

2. Kiểm tra và Làm sạch Đầu vào

Một lớp bảo vệ khác là kiểm tra và làm sạch tất cả các đầu vào của người dùng. Đây giống như có một bảo vệ ở cửa, kiểm tra papers của mọi người trước khi họ vào.

$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
$password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_STRING);

Mã này sử dụng hàm filter_input()内置 trong PHP để loại bỏ bất kỳ ký tự có hại nào từ đầu vào.

3. Nguyên tắc Least Privilege

Đây là cách nói hoa mỹ để nói "chỉ cấp cho người dùng tối thiểu quyền họ cần." Trong thuật ngữ cơ sở dữ liệu, nó có nghĩa là tạo ra các người dùng cơ sở dữ liệu khác nhau với quyền hạn chế cho các phần khác nhau của ứng dụng.

Ví dụ, nếu bạn có một phần của trang web chỉ cần đọc dữ liệu, bạn có thể sử dụng một người dùng cơ sở dữ liệu chỉ có quyền SELECT:

CREATE USER 'readonly_user'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT ON mydb.* TO 'readonly_user'@'localhost';

4. Sử dụng ORM (Object-Relational Mapping)

ORMs giống như thuê một nhà phiên dịch chuyên nghiệp biết tất cả các协议 an toàn. Chúng xử lý việc giao tiếp giữa ứng dụng của bạn và cơ sở dữ liệu, thường bao gồm các biện pháp bảo vệ内置 chống lại SQL Injection.

Dưới đây là một ví dụ đơn giản sử dụng ORM phổ biến trong PHP, Doctrine:

$user = $entityManager->getRepository(User::class)->findOneBy([
'username' => $username,
'password' => $password
]);

if ($user) {
echo "Đăng nhập thành công!";
} else {
echo "Đăng nhập thất bại!";
}

Mã này đạt được cùng kết quả như ví dụ ban đầu, nhưng ORM xử lý tất cả việc tạo SQL và binding tham số cho chúng ta, giảm thiểu nguy cơ SQL Injection.

Tóm tắt các phương pháp phòng ngừa

Dưới đây là bảng tóm tắt các phương pháp chúng ta đã thảo luận:

Phương pháp Mô tả Hiệu quả
Truy vấn Parameterized Tách biệt lệnh SQL khỏi dữ liệu Rất cao
Kiểm tra và Làm sạch Đầu vào Làm sạch dữ liệu người dùng Cao
Nguyên tắc Least Privilege Giới hạn quyền người dùng cơ sở dữ liệu Trung bình
Sử dụng ORM Xử lý giao tiếp cơ sở dữ liệu an toàn Cao

Nhớ rằng, trong thế giới an ninh, luôn tốt hơn khi có nhiều lớp bảo vệ. Hãy nghĩ đến nó như việc mang cả dây an toàn và túi khí trong xe - mỗi lớp đều thêm một lớp an toàn.

Kết luận

Và thế là chúng ta đã cùng nhau hành trình qua thế giới nguy hiểm của SQL Injection và ra về với kiến thức để tự vệ. Nhớ rằng, trong bối cảnh an toàn trực tuyến liên tục thay đổi, việc giữ vững và áp dụng các thực hành tốt nhất là rất quan trọng.

Là người giáo viên máy tính đáng tin cậy của bạn, tôi hy vọng hướng dẫn này đã chỉ ra con đường đến các thực hành lập trình an toàn hơn. Hãy tiếp tục thực hành, 保持好奇心, và quan trọng nhất, luôn kiểm tra đầu vào của bạn! Hẹn gặp lại các bạn, chúc các bạn lập trình (và an toàn) vui vẻ!

Credits: Image by storyset