Trang chủ Bảo mật Tìm hiểu về SQL Injection (Phần 02)

Tìm hiểu về SQL Injection (Phần 02)

bởi Vi Nè
Cập nhật:

SQL injection – còn được gọi là SQLi – sử dụng những lỗ hổng trong các kênh đầu vào (input) của website để nhắm mục tiêu vào cơ sở dữ liệu nằm trong phần phụ trợ của ứng dụng web, nơi lưu giữ những thông tin nhạy cảm và có giá trị nhất.

Chúng có thể được kẻ tấn công sử dụng để ăn cắp hoặc xáo trộn dữ liệu, cản trở sự hoạt động của các ứng dụng, và, trong trường hợp xấu nhất, nó có thể chiếm được quyền truy cập quản trị vào máy chủ cơ sở dữ liệu. Dưới đây là những gì bạn cần biết về tấn công SQL Injection và cách bảo vệ website của bạn khỏi chúng.

Cách thức website bị tấn công SQL Injection

Các cuộc tấn công SQL Injection được thực hiện bằng cách gửi lệnh SQL độc hại đến các máy chủ cơ sở dữ liệu thông qua các yêu cầu của người dùng mà website cho phép. Bất kỳ kênh input nào cũng có thể được sử dụng để gửi các lệnh độc hại, bao gồm các thẻ <input>, chuỗi truy vấn (query strings), cookie và tệp tin.

Để xem cách nó hoạt động, giả sử bạn có một form đăng nhập có 2 input username và password như dưới đây:

Khi người dùng nhập thông tin đăng nhập của họ và nhấn vào nút “log in”, thông tin sẽ được gửi lại cho máy chủ web của bạn, ở đó nó sẽ được kết hợp với một lệnh SQL. Ví dụ, trong PHP, mã sẽ giống như sau:

$sql_command="select * from users where username = '".$_POST['username'];
$sql_command.="' AND password = '".$_POST['password']."'";

Lệnh này sau đó sẽ được gửi đến một máy chủ cơ sở dữ liệu và tập dữ liệu kết quả sẽ xác định xem username và password có tương ứng với một tài khoản người dùng hợp lệ hay không. Ví dụ người dùng nhập “john” làm username và “123456” làm password (đừng bao giờ sử dụng mật khẩu này) sẽ chuyển mã trên thành lệnh sau:

SELECT * FROM users WHERE username='john' AND password='123456'

Nhưng điều gì sẽ xảy ra nếu người dùng quyết định thử cái khác, chẳng hạn như sau:

Lệnh kết quả sẽ là như sau:

SELECT * FROM users WHERE username='john' OR 1=1; -- ' AND password='123456'

Kết quả trả về là thông tin đăng nhập của người dùng có tên là “john” mà không cần mật khẩu chính xác.

Làm sao để tự vệ với các cuộc tấn công SQL Injection?

Với vectơ chính cho các cuộc tấn công SQL Injection là các kênh input của người dùng, hầu hết các phương pháp phòng đều thủ liên quan đến kiểm soát input của người dùng
Dưới đây là một số biện pháp có thể đảm bảo an toàn cho input của người dùng.

Đừng bao giờ tin tưởng vào input của người dùng

Quy tắc đầu tiên về input mà người dùng nhập là “don’t trust and verify” (không tin tưởng và cần xác minh), có nghĩa là tất cả những gì người dùng nhập vào phải được coi là độc hại trừ khi có bằng chứng khác. Nó không chỉ dành cho các hộp nhập liệu đơn giản như các vùng văn bản mà còn cho mọi thứ khác – như input ẩn, các chuỗi tham số truy vấn, cookie và tệp tải lên.

Browsers của trình duyệt không cho phép người dùng thao tác với một input, không nghĩa là nó không thể bị giả mạo. Các công cụ đơn giản như Burp Suite cho phép người dùng chiếm được HTTP requests và sửa đổi bất cứ điều gì, kể cả các giá trị dạng ẩn, trước khi gửi chúng tới máy chủ. Và nếu bạn nghĩ mình là người thông minh bằng cách sử dụng Base 64 mã hóa dữ liệu, bạn đã nhầm, nó có thể dễ dàng được giải mã, sửa đổi và mã hoá lại bởi kẻ tấn công

Xác nhận các chuỗi input ở phía máy chủ

Xác nhận là quá trình đảm bảo dữ liệu người dùng nhập vào là hợp lệ và vô hiệu hóa bất kỳ lệnh độc hại tiềm ẩn nào có thể được nhúng trong chuỗi nhập. Ví dụ, trong PHP, bạn có thể sử dụng mysql \ _real \ _escape \ _string () để thoát các ký tự có thể thay đổi bản chất của lệnh SQL.
Mã đăng nhập được đề cập trước đó sẽ được thay đổi như sau:

