Xác thực biểu mẫu (form) trong PHP

Xác thực biểu mẫu (form) trong PHP

Hãy đảm bảo tính BẢO MẬT khi xử lý các biểu mẫu PHP!
Chương này sẽ chỉ ra cách xử lý các biểu mẫu PHP một cách an toàn. Xác thực dữ liệu biểu mẫu hợp lệ thì rất quan trọng để bảo vệ biểu mẫu của bạn khỏi tin tặc và kẻ gửi thư rác!

Biểu mẫu HTML mà chúng tôi sẽ làm việc trong chương này, chứa các trường nhập dữ liệu khác nhau như: các trường văn bản bắt buộc và tùy chọn, nút radio và nút gửi như sau:

Xác thực biểu mẫu form trong PHP

Các quy tắc xác thực dữ liệu cho các trường của biểu mẫu ở trên như sau:

Trường Quy tắc xác thực
Name Bắt buộc nhập + Chỉ được chứa các chữ cái và khoảng trắng.
E-mail Bắt buộc nhập + Phải chứa địa chỉ email hợp lệ (có @ và.).
Website Không bắt buộc. Nếu có, nó phải chứa một URL hợp lệ.
Comment Không bắt buộc. Trường nhập nhiều dòng (textarea).
Gender Bắt buộc nhập. Phải chọn một.

Trường văn bản

Các trường Name, Email và Website là các trường nhập văn bản, trường Comment là một vùng nhập văn bản. Mã HTML trông như thế này:


Name: <input type="text" name="name">
E-mail: <input type="text" name="email">
Website: <input type="text" name="website">
Comment: <textarea name="comment" rows="5" cols="40"></textarea>

Nút radio

Các trường Gender là các nút radio và mã HTML trông như thế này:

Gender:
<input type="radio" name="gender" value="female">Female
<input type="radio" name="gender" value="male">Male
<input type="radio" name="gender" value="other">Other

Phần tử biểu mẫu

Mã HTML của biểu mẫu (form) trông như thế này:


<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

Dữ liệu của biểu mẫu sẽ được gửi với phương thức "POST".

Biến $_SERVER["PHP_SELF"] là gì?
$_SERVER["PHP_SELF"] là một biến siêu toàn cục trả về tên file của tập lệnh hiện đang thực thi.

Vì vậy, $_SERVER["PHP_SELF"] sẽ gửi dữ liệu biểu mẫu đến chính trang đó, thay vì chuyển đến một trang khác. Bằng cách này, người dùng sẽ nhận được thông báo lỗi trên cùng một trang với biểu mẫu.

Hàm htmlspecialchars() là gì?
Hàm htmlspecialchars() chuyển đổi các ký tự đặc biệt thành các ký tự mã hóa HTML. Điều này có nghĩa là nó sẽ thay thế các ký tự HTML như < và > bằng &lt; và &gt;. Điều này ngăn chặn kẻ tấn công khai thác mã bằng cách tiêm mã HTML hoặc Javascript (tấn công Cross-site Scripting) vào các biểu mẫu.

Lưu ý lớn về bảo mật biểu mẫu PHP

Biến $_SERVER["PHP_SELF"] có thể được sử dụng bởi tin tặc!

Nếu PHP_SELF được sử dụng trong trang của bạn thì người dùng có thể nhập dấu gạch chéo (/) và sau đó một số lệnh XSS (Cross-site Scripting) để thực thi.

Cross-site Scripting (XSS) là một loại lỗ hổng bảo mật máy tính thường thấy trong các ứng dụng Web. XSS cho phép kẻ tấn công đưa tập lệnh phía máy khách vào các trang Web được xem bởi người dùng khác.

Giả sử chúng ta có biểu mẫu sau trong một trang có tên "test_form.php":


<form method="post" action="<?php echo $_SERVER["PHP_SELF"];?>">

Bây giờ, nếu người dùng nhập URL bình thường vào thanh địa chỉ như "http://www.example.com/test_form.php", đoạn mã trên sẽ được dịch thành:


<form method="post" action="test_form.php">

Tuy nhiên, hãy xem xét trường hợp người dùng nhập URL sau vào thanh địa chỉ:


http://www.example.com/test_form.php/%22%3E%3Cscript%3Ealert('hacked')%3C/script%3E

Trong trường hợp này, đoạn mã trên sẽ được dịch thành:


<form method="post" action="test_form.php/"><script>alert('hacked')</script>

