Understanding PHP Form Validation
A PHP form has two parts. An HTML web page containing UI elements and a backend PHP script for processing the form data. Additionally, there is yet another component, the database. The PHP acts as a middleware for accepting and processing the form data and stores it in the database by running an internal query.
The form validation login must perform three types of validations on the user input:
- Eradicating garbage data: Garbage data corresponds to user inputs that are not acceptable in general. For example, entering blank data, numeric names, and alphabetic age are examples of such data that is illegible by default and hence ends up as garbage.
- Ensuring data integrity: Data integrity refers to rules to ensure the data is credible. For example, an email address submitted by a user must correspond to a valid email domain and be deliverable.
- Enforcing data format compliance: Data format compliance ensures that the data does not contain hidden or embedded information that can be exploited.
The first two types of validation are part of the standard checks to ensure that the data submitted by the user conforms to a set of well-defined formats.
The third validation is vital from a security point of view. That’s because a form is indirectly linked to the databases since any input data submitted through the form eventually becomes part of the database query. Under these circumstances, there is a possibility that a user can hijack the built-in query by presenting data that resembles the query language.
In this post, you will get to follow a step-by-step tutorial through building a sample PHP project that shows you how to handle various form validation scenarios. We will build this project in three phases to showcase the three types of validations explained above.
Before diving into the specifics of PHP development for this project, ensure you have the Docker runtime and Docker Desktop installed on your computer. The sample PHP app presented in this post is deployed and tested using Docker version 24.
PHP Form Validation Examples: Handling Garbage Data
Let’s build a skeletal PHP application that accepts form data. You will subsequently modify the code for handling validations.
Basic PHP Form Validation Application
Application source code
There are two source files for this application. An index.html file defines the frontend webpage with the HTML form and an index.php PHP script for the backed processing logic.
This is a simple HTML page with a form containing a few input fields to accept some information from the user, and a submit button.
This simple PHP backend logic accepts the form submission as a POST request to read all form fields, extract the data, and store them in a MySQL database table named form_data.
Application Docker Setup
Open a terminal and run the following commands in sequence to create a project directory structure with a top-level project directory named ‘php-form-validation’.
Save the index.html and index.php files within the ‘public’ subdirectory.
To run this sample application, you will need two docker containers:
- mysql : This is the container that hosts the MySQL database. It is launched from the default mysql docker image available from Docker hub.
- php-container: This container hosts the web server and PHP runtime. It is a custom docker image that you will build by packaging the application files.
Here is the Dockerfile for building the custom image.
This will create a custom docker image from the official PHP version 8.2 image after installing the MySQL driver and copying the source files from the ‘public’ subdirectory.
Save this file within the top level ‘php-form-validation’ directory. The resulting directory structure looks like this:
Commands for running the application
To run this application, you must run three docker commands from a terminal, ensuring that you are in the top level project directory.
This will start the two containers. After executing the commands, you can check the container’s running status within your Docker Desktop console.
Application database creation
To store the form data submitted via the webpage, you must create a table within the database hosted in the mysql container.
For this step, you have to log into the terminal of mysql container. Under Docker Desktop, this is easily accessible via the “Exec” tab. Once you are at the terminal prompt, log into the MySQL CLI.
Run these commands to create a database named validation and a table within it named form_data.
Make sure to enter the correct syntax in the MySQL CLI, as shown below.
Now, you are ready to test the form. Open the URL http://localhost:8080/index.html in a browser window to launch the PHP application. You will see the webpage containing the form, rendered by index.html.
Here is how you can enter data through this form, which will also be reflected in the database table.
PHP Form Validation for Handling Garbage Data
Let’s try to enter some random data into this form.
As you can see, if the name or other text fields have random characters, the form still accepts it, and the data is stored in the database. Some fields, like the “Age” are validated by MySQL since the form_data table schema checks that it is of integer type. Otherwise, the rest of the data is garbage, which is useless.
To solve this problem, you can use basic PHP functions to check for specific conditions in the data submitted in the form:
- ctype_alpha( ): This function checks for name fields so the user cannot enter non-alphabetic characters.
- preg_match( ): This function performs a regular expression matching to ensure that the address field does not have empty values or contains only whitespaces.
- filter_var( ): This function can perform format validations for specific fields, like checking for a valid email address or a valid URL.
Here is the modified index.php file to handle these input validations. You can see that the PHP code validates the name, address, and email field using the PHP validation functions before inserting the record in the database table. For now, the Userid field is not validated.
To test this modified index.php file with validation checks, you have to update this file in the php-container container. Access this container’s filesystem within the Docker Desktop’s “Files” tab. Replace the existing index.php with the modified code and save it.
Here is a quick test with the modified index.php file to ensure that any garbage entry against name, address, and email is discarded, as intended.
PHP Form Validation: Data Integrity
So far so good. But there is more to form validation than merely checking for character types, regular expression patterns, and formats.
All data submitted through forms must have a field that uniquely identifies the user. Otherwise, there can be duplicate entries from the same user. This brings us to our next set of validations for data integrity.
PHP Form Validation for Data Integrity of User Id and Email Field
In all web applications, some form fields are always used to identify a user uniquely. In this sample application, two fields can be used to ensure the uniqueness of the user, UserId, and Email. Let’s consider the UserId to check for uniqueness. This check validates the duplication of data submitted by the same user.
Here is the next iteration of the index.php file that performs further validation on the $userid variable in the PHP script. It performs an SQL SELECT query to ascertain whether the user id is already stored in the database and prevents the data from being stored in the database if the same userid is found.
Note: The user id uniqueness check can also be done better by adding the UNIQUE constraint during table creation. However, we have used a naive approach to demonstrate advanced validation issues, which will be covered later in this post.
Copy the code and update the index.php file within the php-container docker container’s filesystem. Here is a quick test to ensure the deplication check for the UserId field.
Foolproof Email Validation using Abstract API
There is more to data integrity than just checking for duplicate data. Email is an important piece of data that requires an additional integrity check. Even though an email address has the correct email format, it is not guaranteed to be genuine. Whether the email address is active or an email sent to that address is delivered is still a question.
Therefore, to add an extra layer of integrity check, you can perform a 360-degree validation of the email address. By leveraging the Abstract Email Validation and Verification API, you can check for deliverability of the email domain, among other advanced validations.
Here is the next version of the index.php that performs an additional integrity check on the email address using the API.
After swapping it with the existing file in the php-container container’s filesystem, replace the placeholder <YOUR_ABSTRACTAPI_KEY> with a valid key. You can create a free account on Abstract and access the API dashboard to see your API key.
Now, you can feed in an email address with the correct format but a junk domain to test the email validation.
Advanced PHP Form Validation: Security Compliance
At this point, the sample PHP application has covered validation for all the form fields. Although you can incorporate additional rules, like optional and required field, minimum or maximum characters, and specialized regex checks for enforcing stricter validations for certain fields, this form is more or less well-validated.
Or, that is what you may want to believe. Let’s have some fun with the form.
As you can see, the UserID form field has been exploited to induce a security loophole in two ways:
- SQL injection: By injecting a SQL statement snippet, a user can trick the PHP logic into returning all the records of a form field. In this case, all the user ids stored in the database have been revealed and are compromised. A similar disaster can happen for email addresses or other personal information, depending upon how the PHP backend is written.
So, even though you have taken all precautions for validating the fields, the form validation logic is only complete if additional measures are taken to ensure that the data entered in the form is sanitized to prevent hidden security loopholes like XSS or SQL statements.
Here is your final version of the index.php file that addresses this injection problem.
Swap the existing index.php file in the php-container container with the code inside this file. Note that you still have to replace the placeholder for the Abstract API key.
If you look at the code closely, an additional validate_input( ) function is now added in the code that runs a sanity check on $userid variable to ensure the PHP can detect and warn against XSS or SQL injection attacks.
Common Mistakes in PHP Form Validation
We have seen four iterations of the PHP code for form validation. Based on the learnings across the various tests performed on the form and the iterations, here are the common mistakes you must avoid while writing any form validation logic:
- Not checking for string validation for specific character types.
- Leaving blanks and white spaces in the string.
- Ignoring specific format checks like email address formats.
- Assuming the data is valid, like in the case of an email address with an invalid or unserviceable domain.
- Overlooking hidden data, like scripts and SQL instructions.
Apart from the remedies suggested here, you can employ more advanced and foolproof techniques for form validation, such as
- Using a PHP framework: If you are building a real-world application with PHP, you are better off using one of the tried and tested frameworks like Laravel or CakePHP, which have built-in facilities to guard against all the validation checks, including advanced validations for injection attacks.
- Using prepared statements in SQL: You can eliminate SQL injection issues in PHP by using prepared SQL statements instead of directly running the queries, as shown in the sample application.
- Ensuring a strict database schema: In this sample application, all string datatypes are set to a length of 255 characters. You can mitigate the injection attacks by reducing the length per the field’s practical length. For example., setting the UserID field to a VARCHAR(10) during table creation would have handled both the injection attacks since most user ids are not longer than ten characters.
Working with AJAX and PHP? Check out how to validate emails with AJAX and PHP.
Why is PHP form validation important?
PHP form validation ensures the data submitted through web forms is correct and sane. Besides handling human errors during data entry, the validation checks for spurious inputs intended to cause harm. Human errors can be induced due to wrong character types, such as entering an alphabetic character for the age. Similarly, there can be whitespace in strings that must be filtered out. Spurious inputs are intended to penetrate the application through injection attacks. The most common form of such injections is XSS (Cross Site Scripting) or SQL. The PHP form validation logic should guard against these injection attacks by detecting special characters in the input data.
What are the common mistakes in PHP form validation?
Common mistakes in PHP form validation include ignoring the character type checks, specific formats, and hidden data, which can be used for injection attacks. These mistakes can be handled as part of basic and advanced validation checks. Basic validations include character checks, string patterns, and specific data formats like email addresses. Advanced validations include checking for unique IDs, ensuring email address legitimacy, and discarding any data that contains maliciously injected code. Email address legitimacy can be confirmed using an API service like Abstract API. For detecting injections, additional pattern matching and SQL prepared statements are recommended.
What are the best practices for PHP form security?