$con=mysqli_connect("localhost","user","password","db");
$username = mysqli_real_escape_string($con, $_POST['username']);
$password = mysqli_real_escape_string($con, $_POST['password']);
$sql_command = "select * from users where username = '" . $username; $sql_command .= "' AND password = '" . $password . "'";

Sửa đổi đơn giản này sẽ bảo vệ mã của bạn khỏi cuộc tấn công SQL Injection bằng cách thêm một ký tự thoát (\) phía trước dấu nháy đơn đã được kẻ tấn công thêm vào.

Ghi chú về xác nhận: Sẽ rất tốt nếu bạn đã thêm các chức năng xác thực phía máy khách. Nhưng không nên dựa vào nó như là một biện pháp phòng thủ chống lại các cuộc tấn công SQL injection. Mặc dù các chức năng phía máy khách có thể làm nó khó có thể gửi dữ liệu input nguy hiểm vào máy chủ của bạn, nhưng chức năng này có thể dễ dàng bị phá vỡ bằng một số browser tweak và công cụ khác. Vì vậy, bạn cần phải bổ sung thêm việc xác nhận phía máy chủ.

Một số nền tảng lập trình, chẳng hạn như ASP.NET, bao gồm các tính năng tích hợp sẽ tự động đánh giá input của người dùng nhưng các tin tặc đủ thông minh và tinh tế vẫn có thể phá hoại chúng, vì vậy bạn nên có 1 quy trình chặt chẽ kiểm định input của khách hàng, điều đó sẽ chẳng bao giờ là quá thận trọng.

Sử dụng các câu lệnh tham số

Một lựa chọn tốt hơn để thoát khỏi tấn công SQL Injection là sử dụng các câu lệnh tham số. Các câu lệnh tham số được định nghĩa bằng cách thêm tên của placeholder vào các lệnh SQL, thứ sau này sẽ được thay thế bởi input của người dùng. ASP.NET có một bộ API rất trực quan và dễ sử dụng cho mục đích này.

Đoạn mã sau, được viết bằng C#, cho thấy cách bạn có thể sử dụng các câu lệnh tham số để bảo vệ trang web của bạn khỏi tấn công SQL Injection:

SqlCommand cmd = new SqlCommand ("SELECT * FROM users WHERE username=@username AND password=@password",con);
SqlParameter username = new SqlParameter(); username.ParameterName = "@username"; username.value = txtUsername.Text; cmd.Parameters.Add(username);
SqlParameter password = new SqlParameter(); password.ParameterName = "@password"; password.value = txtPassword.Text; cmd.Parameters.Add(password);

Bạn bắt đầu bằng cách tạo ra một SqlCommand object và sử dụng placeholder @parameter_name trong chuỗi lệnh nơi mà dữ liệu người dùng nhập vào nên được chèn vào.

Sau đó bạn tạo instance của các SqlParameter object, trong đó bạn chèn input của người dùng, thay vì chèn trực tiếp nó vào chuỗi lệnh.

Cuối cùng, bạn thêm SqlParameter object vào bộ tham số SqlCommand object, nó sẽ thay thế các tham số bằng input được cung cấp.

Trong PHP sẽ có PDO và mysqli có thể giải quyết giống như ASP.net. Bạn có thể tham khảo ở đây.

Phân định rõ ràng kiểu input

Mẹo này dành cho các ngôn ngữ như PHP, khi bạn thường không định nghĩa các kiểu dữ liệu cho các biến số
Việc định nghĩa rõ ràng kiểu input như một cách để loại bỏ những dữ liệu có thể gây sai cho câu lệnh SQL. Vì vậy, nếu bạn đang mong đợi người dùng nhập “int” cho tham số “age”, bạn có thể đảm bảo sự an toàn của input với mã sau đây trong PHP:

$age = (int)$_POST['age'];

Lưu ý rằng đoạn mã này chỉ xác nhận kiểu của input chứ không phải phạm vi của nó. Vì vậy, bạn sẽ phải chạy mã khác để đảm bảo người dùng không nhập vào dữ liệu không hợp lệ
Ngoài ra,hành động tốt nhất là tránh sử dụng các dấu nháy đơn trong các lệnh SQL khi không có string được truyền vào. Thay vì sử dụng mã sau đây …

$sql_command = "select * from users where age = " . $age;

… sẽ an toàn hơn một chút nếu sử dụng lệnh sau:

$sql_command = "select * from users where age = '" . $age . "'";

Còn tiếp …

Có thể bạn sẽ thích