Mã này thêm một thẻ script và một lệnh cảnh báo. Và khi tải trang, mã JavaScript sẽ được thực thi (người dùng sẽ thấy một hộp cảnh báo). Đây chỉ là một ví dụ đơn giản và vô hại về cách khai thác biến PHP_SELF.

Xin lưu ý rằng mọi mã JavaScript đều có thể được thêm vào trong thẻ <script>! Một hacker có thể chuyển hướng người dùng đến một file trên một máy chủ khác và ví dụ, tệp đó có thể chứa mã độc có thể thay đổi các biến toàn cục hoặc gửi biểu mẫu đến một địa chỉ khác để lưu dữ liệu người dùng.

Làm cách nào để tránh khai thác $_SERVER ["PHP_SELF"]?

Có thể tránh khai thác $_SERVER["PHP_SELF"] bằng cách sử dụng hàm htmlspecialchars(). Mã biểu mẫu sẽ trông như thế này:


<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">

Hàm htmlspecialchars() chuyển đổi các ký tự đặc biệt thành các ký tự mã hóa HTML. Bây giờ nếu người dùng cố gắng khai thác biến PHP_SELF, nó sẽ dẫn đến kết quả đầu ra sau:


<form method="post" action="test_form.php/&quot;&gt;&lt;script&gt;alert('hacked')&lt;/script&gt;">

Nỗ lực khai thác thất bại, và không có tác hại nào được thực hiện!

Xác thực dữ liệu biểu mẫu trong PHP

Điều đầu tiên chúng ta sẽ làm là truyền tất cả các biến thông qua hàm htmlspecialchars() của PHP.

Khi chúng ta sử dụng hàm htmlspecialchars(), sau đó nếu người dùng cố gắng gửi nội dung sau trong trường văn bản:

<script> location.href ('http://www.hacked.com') </ script>

Điều này sẽ không được thực thi, bởi vì nó sẽ được lưu dưới dạng mã hóa HTML, như thế này:

&lt;script&gt; location.href ('http://www.hacked.com') &lt;/script&gt;

Mã hiện tại đã an toàn để được hiển thị trên một trang hoặc bên trong một e-mail.

Chúng tôi cũng sẽ làm thêm hai điều nữa khi người dùng gửi biểu mẫu:

  1. Tách các ký tự không cần thiết (khoảng trắng, tab, dòng mới) khỏi dữ liệu đầu vào của người dùng (với hàm trim() của PHP).
  2. Xóa dấu gạch chéo ngược (\) khỏi dữ liệu đầu vào của người dùng (với hàm stripslashes() của PHP).

Bước tiếp theo là tạo ra một hàm sẽ thực hiện tất cả việc kiểm tra cho chúng tôi (thuận tiện hơn nhiều so với việc viết cùng một mã nhiều lần).

Chúng ta sẽ đặt tên hàm là test_input().

Bây giờ, chúng ta có thể kiểm tra từng biến $_POST bằng hàm test_input() và tập lệnh trông như thế này:

<?php    
   // define variables and set to empty values    
   $name = $email = $gender = $comment = $website = "";    
    
   if ($_SERVER["REQUEST_METHOD"] == "POST") {    
      $name = test_input($_POST["name"]);    
      $email = test_input($_POST["email"]);    
      $website = test_input($_POST["website"]);    
      $comment = test_input($_POST["comment"]);    
      $gender = test_input($_POST["gender"]);    
   }    
    
   function test_input($data) {    
      $data = trim($data);    
      $data = stripslashes($data);    
      $data = htmlspecialchars($data);    
      return $data;    
   }    
?>

Đây là kết quả:

Xác thực biểu mẫu form trong PHP

Khi bạn nhập thông tin và nhấn nút Submit:

Kết quả xác thực biểu mẫu form trong PHP
Lưu ý rằng khi bắt đầu tập lệnh, chúng tôi sẽ kiểm tra xem biểu mẫu đã được gửi hay chưa bằng cách kiểm tra $_SERVER["REQUEST_METHOD"]. Nếu REQUEST_METHOD là POST thì biểu mẫu đã được gửi và nó phải được xác thực. Nếu biểu mẫu chưa được gửi thì sẽ bỏ qua xác thực biểu mẫu và hiển thị một biểu mẫu trống.

Tuy nhiên, trong ví dụ trên, tất cả các trường đầu vào là tùy chọn (không bắt buộc nhập). Mọi thứ vẫn hoạt động tốt ngay cả khi người dùng không nhập bất kỳ dữ liệu nào và nhấn nút Submit.

