Hướng dẫn dùng prepard 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.

Hướng dẫn dùng prepard trong PHP

Hướng dẫn dùng prepard trong PHP

Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

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à:

Bài viết này được đăng tại [free tuts .net]

  • 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.