How can I know I am writing secure WordPress code?
Even as an experienced coder, it can be daunting to try to write secure code.
There is one golden rule: trust no one!
If you habitually make use of a few good tools, you should be able to significantly reduce potential vulnerabilities. As a bonus, secure code tends to be both performant and readable.
Where to begin — good tools
Use the tools that exist — don’t just write your code in a basic text editor!
I would recommend, as a starting point, using an IDE — Visual Studio Code, PHP Storm (IntelliJ), or, if you want a more editor based option, Sublime Text. Among other features, all of these offer code completion and syntax highlighting. This easily eliminates some of the "basics" and allows you to focus on the actual code.
Next, you need a static code analysis tool. For WordPress coders, this means installing PHP_CodeSniffer. If you integrate this with your IDE, you will get realtime feedback as to whether you are meeting the coding standards that you have selected. For example, CodeSniffer will complain if you do not sanitize the input, escape the output, or use a nonce when receiving data.
Note: A team of volunteers has created a set of WordPress Coding Standards rules (sniffs) to enforce WordPress coding conventions. You can download these, together with integration instructions, from GitHub.
Applying the Golden Rule
What does it mean to "trust no one?"
Your code needs to check that any input passed to it from a user, another coder, or function is what you expect, and that any time you return content, your code confirms that it is the right type of content.
Example of checking input data
In the code fragment below, I first check that a form submitted was created by the website by using a nonce and then using absint() to make sure the form value is a number, followed by using the sanitize_text_field() to clean the name value input.
// Check the value of wp_nonce is what WP created
if ( isset( $_POST['wp_nonce'] ) && wp_verify_nonce( $_POST['wp_nonce'], 'save_form' ) ) {
// make sure that a and ID is an int
$post_id = absint( $_POST['id'] );
// Checks for invalid UTF-8,
// Converts single `<` characters to entities
// Strips all tags
// Removes line breaks, tabs, and extra whitespace
// Strips octets
$name = sanitize_text_field( $_POST['name'] );
// Save form
}
Examples of escaping output data
It is important to escape any translated content, as you don't know what is in the translation. In the following code fragment, the last thing I do before echoing the HTML is to pass it through esc_html() to make sure it is valid and allowed HTML.
echo esc_html( sprintf( '<p>%s</p>', __( ' Some content to by translated', 'text_domain' ) ) );
You should never trust the output of a function, even if you wrote it, as someone else might change it later. In this example, I use esc_url() and esc_attr() to clean the returned output of the functions.
echo sprintf( '<a href="%s" title="%s">click here</a>', esc_url( get_a_url_from_somewhere() ), esc_attr( get_a_title_from_somewhere() ) );
Last word
Security is always going to be a challenge — change is a constant and vulnerabilities exist everywhere. It is our responsibility as coders to do the best we can. At the very least, consistently using the tools available, applying coding standards, and following basic good practice guidelines, is just good sense. Doing so should eliminate a significant proportion of risks and leave you some headspace to tackle the edge-case scenarios.