Bước tiếp theo là tạo các trường đầu vào bắt buộc nhập dữ liệu và tạo thông báo lỗi nếu cần.

Các trường bắt buộc trong biểu mẫu PHP

Trong đoạn mã sau, chúng tôi đã thêm một số biến mới: $nameErr, $emailErr, $maleErr và $websiteErr.

Các biến này sẽ chứa các thông báo lỗi cho các trường bắt buộc. Chúng tôi cũng đã thêm lệnh điều kiện if else cho mỗi biến $_POST. Điều này là để kiểm tra xem biến $_POST có trống không (sử dụng hàm empty() trong PHP ).

Nếu nó trống, một thông báo lỗi được lưu trong các biến lỗi khác nhau, ngược lại nó sẽ gửi dữ liệu đầu vào của người dùng tới hàm test_input() để xác thực dữ liệu:

<?php    
   // define variables and set to empty values    
   $nameErr = $emailErr = $genderErr = $websiteErr = "";    
   $name    = $email = $gender = $comment = $website = "";    
    
   if ($_SERVER["REQUEST_METHOD"] == "POST") {    
      if (empty($_POST["name"])) {    
         $nameErr = "Name is required";    
      } else {    
         $name = test_input($_POST["name"]);    
      }    
          
      if (empty($_POST["email"])) {    
         $emailErr = "Email is required";    
      } else {    
         $email = test_input($_POST["email"]);    
      }    
          
      if (empty($_POST["website"])) {    
         $website = "";    
      } else {    
         $website = test_input($_POST["website"]);    
      }    
          
      if (empty($_POST["comment"])) {    
         $comment = "";    
      } else {    
         $comment = test_input($_POST["comment"]);    
      }    
          
      if (empty($_POST["gender"])) {    
         $genderErr = "Gender is required";    
      } else {    
         $gender = test_input($_POST["gender"]);    
      }    
   }    
?>

Hiển thị thông báo lỗi trong PHP

Chúng tôi sẽ bổ sung thêm một số thành phần trong biểu mẫu HTML để hiển thị thông báo lỗi.

Nếu người dùng cố gắng gửi biểu mẫu mà không điền dữ liệu vào các trường bắt buộc sẽ nhận được các thông báo lỗi. Hãy xem ví dụ sau:


<form method="post" action="<?php echo htmlspecialchars($_SERVER[" PHP_SELF "]);?>">

    Name: <input type="text" name="name">
    <span class="error">* <?php echo $nameErr;?></span>
    <br>
    <br>
    E-mail: <input type="text" name="email">
    <span class="error">* <?php echo $emailErr;?></span>
    <br>
    <br> 
    Website: <input type="text" name="website">
    <span class="error"><?php echo $websiteErr;?></span>
    <br>
    <br> 
    Comment: <textarea name="comment" rows="5" cols="40"></textarea>
    <br>
    <br> 
    Gender:
    <input type="radio" name="gender" value="female">Female
    <input type="radio" name="gender" value="male">Male
    <input type="radio" name="gender" value="other">Other
    <span class="error">* <?php echo $genderErr;?></span>
    <br>
    <br>
    <input type="submit" name="submit" value="Submit">

</form>

Khi người dùng không nhập dữ liệu vào các trường bắt buộc trong biểu mầu và nhấn nút Submit thì sẽ nhận được các thông báo lỗi như sau:

Thông báo lỗi trong biểu mẫu PHP

Bước tiếp theo chúng ta sẽ xác thực dữ liệu đầu vào, đó là:

  • Trường Name có chỉ chứa các chữ cái và khoảng trắng không?
  • Trường Email có chứa địa chỉ email hợp lệ không?
  • Trường Website có chứa URL hợp lệ không nếu người dùng cung cấp thông tin?

Xác thực Email và URL trong biểu mẫu PHP

Xác thực tên hợp lệ trong PHP

Mã dưới đây cho thấy một cách đơn giản để kiểm tra xem trường Name chỉ chứa các chữ cái và khoảng trắng. Nếu giá trị của trường Name không hợp lệ, thì lưu thông báo lỗi vào biến $nameErr:


$name = test_input($_POST["name"]);
if (!preg_match("/^[a-zA-Z ]*$/",$name)) {
  $nameErr = "Only letters and white space allowed";
}
Hàm preg_match() tìm kiếm một chuỗi theo mẫu, trả về true nếu mẫu tồn tại.

Xác thực Email trong PHP

