Sql injection – Tấn công thay đổi hoặc làm lộ CSDL hệ thống

trieu.dev.da

Nguyễn Thanh Triều
Tấn công SQL injection là gì
SQL injection là kĩ thuật cho phép các kẻ tấn công chèn và thực thi các lệnh SQL bất hợp pháp (mà người phát triển không lường trước được) bên trong hệ thống, bằng cách lợi dụng các lỗ hổng bảo mật từ dữ liệu nhập vào của các ứng dụng. Qua đó làm lộ thông tin trong cơ sở dữ liệu, tạo ra sự sai lệch hoặc gây hư hỏng dữ liệu của hệ thống.
Một cách nôm na, ta có thể hiểu tấn công SQL injection là việc truyền vào các mã SQL thông qua các ô nhập liệu, để làm thay đổi mục đích câu truy vấn ban đầu. Với định nghĩa trên, ta có thể tạm chia SQL injection thành một số loại chính sau đây dựa vào cách tấn công dữ liệu truyền vào:
  • Tấn công: không mã hóa kí tự nhập
  • Tấn công: không kiểm tra kiểu dữ liệu nhập
Giải thích cách tấn công SQL injection
Như đã trình bày, lỗi Sql injection thường xảy ra do hệ thống đã thiếu kiểm tra dữ liệu truyền vào. Điều này sẽ thay đổi mục đích ban đầu của câu truy vấn và do đó gây ra những tác động không mong muốn. Ta hãy xem xét câu truy vấn sau:
1
2
3
4
$query_string = "SELECT * FROM users WHERE name = '$userName'";

//Gọi thực thi truy vấn sau đó:
$sql_executer->query( $query_string );
Ta thấy rằng, mục đích chính của câu truy vấn này là lục tìm trong bảng users những dòng dữ liệu nào mà trường name có giá trị bằng với tham số userName đã truyền vào.
Thoạt nhìn thì câu truy vấn này là đúng cấu trúc và không có bất cứ vấn đề gì, thế nhưng ta hãy thử phân tích một tình huống sau đây: giả sử người gọi câu truy vấn này truyền vào tham số userName có giá trị:
1somename' or '1'='1
Như vậy, sau khi ghép dữ liệu nhập vào bên trong, ta có được câu truy vấn như sau:
1SELECT * FROM users WHERE name = 'somename' or '1'='1'
Các bạn có thấy không, câu truy vấn của ta giờ đã mang một ý nghĩa khác. Mệnh đề WHERE của câu truy vấn trên luôn đúng, lí do là vì '1'='1' luôn cho ra giá trị TRUE, dẫn tới câu truy vấn của ta luôn luôn thỏa điều kiện ở mệnh đề WHERE. Như vậy, thay vì trả về kết quả của MỘT dòng dữ liệu mong muốn, câu truy vấn này trả về kết quả là TOÀN BỘ dữ liệu của bảng users. Vậy là dữ liệu của hệ thống đã bị lộ.
Nguyên nhân chính của việc truy vấn sai này chính là: tấn công truyền dữ liệu vào tham số của câu truy vấn nhằm thay đổi ý nghĩa truy vấn. Hãy tưởng tượng rằng toàn bộ dữ liệu này bị sử dụng nhằm mục đích không tốt, hậu quả thật khó lường phải không nào?

Dữ liệu nhập từ người dùng là mục tiêu tấn công của SQL injection
Một số ví dụ về tấn công SQL injection
Ví dụ: dữ liệu truyền vào làm thay đổi mệnh đề điều kiện của truy vấn. Từ đây kẻ tấn công sẽ thực hiện xóa dữ liệu hệ thống
1
2
//Câu truy vấn gốc
$query_string = "SELECT * FROM users WHERE name = '$userName'";
Tham số truyền vào sẽ gây ra lỗi:
1a'; DROP TABLE users;
Khi đó câu truy vấn khi bị tấn công sẽ trở thành:
1SELECT * FROM users WHERE name = 'a'; DROP TABLE users;
Với ý nghĩa ban đầu là: truy vấn thông tin một user với tên được truyền vào, giờ đây câu truy vấn đã thực thi lệnh xóa bảng users. Việc này cực kì nguy hiểm.

Một số tình huống khác dễ bị tấn công SQL injection
Bất cứ thao tác nào của ứng dụng có thực thi truy vấn tới cơ sở dữ liệu đều có thể bị lợi dụng để tấn công Sql injection. Các thao tác cơ bản với CSDL là: select, insert, update đều có thể bị tấn công.
Có thể kể ra vài thao tác phổ biến để tấn công như:
  • Kiểm tra đăng nhập ứng dụng.
  • Thao tác lưu comment của user xuống DB.
  • Thao tác tìm kiếm thông tin: tên user, tên sản phẩm, …
Cách phòng tránh
Như đã phân tích ở trên: điểm để tấn công chính là tham số truyền vào câu truy vấn. Do vậy, cần phải đảm bảo thực hiện việc kiểm tra dữ liệu truyền vào từ người dùng, để tránh người dùng nhập vào những nội dung có thể gây ra sai lệch khi thực hiện truy vấn. Để kiểm tra dữ liệu từ người dùng, ta cần lọc bớt những nội dung nguy hiểm. Giải pháp cho việc lọc dữ liệu này là sử dụng chuỗi được escape (mã hóa). Lưu ý: mình sẽ dùng từ “escape” để chỉ việc mã hóa cho sát nghĩa.
Khi thực hiện escape một chuỗi, tức là mã hoá các kí tự đặc biệt của chuỗi (ví dụ như kí tự ‘, &, |, …) để nó không còn được hiểu là 1 kí tự đặc biệt nữa. Mỗi ngôn ngữ lập trình đều cung cấp các hàm để thực hiện escape chuỗi, trong PHP ta sẽ sử dụng hàm mysqli_real_escape_string() hoặc cũng có thể dùng addslashes() để thực hiện điều này.
1
2
3
4
5
$uname_escaped = mysqli_real_escape_string($sql_executer, $userName);
$query_string = "SELECT * FROM users WHERE name = '$uname_escaped'";

//Gọi thực thi truy vấn sau đó, dùng biến đã escaped:
$sql_executer->query( $query_string );
 
Bên trên