So far, you have become familiar with the basics of developing plugins and themes for WordPress.
Now would be a good time to also consider the security implications of writing your code.
In this lesson, you're going to learn how to develop with security in mind.
You will learn the benefits of ensuring that your code is secure,
what steps to follow to secure your code, and where to find more information around
developing with security in mind. First, a disclaimer. This lesson was created
as an introduction to being security-minded when developing plugins and themes. The code used in
this tutorial is very simplified. You should not use any of the code used in this tutorial
in your plugins or themes. Please make sure to read the full documentation on security in the
WordPress Developer Handbook at developer.wordpress.org/api/security to ensure that you
follow the correct methods and procedures. What does developing with security in mind mean?
Developing with security in mind is the process of ensuring that your code not only works,
but also does not introduce any security vulnerabilities. If your plugin or theme
code has security vulnerabilities, it could make any WordPress site that has your product installed
open to potential attacks and can lead to that site being compromised. When writing code,
it's important to develop a security mindset and to think about how your code could be used
maliciously. Don't trust any data, whether it's user input, third-party API data, or even data
from your database. Always be checking to make sure it's valid and safe to use.
WordPress has a number of APIs that can help you with common tasks, such as sanitizing user input,
validating data, and escaping output. Rely on using these APIs to help validate and sanitize your data
instead of writing your own functions. And make sure to keep up to date with common vulnerabilities
and keep your code up to date to prevent them. Where does security fit in the development process?
Security should be a consideration at every stage of the development process.
Generally though, the largest number of security vulnerabilities that are found take place at the
PHP layer, which is executed at the server. Therefore, this lesson will focus on the top
most common preventative measures in your PHP code. One of the first steps to take when developing is
to ensure that any user input is sanitized. This means that any data that is being passed from the
user, such as a form submission or URL parameter, is checked to make sure it's safe to use.
In this example code, the name and email fields are being posted from a form that the plugin
generates, and then saved to a custom database table called form submissions. As you can see,
the data is being saved directly to the database without any sanitization. This means that if a
user were to submit a name of John colon drop table form submissions, the SQL insert query would be
run, followed by the drop query, and the table would be deleted from the database. WordPress
has a sanitization API that can be used to sanitize incoming data. You can use the sanitize text field
and sanitize email functions to sanitize the name and email fields before they're using the query.
Sanitize text field
and sanitize email.
To read more about the available sanitization functions available to WordPress developers,
check out the sanitizing page in the WordPress developer documentation.
Next up is validating data. Validating data is the process of testing it against a predefined
pattern or patterns with a definitive result, either valid or invalid. Untrusted data can come
from many sources. Users, third-party API data, even your database data can be considered untrusted,
especially if another plugin may have modified it. Even site admins can make a mistake and enter
incorrect or insecure data, so it's important to always be checking. In this example, a deletion
function requires that a numeric ID is posted to an admin Ajax callback. Here, the ID is being used
directly in the SQL query without any validation. Again, this means if a user were to submit the ID
of one semicolon drop table form submissions, the same SQL drop query would be run after the insert
and the table would be dropped. To prevent this, you can use PHP's typecasting functionality
to ensure that the value of ID is always an integer value. This can be done by adding
int before the variable name, like so. Note that this will only work if the first character in
the string passed via the post array can be cast as an integer, otherwise the value of ID will be
zero. In that case, it's a good idea to update the code to handle this case.
So we could say something like if ID is equal to and the same type of zero, then we can return
with a JSON response and pass in some kind of error message.
This is one way to validate integer data. To read more about the various ways to validate your data,
check out the section on validating data in the WordPress developer documentation.
Another aspect of security is to ensure that any information you output to the browser is safe,
including any text, HTML, JavaScript code or data from the database. Even if your code is not
responsible for the source of the data being displayed, it is responsible for displaying it
safely. In this example, this code fetches the form submissions from the database and then loops
through the submission and outputs the submission data into an admin screen in the WordPress dashboard.
Here we have three pieces of data that need to be escaped. The submission name
and submission email fields, as well as the submission ID. For the name and email fields,
we can use the built-in WordPress escaping function ESC_HTMR.
We can copy that with the email.
The ID can be escaped by casting it to an integer, as you might have done for the data validation.
Notice that this code follows a key principle of escaping data,
and that you escape the data as late as possible. To read more about the various ways to escape data,
check out the section on escaping outputs in the WordPress developer documentation.
Whenever a request is made, it's important to check that the request is valid.
This means checking that the request is coming from a trusted source.
For example, you might have a short code that renders a form where users can submit their
information. The function to render the form might look something like this.
When the user submits the form, the data is sent to the server.
The data is then processed, sanitized, and stored in the database.
Because the form might appear on any page where the short code is used,
it's possible that a malicious user could attempt to send a post request to the form,
either looking for a vulnerability or by sending multiple requests to the form.
To prevent this, you can check that the request is coming from a trusted source.
In other words, the website rendering the form.
To do this, you can implement something called a "nots" or a number used once.
First, in the form itself, you can add a "nots" field by using the "wp_nots" field function
and passing a "nots" action and "nots" name to the function.
So just below the hidden field on the form, open up "php tags"
and call the "wp_nots" field function and pass in an "action" name
and then pass in a "field" name.
When the form is rendered on the front end, a hidden field is added to the form,
using the "nots" name as the id and name attributes of the hidden field
and the generated "nots" as the field's value.
This data is posted when the form is submitted.
Then, in the function that processes the form data, you can verify that the "nots" has been
sent and is valid by using the "wp_verify_nots" function and passing the "posted_nots" field
and the "nots" action to this function.
If the result of this verification is false, you can exit early, preventing any further code execution.
So before we sanitize the data, we can say "if wp_verify"
"nots" and pass in "wp_learn_form_nots" field hosted in the form
and the action name we set up, which was "wp_learn_form_nots_action"
and we specify "nots" to say if the function returns a false value, in other words,
the "nots" is not verified, then we can redirect to some error page
and exit code execution.
Any time your code makes a web request, be it via redirection to a new URL,
posting data to a form, or making an AJAX request, you should check that that request is valid
using nonces. To read more about how to use nonces in your plugins,
check out the section on nonces in the WordPress developer documentation.
Depending on your code functionality, it's a good idea to restrict certain features only to users
with a specific permission level. For example, you may have a function that deletes data from the
database. While you might do your best to prevent anyone who doesn't have permission to run this
function, it's still a good idea to include checks against this situation. WordPress includes a
robust user roles and capabilities system, which allows you to either use the default user roles
and capabilities or create custom ones. In this case, it could be as easy as only allowing users
who have the capability to manage site options, which is a standard capability that is included
with the administrator role. To do this, you can use the current user can function and check if
they have the capability to manage options. Inside the form submission, nice and early, we can say,
if not current user can manage options. So in other words, if the user can't manage options,
then we want to return early with some kind of response.
The WordPress developer documentation has a detailed section on user roles and capabilities,
which includes a list of all the default capabilities and how to create custom ones.
Finally, make sure to read the entry in the WordPress developer documentation on security,
as it includes all of the examples in this tutorial, as well as additional information
on security best practices, common vulnerabilities, and much more example code.
serving the healthcare care worker that did what they should do,