Cách dễ nhất và an toàn nhất để kiểm tra xem địa chỉ email có được định dạng tốt hay không là sử dụng hàm filter_var() của PHP.

Trong mã dưới đây, nếu địa chỉ email không hợp lệ, thì lưu thông báo lỗi vào biến $emailErr:


$email = test_input($_POST["email"]);
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
  $emailErr = "Invalid email format";
}

Xác thực URL trong PHP

Mã dưới đây cho thấy một cách để kiểm tra xem cú pháp địa chỉ URL có hợp lệ không. Nếu địa chỉ URL không hợp lệ, thì lưu thông báo lỗi vào biến $websiteErr:


$website = test_input($_POST["website"]);
if (!filter_var($website, FILTER_VALIDATE_URL)) {
  $websiteErr = "Invalid URL";
}

Như vậy là chúng ta đã cập nhật xong xác thực dữ liệu cho các trường Name, Email và URL trong biểu mẫu. Bây giờ tập lệnh trông sẽ như thế này:


<?php
// define variables and set to empty values
$nameErr = $emailErr = $genderErr = $websiteErr = "";
$name = $email = $gender = $comment = $website = "";

if ($_SERVER["REQUEST_METHOD"] == "POST") {
  if (empty($_POST["name"])) {
    $nameErr = "Name is required";
  } else {
    $name = test_input($_POST["name"]);
    // check if name only contains letters and whitespace
    if (!preg_match("/^[a-zA-Z ]*$/",$name)) {
      $nameErr = "Only letters and white space allowed";
    }
  }

  if (empty($_POST["email"])) {
    $emailErr = "Email is required";
  } else {
    $email = test_input($_POST["email"]);
    // check if e-mail address is well-formed
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
      $emailErr = "Invalid email format";
    }
  }

  if (empty($_POST["website"])) {
    $website = "";
  } else {
    $website = test_input($_POST["website"]);
    // check if url is well-formed
    if (!filter_var($website, FILTER_VALIDATE_URL)) {
      $websiteErr = "Invalid URL";
    }
  }

  if (empty($_POST["comment"])) {
    $comment = "";
  } else {
    $comment = test_input($_POST["comment"]);
  }

  if (empty($_POST["gender"])) {
    $genderErr = "Gender is required";
  } else {
    $gender = test_input($_POST["gender"]);
  }
}
?>

Nếu người dùng nhập dữ liệu không hợp lệ sẽ nhận được các thông báo lỗi như hình dưới đây:

Xác thực biểu mẫu form trong PHP

Giữ các giá trị trong biểu mẫu trong PHP

Để hiển thị các giá trị trong các trường đầu vào sau khi người dùng nhấn nút gửi (Submit), chúng tôi thêm một vài lệnh PHP nhỏ bên trong thuộc tính value của các trường đầu vào sau: name, email và website.

Trong trường comment (thẻ textarea) chúng tôi đặt tập lệnh giữa các thẻ <textarea> và </textarea>.

Các lệnh này sẽ xuất ra giá trị của các biến $name, $email, $website và $comment.

Sau đó, chúng tôi cũng cần hiển thị nút radio nào đã được chọn. Đối với trường hợp này, chúng ta phải sử dụng thuộc tính checked (không phải thuộc tính value cho các nút radio):


Name: <input type="text" name="name" value="<?php echo $name;?>">

E-mail: <input type="text" name="email" value="<?php echo $email;?>">

Website: <input type="text" name="website" value="<?php echo $website;?>">

Comment: <textarea name="comment" rows="5" cols="40"><?php echo $comment;?></textarea>

Gender:
<input type="radio" name="gender"
<?php if (isset($gender) && $gender=="female") echo "checked";?>
value="female">Female
<input type="radio" name="gender"
<?php if (isset($gender) && $gender=="male") echo "checked";?>
value="male">Male
<input type="radio" name="gender"
<?php if (isset($gender) && $gender=="other") echo "checked";?>
value="other">Other

Khi người dùng nhập thông tin vào biểu mẫu và nhấn nút Submit thì kết quả như sau:

Xác thực biểu mẫu form trong PHP


Bài viết liên quan:

Hướng dẫn lập trình PHP toàn tập sẽ giúp bạn từng bước tìm hiểu và nắm vững ngôn ngữ lập trình PHP.

Hướng dẫn cách truy xuất, lọc, sắp xếp dữ liệu MySQL trong PHP sử dụng MySQLi và PDO.

MySQL prepared statements trong PHP rất hữu ích để chống lại các cuộc tấn công SQL Injection.