SQL injection

SQL injection là gì?

  • SQL injection là kĩ thuật cho phép các kẻ tấn công thực hiện các lệnh thực thi SQL bất hợp pháp (mà người phát triển không lường trước được), 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.
  • SQL Injection tấn công vào `hệ thống cơ sở dữ liệu` của ứng dụng (bao gồm các ứng dụng web, mobile hoặc desktop...) thông qua việc khai thác các lỗ hổng bảo mật với mục đich lấy về, thay đổi hoặc thậm chí là phá huỷ hệ thống cơ sở dữ liệu của ứng dụng.

Cách thức hoạt động của Sql injection

Ví dụ 1

  • Lỗi Sql injection thường xảy ra do sự thiếu kiểm tra dữ liệu truyền vào, điều này gây ra những tác động không mong muốn ngoài mục đích chính của câu truy vấn. Ta hãy xem xét câu truy vấn sau:
// Câu lệnh SELECT Kiểm tra đăng nhập...
$sqlSelect = <<<EOT
    SELECT *
    FROM khachhang kh
    WHERE kh.kh_tendangnhap = '$kh_tendangnhap' AND kh.kh_matkhau = '$kh_matkhau';
EOT;
  • 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ị:
a' or 't'='t
Như vậy câu truy vấn của ta có được hiểu như sau:
SELECT * FROM khachhang kh WHERE kh.kh_tendangnhap = 'zzxxcc' AND kh.kh_matkhau = 'a' or 't'='t';
Câu truy vấn trên có ý nghĩa là gì? Mệnh đề WHERE của câu truy vấn trên luôn đúng, lí do là vì ‘t’=’t’ luôn cho ra giá trị đúng. Như vậy, thay vì trả về kết quả của 1 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.
  • Nguyên nhân chính của việc truy vấn sai này chính là do dữ liệu của tham số truyền vào. 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?

Ví dụ 2

Nguy hiểm hơn nữa nếu người dùng nhập vào trường input với giá trị như sau:
a' or 't'='t'; DROP TABLE users;
Khi đó câu lệnh SQL từ ứng dụng gửi lên database server sẽ như sau:
SELECT * FROM users 
WHERE name = 'a' or 't'='t'; DROP TABLE users;
Và toàn bộ các bản ghi trên bảng users sẽ bị xoá bỏ!

Các trường hợp thường bị tấn công Sql injection

  • Bất cứ thao tác nào của ứng dụng có thực hiện 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 có thể 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 truy vấn thông tin user.

Cách phòng tránh lỗi Sql injection trong PHP

  • 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 phải thực hiện các biện pháp phòng chống để đảm bảo việc kiểm tra dữ liệu truyền vào không thể gây ra sai lệch khi thực hiện truy vấn.
  • Giải pháp cho việc kiểm tra này là sử dụng “chuỗi escape”. 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 (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, với 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.
  • Ví dụ về hàm addslashes(): kí tự nháy kép lúc này không còn được hiểu là kí tự điểu khiển nữa.

Sử dụng hàm addslashes() trong PHP

Các dữ liệu nhận về từ người dùng gởi đến (send REQUEST GET/POST) cần đưa vào hàm addslashes() để thêm vào các ký tự dấu \ nếu phát hiện trong dữ liệu gởi đến có các ký tự sau:
  • single quote (‘)
  • double quote (“)
  • backslash (\)
  • NULL
hoặc sử dụng hàm `mysqli_real_escape_string($con, $_POST['username']);` của thư viện mysql. Kết quả cũng tương tự.

Thực hành

Tạo trang Đăng nhập với 2 thông tin đơn giản như sau:
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SQL Injection và cách phòng chống - Nền tảng .vn</title>
</head>

<body>

  <form name="frmDangNhap" id="frmDangNhap" method="post" action="">
    Tên tài khoản: <input type="text" name="kh_tendangnhap" id="kh_tendangnhap" /><br />
    Mật khẩu: <input type="text" name="kh_matkhau" id="kh_matkhau" /><br />

    <input type="submit" name="btnLogin" id="btnLogin" value="Đăng nhập" />
  </form>

  <?php
  // Hiển thị tất cả lỗi trong PHP
  // Chỉ nên hiển thị lỗi khi đang trong môi trường Phát triển (Development)
  // Không nên hiển thị lỗi trên môi trường Triển khai (Production)
  ini_set('display_errors', 1);
  ini_set('display_startup_errors', 1);
  error_reporting(E_ALL);

  // Truy vấn database
  // 1. Include file cấu hình kết nối đến database, khởi tạo kết nối $conn
  include_once(__DIR__ . '/../../../dbconnect.php');

  // Chưa đăng nhập -> Xử lý logic/nghiệp vụ kiểm tra Tài khoản và Mật khẩu trong database
  if (isset($_POST['btnLogin'])) {
    // Phân tách thông tin từ người dùng gởi đến qua Request POST
    // Bổ sung hàm addslashes để chống SQL Injection
    $kh_tendangnhap = addslashes( $_POST['kh_tendangnhap'] );
    $kh_matkhau = addslashes( $_POST['kh_matkhau'] );
    // var_dump($kh_tendangnhap);
    // var_dump($kh_matkhau);die;

    // Câu lệnh SELECT Kiểm tra đăng nhập...
    $sqlSelect = <<<EOT
    SELECT *
    FROM khachhang kh
    WHERE kh.kh_tendangnhap = '$kh_tendangnhap' AND kh.kh_matkhau = '$kh_matkhau';
EOT;

    // var_dump($sqlSelect);die;

    // Thực thi SELECT
    $result = mysqli_query($conn, $sqlSelect);

    // In ra màn hình câu lệnh vừa thực thi
    echo 'Câu lệnh vừa thực thi: <br />';
    echo $sqlSelect;
    echo '<br />';

    // Sử dụng hàm `mysqli_num_rows()` để đếm số dòng SELECT được
    // Nếu có bất kỳ dòng dữ liệu nào SELECT được <-> Người dùng có tồn tại và đã đúng thông tin USERNAME, PASSWORD
    if (mysqli_num_rows($result) > 0) {

      echo '<h2>Đăng nhập thành công!</h2>';

    } else {
      echo '<h2 style="color: red;">Đăng nhập thất bại!</h2>';
    }
  }
  ?>
</body>

</html>

Ví dụ nhập liệu

Nhập các mẫu hack sau:
a' or 't'='t

Tham khảo

Nên nhớ xem kỹ thuật hack để biết và phòng tránh, nên là một Hacker mũ trắng chân chính.