WordPress security is for everyone and this article is for anyone who opened the code editor at least once. If you are tweaking WordPress themes or you are a plugin developer looking to recap some best practices, this is the article for you. I will go through some techniques and share some code snippets with you to help you get started or to help you further refine your WordPress security reflexes.
When it comes to WordPress security much depends on the hosting environment, your WP core version, and the contributed plugins you are using. If you are looking for securing your WP installation here is an article for WordPress Security Checklist.
Now let’s focus on upping your skills for plugin development. But before we do that I want to mention something, that many of you forget.
Being creative with WordPress security
Before we begin I want to mention the following: When a software is going to be open source you can not be as creative with security as proprietary software. But if your plugin will never be released as open source software you can be creative ON TOP of well established security patterns. For example on top of a two factor authentication you could implement a functionality that requires you to type in “orange elephant” to a field that says “please type ‘purple hippo’ in this field to prove you are not a robot“. I just made this up, but you can see the pattern where i am going. you can be creative with MD5 and SH265 functions as well.
However in case of open source software since developers and hackers a-like have access to your code stick with established principles instead. I just wanted to mention the above example because so many of you develop for a single site for a single use-case scenario and while you are using open-source technology you also get stuck in the open-source box. I am not saying to rely solely on creative solutions but when the code will never be released spice-it up a little on top of existing patterns. As long as the creative pattern is unique and made up on the spot there is no one else who will be able to crack it. Only use creative security patterns if you are an advanced developer with enough of experience. Now back to coding and WP.
WordPress Security Techniques
Here is what you need to think about whenever you write any line of code.
- Data Validation
- Sanitizing input
- Sanitizing Output
- Using nonces
- Checking users and permissions
- Can I be creative on top of all of the above?
1. Data Validation
This basically means to check if your data is valid or invalid. Do it as early as possible. Preferably on input or before it gets saved in the DB.
PHP functions and patterns used for validations
Here are a few common functions that you can use:
- isset() and empty() – to check if a variable exists at all or is it blank
- is_int() and is_float() – to check for numbers
- mb_strlen() or strlen() – to see if a string has the expected number of characters
- preg_match(), strpos() – to check if a sub-string exists within another string
- count() – to check the length of an array
- in_array() – to see if some variable is in the array
- shitch … case: – pattern to only expect and accept certain preset values
Here is a tip regarding validation:
When you expect a string for instance. and the label says “Please enter your First Name” when the validation fails do not specify in the error message what exactly failed when it’s obvious for any human what it is. So instead of saying “Name too long, this field can only be 256 characters long” just say “Please enter a real name”. The first would give it away that the form failed because you have a strlen() function on it. This will just tell the hacker, ok so I need to write a shorter hack script to enter in the field.
The example above may look trivial now, but when consuming JSON files and using REST API etc. If you know that the front-end is not generating the bad input, because the front-end has form field checks too. That means when a bad input is being passed it’s usually a hack attempt. If you are absolutely sure that it’s not a human being trying to access your API returning nothing is the best way to go.
Remember the saying: Silence is golden, and the other one.. everything you say can and will be used against you. Always exercise your Fifth Amendment rights.
WordPress Validation Functions
Whenever possible use WP validation functions on top of the php ones listed above.
- is_email() – to check if is email
- term_exists() – to see a term exists
- username_exists() – to see if username exists
- validate_file() – to see if file path is real or not
Custom validation functions
You can write your own custom functions that mix and match any of the above or even use programming patterns to decide if the data is valid or not. Validation functions should return true or false. There are two main ways to think of them.
- False until true – meaning bunch of IF statements for checking if invalid return false all the way and if it goes through all the filters it returns true
- is it not a number? -> return false
- is it too long? -> return false
- is it negative? ->return false
- return true
- True if a certain criteria, false for everything else – meaning is it one of these values? if yes return true if not return false for anything else.
- is it ==”past” or is it ==”future” ->return true
- return false
For #1 you have to be very careful to check for all wrong possibilities. For #2 you check for some very specific values. Much safer but limited use.
Here is a function that i wrote to sanitize and validate a specific date format. Sanitize Date and Time values in WordPress.
2. Data Sanitization
It’s for filtering data for a certain way to make it safe for consumption by another function, input in a database or for being output. This very much depends on the context it’s being used and on the next function that will process the data. Certain filtering for data is good enough in one case a different type of filtering may be needed for the same data in another case. For instance maybe HTML is good for the front-end but you don’t want to render it on the back-end admin interface.
PHP sanitization functions
filter_var() – sanitizing data by using filters you can find more about this here https://www.php.net/manual/en/function.filter-var.php
WordPress security and sanitization functions
There are many useful functions available in WordPress. Here some you should always have in your pocket.
- sanitize_email() – filters character that not allowed in email
- sanitize_file_name() – for file names, turns white space ” ” into “-“
- sanitize_hex_color() – for values like
#f72525
- sanitize_hex_color_no_hash() – same without the #
- sanitize_html_class() – for
<div class="'.esc_attr( $post_class) .'">
- sanitize_key() – Lowercase alphanumeric characters “-” and “_” is ok
- sanitize_meta() – specific meta keys of a specific meta type
- sanitize_mime_type() – for mime types
- sanitize_option() – very extensive see https://developer.wordpress.org/reference/functions/sanitize_option/
- sanitize_sql_orderby() – for valid SQL ‘order by’ clause
- sanitize_text_field() – string from user input, very handy!
- sanitize_title() – strips html and php tags, you can provide fallback title
- sanitize_title_for_query() – titles for the ‘query’ context.
- sanitize_title_with_dashes() – same as above above but with dashed
- sanitize_user() – strips out bad characters from usernames
- esc_html() – to escape html value when printing out variables, very handy! use it all the time when possible.
- esc_ur() – cleans URL
- esc_url_raw() – same as above but for databases
- esc_js() – when printing out javascript inside <script> tags
- wp_localize_script() – instead of the above try using this https://codex.wordpress.org/Function_Reference/wp_localize_script
- wp_kses() – filter out disallowed HTML tags, very extensive function, get to know it. https://developer.wordpress.org/reference/functions/wp_kses/
- wp_filter_post_kses() – same as above but for specifically filtering post content and fields
- wp_filter_nohtml_kses() – strips all HTML
- sanitize_email() – when printing out emails, to obscure them from spam bots use it like this
'<a href="mailto:'.antispambot($email,1).'" title="Click to e-mail me" >'.antispambot($email).' </a>'
Print some of these out and hang them on the wall.
Get to know them by heart. When not sure what to use just do a quick Duckduckgo.com search on the web for extra WP functions. Here are some for inputs only https://developer.wordpress.org/plugins/security/securing-input/
This should be second nature to you. When not sure what to use just do a quick Duckduckgo.com search on the web for extra WP functions. Here are some for inputs only https://developer.wordpress.org/plugins/security/securing-input/
Here is a bigger list. Even though the title says validation the page contains a lot of sanitation functions. https://codex.wordpress.org/Data_Validation
Custom sanitization functions
Mix and match any of the above to come up with your own to make whatever is required and add some spice on top, be as strict as you can.
3. Nonce Security
These are unique generated strings that can be used to verify the origin of a submitted form request, to make sure it actually originated from the page you think it was, instead a “POST” is getting triggered from somewhere else on the internet.
For instance you have a form with a bunch of fields and you add a Nonce field to the firm with the following line.
wp_nonce_field('myplugins_form_action','myplugin_nonce_field', false);
This will add a hidden <input> field on your form that will have a unique string that will get posted together with the form field values. Then when processing the form you verify the nonce.
// GET THE NONCE
if (isset($_POST['myplugin_nonce_field'])){
$nonde = $_POST['myplugin_nonce_field'];
}else{
$nonce = false;
}
// Do other stuff
// some time later you Verify The nonce
if (!wp_verify_nonce($nonce, 'my_plugin_form_action')){
wp_die('Noice try hacker!');
}
// continue to process the form
More about nonce fields here https://www.php.net/manual/en/function.filter-var.php .
4. Checking users and permissions
When it comes to WordPress Security this one is easy and worth a lot as long as you do it. Whenever an action is being performed just check if the current user has the permission rights to perform the action. These are called capabilities, check them out here https://wordpress.org/support/article/roles-and-capabilities/. To implement this correctly you need to know what type of right a user can have and check for it in context.
Wrap things in if statements like these.
if (current_user_can('edit_others_posts')) {
/**
* add the delete link to the end of the post content
*/
add_filter('the_content', 'wporg_generate_delete_link');
/**
* register our request handler with the init hook
*/
add_action('init', 'wporg_delete_post');
}
5. WordPress security is your friend
On top of all of these here is a bonus tip for all, but especially for more advanced developers.
Don’t show of your PHP skills by trying to achieve something extra ordinary by doing it in plain PHP when WordPress has an API of doing exactly that.
For instance, if you are building a Settings page for your plugin use the Settings API https://developer.wordpress.org/plugins/settings/settings-api/ it already has a lot of checks and balances built into it, leverage it. On top of security you will have The WP consistency everyone is looking for when they are using plugins.
6. Shoot back
This may be controversial, but you need to be more than average when it comes to security. You can’t just apply all the things everyone else applies and expect different results. There are many options how to fight back against hackers while still staying within legal boundaries.
When it comes to your WP installation add plugins that track hacking attempts and create blacklists of hackers. When it comes to plugin or theme development add traps, labyrinths and feed hackers junk data. The number one motivation for hackers is monetary, and since time is money, let them lose as much time as they need to give up this sport.
Here are some examples:
- Instead of returning valid error messages, return fake success messages. When someone tries to save compromising data, filter it out but return “Data saved successfully message”.
- When someone queries your users illegally, instead of returning “you don’t have access” message return a bogus list of users, all while mimicking a standard WP response.
- Return fake error messages, when a hacker tries to log in generate messages like: “username too short” “password is too long” “this is the old password” “we sent a verification email to [make up email]” or “this is a static website there is no login”.
- Spread fake email addresses all around your websites hidden from regular uses but available for hungry bots to pick up. Make sure they are random enough that nobody actually uses them. Generate hundreds of these “[email protected]”. Usually bots collect them to be sold to marketing companies without consent. Fill their lists with useless junk.
Don’t try to anger them or be smug, just play it dumb but always deflect and feed them junk data.
Feel free to add more suggestions in the comment section!
Cheers! Lehel