- Intro to WordPress Plugin Development
- Intro to WordPress Plugin Development: Best Practices
- Intro to WordPress Plugin Development: Using Filters
- Intro to WordPress Plugin Development: Using Actions
- Intro to WordPress Plugin Development: Register Custom Post Types
- Intro to WordPress Plugin Development: Shortcodes
- Intro to WordPress Plugin Development: Loading Scripts and Styles
- Intro to WordPress Plugin Development: Add a Menu to Dashboard
- Intro to WordPress Plugin Development: Adding a Settings Page
- Intro to WordPress Plugin Development: Sanitize and Validate Data
- Intro to WordPress Plugin Development: Object Oriented Programming
- Intro to WordPress Plugin Development: Separate Into Multiple Files
Data sanitization and validation are two very important topics to be aware of when building a plugin. Your plugin will be writing, reading, and editing data in the database. Whenever you interact with the database, you open the possibility of bad things happening.
What is data validation?
Data validation is basically a way to make sure that the data you’re receiving from the user matches what you’re requesting from the user.
For example, if you are requesting a zip code for users in the US, you may want to restrict the input to 5 characters that are between 0-9. We don’t want to allow alphabetical input because no US zip code uses letters. Allowing letters would also open the possibility of the user entering something we don’t want in the input box.
If you remember the previous part of this series, we looked at registering settings. Part of registering the settings was a callback function. This function can be used to validate the data that is being passed to the database from our form.
One way to validate our zip code field would be like this:
<?php $zip = abs( intval( $_POST['zipcode'] ) ); //make sure we only have positive numbers. intval will return 0 if anything other than a number is sent to it. if ( $zip == 0 || strlen( $zip ) != 5 ){ //if $zip is 0 or is not 5 digits long we'll set $zip = ''; $zip = ''; } update_option( 'sd_zip_code', $zip );// will either update the option with a 5 digit number or '' |
What is data sanitization?
If data validation checks the input of the data we are receiving, data sanitization checks the output of the data from the database.
We sanitize the output onto the user’s screen because bad characters could break the output. Here’s an example:
<?php $title = '</h1><h2>mytitle</h2><h1>'; <h1><?php echo $title; ?></h1> //$title would close the first <h1> tag, then output <h2>mytitle</h2>, then open a new <h1> tag to properly close the original H1 |
As you can see the title above would be displayed as an H2 tag, and not an H1 tag as was originally intended. We would also end up with two extra H1 tags with no content in between.
To sanitize this output, we would do something like this:
<?php $title = '</h1><h2>mytitle</h2><h1>'; <h1><?php echo esc_html( $title ); ?></h1> //would output <h1></h1><h2>mytitle</h2><h1></h1> |
This would display the title in the H1 tag as desired. It would look pretty funny to the user, but that would hopefully be corrected once they realized their mistake. We could validate this data, but if it is a long string of text that the user input, it wouldn’t be very user-friendly to just not save their data. An output like this at least keeps the good part of their data, so it can be fixed.
The important part is that our output would be wrapped in the HTML tag we intended for it to be wrapped in.
Let’s look at our settings page again
If you remember from the last lesson in this series, we created a settings page. That page wasn’t very secure though because a user could have entered anything in those text boxes.
Let’s make it a little more secure now.
<?php // this function is the callback function that displays content on our menu's page function sd_display_top_level_menu_page(){ //$settings = get_option( 'sd_fields' ); $teams = array( 'Baltimore Orioles', 'Boston Red Sox', 'Chicago White Sox', 'Cleveland Indians', 'New York Yankees' ); ?> <form method="post" action="options.php"> <?php settings_fields( 'sd_option_group' ); echo wp_nonce_field( 'sd_save_option_group', 'sd_option_group_nonce' ); ?> <table> <tr> <td>Your First Name:</td> <td><input type="text" name="first_name" value="<?php echo esc_attr( get_option( 'first_name' ) ); ?>" /></td> </tr> <tr> <td>Your Email:</td> <td><input type="text" name="email_address" value="<?php echo sanitize_email( get_option( 'email_address' ) ); ?>" /></td> </tr> <tr> <td>Your Favorite Team:</td> <td> <select name="favorite_team"> <?php foreach ( $teams as $team ){ echo '<option value="' . $team . '" ' . selected( $team, esc_attr( get_option( 'favorite_team' ) ) ) . '>' . $team . '</option>'; } ?> </select> </td> </tr> <tr> <td colspan="2"><?php echo submit_button(); ?></td> </tr> </table> </form> <?php } |
Now our form’s output will be more secure. If we were to enter me@example.com!#$
in the email field, it would still save the email address with the !#$ in it, even though they are invalid characters. However, the output would only display me@example.com
without the characters in it because we used the sanitize_email()
function.
Callback functions before saving data
Let’s create our callback functions that will validate the user input before saving it to the database.
function sd_callback_function_name( $input ){ if ( ! isset( $_POST['sd_option_group_nonce'] ) || ! wp_verify_nonce( $_POST['sd_option_group_nonce'], 'sd_save_option_group' ) ) { exit; } $output = esc_attr( $input ); return $output; } function sd_callback_function_email( $input ){ if ( ! isset( $_POST['sd_option_group_nonce'] ) || ! wp_verify_nonce( $_POST['sd_option_group_nonce'], 'sd_save_option_group' ) ) { exit; } $input = sanitize_email( $input ); if ( is_email( $input ) ){ $output = $input; } else { $output = ''; } return $output; } function sd_callback_function_team( $input ){ if ( ! isset( $_POST['sd_option_group_nonce'] ) || ! wp_verify_nonce( $_POST['sd_option_group_nonce'], 'sd_save_option_group' ) ) { exit; } $teams = array( 'Baltimore Orioles', 'Boston Red Sox', 'Chicago White Sox', 'Cleveland Indians', 'New York Yankees' ); if ( in_array( $input, $teams ) ){ $output = $input; } else { $output = ''; } return $output; } |
We created three different functions for the three data types we are expecting. The first one will sanitize the name field. The second function will sanitize the email field, stripping it of any invalid characters, and checking to make sure it is actually an email address. Finally, the third function will make sure that the team name is one of the valid choices from the original array in our form.
The value that these functions return will be what gets saved to the database. So, as you can see in the functions if the values do not match what we’re expecting we tell the function to save an empty value.
Why validate select menu options?
It may seem like a form with a select menu, radio buttons, check boxes, and other pre-defined lists of data won’t need any validation. I mean the user is selecting from our list of choices, right? Well, there are ways that a malicious user can trick our form into accepting other data.
If you are familiar with the inspect element tool in many browsers, you can edit what you see on the screen fairly easily. Doing so, you can take our list of choices in the select menu and change the value of one of the options to whatever you want. By validating the data this field passes to us, we ensure that we are only saving one of the teams we want to be saving.
What’s with the wp_nonce_field?
A nonce is a “number used once”. In WordPress, the nonces are a hash made up of random numbers and letters, and they aren’t used only once. They do have a short lifetime though. So while they aren’t true nonces, they do behave similarly.
The wp_nonce_field
produces hidden fields, which can then be verified later on. This ensures that the values being saved to the database are being saved by actually clicking the “Save” button on your form. Otherwise, a malicious user could try to run the functions that save the data through some other function.
You can then see how we verify the nonce in our callback functions. So, if the nonce check fails, we exit out of the function altogether, therefore it won’t save anything to the database.
Leave a Reply