Bài 08: Cơ chế prepared câu SQL trong PHP

Ở một số website code bằng PHP thuần thường mắc phải lỗi SQL injection là do lập trình viên không biết đến lỗi này hoặc biết nhưng không tìm cách khắc phục nó. Trước đây chúng ta hay sử dụng hàm addslashes() trong PHP để khắc phục nhưng bây giờ các thư viện xử lý database cũng đã đưa ra cơ chế prepared nhằm hạn chế lỗi bảo mật này.

1. Cơ chế prepared câu SQL

Trước đây nếu truyền tham số vào câu truy vấn thì chúng ta hay thực hiện như ví dụ dưới đây:

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

$sql = "select * from users where username = '$username' and password = '$password'";

Đây là cách gán chuỗi thông thường và sẽ mắc phải lỗi SQL injection. Ta có một cách khác sẽ khắc phục được phần nào đó là sử dụng hàm addslashes().

$username = addslashes($_POST['username']);
$password = addslashes($_POST['password']);

$sql = "select * from users where username = '$username' and password = '$password'";

Bây giờ người ta đã không sử dụng cách trên nữa mà sử dụng chức năng có sẵn trong các thư viện MySQLi và PDO. Mỗi thư viện có cách thể hiện khác nhau nhưng chúng đều có chung một quy tắc đó là:

  • Chúng ta đưa vào một câu truy vấn với các tham số là một ẩn danh
  • Chúng ta truyền vào giá trị tương ứng cho các ẩn danh đó
  • PHP sẽ dựa vào thư tự các tham số ẩn danh và các giá trị để repared sao bảo mật nhất.
  • Cuối cùng sẽ thực thi câu truy vấn.
  • Khi bạn đã khai báo các tham số lần đầu rồi và sau đó muốn sử dụng tiếp thì không cần phải khai báo nữa. Đây cũng chính là lợi thế của cơ chế prepared trong PHP.

Để dễ hiểu hơn thì bạn nên đọc qua phần 2 và phần 3 dưới đây.

2. Prepared câu SQL trong MySQLi

Chúng ta chỉ thực hiện Prepared trong trường hợp sử dụng MySQLi Object-oriented.

Các tham số truyền vào sở là các dấu chấm hỏi ?, đây chính là tham số ẩn danh.

// Kết nối
$conn = new mysqli('localhost', 'root', 'vertrigo', 'FreetutsDemo');

// kiểm tra kết nối
if ($conn->connect_error) {
    die("Kết nối thất bại: " . $conn->connect_error);
}

// Câu SQL
$sql = "INSERT INTO News (title, content) VALUES (?, ?)";

// Tạo đối tượng repared
$stmt = $conn->prepare($sql);

// Gán giá trị vào các tham số ẩn
$stmt->bind_param("ss", $title, $content);

// Thực thi câu truy vấn lần 1
$title = 'Tiêu đề 1';
$content = 'Nội dung 1';
$stmt->execute();

// Thực thi câu truy vấn lần 2
$title = 'Tiêu đề 2';
$content = 'Nội dung 2';
$stmt->execute();

echo "Thêm thành công!";

// Giải phóng và ngắt kết nối
$stmt->close();
$conn->close();

Trong đoạn code trên có nhiều vướng mắc mà có lẽ bạn sẽ không hiểu nên mình sẽ giải thích thêm.

Thứ nhất: Câu SQL

$sql = "INSERT INTO News (title, content) VALUES (?, ?)";

Trong câu SQL này thì hai dấu ? chính là hai tham số ẩn danh.

Thứ hai: Thực hiện bind data

$stmt->bind_param("ss", $title, $content);

Giá trị đầu tiên là hai chữ ss, đây chính là khai báo dữ liệu cho hai tham số ẩn danh ở trên. Ý nghĩa như sau:

  • i: interger
  • d: double
  • s: string
  • b: blob

Còn hai tham số $title$content chính là hai tham số dạng tham chiếu nên dù chưa được khai báo nhưng vẫn không bị lỗi.

Những đoạn code còn lại quá đơn giản nên mình không giải hích thêm.

3. Prepared câu SQL trong PDO

Với PDO thì cú pháp có hơi khác chút xíu đó là tham số ẩn không phải là dấu hỏi nữa mà là ở dạng :varname.

try {
    // Kết nối
    $conn = new PDO("mysql:host=localhost;dbname=FreetutsDemo", 'root', 'vertrigo');
    // Khai baso exception
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // prepare sql and bind parameters
    $stmt = $conn->prepare("INSERT INTO News (title, content) VALUES (:title, :content)");
    $stmt->bindParam(':title', $title);
    $stmt->bindParam(':content', $content);

    // Thêm lần 1
    $title = 'Tiêu đề 1';
    $content = 'Nội dung 1';
    $stmt->execute();

    // Thêm lần 2
    $title = 'Tiêu đề 2';
    $content = 'Nội dung 2';
    $stmt->execute();

    echo "Thao tác thành công!";
} 
catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}

// Ngắt kết nối
$conn = null;

Giải thích như phần MySQLi nên nếu ban không hiểu thì hãy đọc lại phần 2 nhé.

4. Lời kết

Như vậy là mình đã giới thiệu cách sử dụng cơ chế Prepared câu SQL trong PHP bằng hai thư viện MySQli và PDO, đây là cách chúng ta nên sư dụng để thực thi các câu truy vấn khi làm ứng dụng thực tế bởi nó tránh được lỗi SQL Injection. Bài tiếp theo chúng ta sẽ tìm hiểu cách lấy dữ liệu từ MySQL trong PHP.

Hãy để lại link bài viết gốc khi chia sẻ bài viết này, mình sẽ report DMCA với những website lấy nội dung mà không để nguồn hoặc copy bài với số lượng lớn.

Nguồn: freetuts.net

Profile photo of adminTheHalfHeart

TheHalfHeart

Có sở thích viết tuts nên đã từng tham gia viết ở một số diễn đàn, đến năm 2014 mới có điều kiện sáng lập ra freetuts.net. Sinh năm 90 và có 1 vợ 2 con, thích ca hát và lập trình.

ĐĂNG BÌNH LUẬN: Đăng câu hỏi trên Group Facebook để được hỗ trợ nhanh nhất.