5 Basic Security Practices For Your WordPress Plugin
WordPress is a paradise for the tweakers. Whether to change theme appearance or to add, remove and modify your website's functionality. Possibility to achieve anything is virtually limitless.
Aside from that, WordPress is also one of the open source platforms where you can contribute to the community and share your theme or plugin to those who aren't able to write their own codes. And because of that, it is important for developers to ensure the plugins they build are comply with all security aspects and safe to use by end users.
I would like to share with you 5 basic security practices you should implement in your WordPress plugin. This is not only important for developers, but it is also very important for users as well to ensure the plugin you use is safe for your site. Check out this article if you want to learn how to create a WordPress plugin.
Prevent Direct Access To Your PHP Files
One of the basic practices to secure your WordPress plugin is by preventing direct access to your PHP files. When your WordPress site going through its firing sequences, it will seek and load all appropriate files from your WordPress directory including active plugins directory. If you haven't change your WordPress directory structure, this PHP files usually located within its default directory.
Assume your domain is www.awesome.com, then your plugin files most likely located at /home/your_domain_username/public_html/wp-content/plugins/my-plugin/my-plugin.php. And this files will be accessible at web browsers or other programs by going to http://www.awesome.com/wp-content/plugins/my-plugin/my-plugin.php. This is not safe and has a high possibility for attackers to exploit this files and harm your website.
To avoid this, you can easily add a constant to the top of your files before any other scripts right after opening PHP tag. By adding this constant, you have prevented direct access to your PHP files while allowing your files to be internally executed within WordPress environment. Please take a look at the code below.
Conduct Role Capability Checks
By default, WordPress has six pre-defined roles. It is Super Admin, Administrator, Editor, Author, Contributor, and Subscriber. This concept of roles is designed to give site owner the ability to control what users can or can't do on their site. This roles and capabilities can be added, removed or extended via plugin or scripts. You might want to read Roles and Capabilities from WordPress Codex for more information.
As a developer, it is our main responsibility to ensure that our plugin allows the site owner to retain the control over their site. We also need to make sure that none of the sensitive data leak to the public.
For this matter, WordPress provides us a handy function called current_user_can(). It can be used to check whether current logged in user has specific role or capability before it is run. Take a look at the example code below which will only run if currently logged in user has the role of Administrator, Editor, or Author.
Prevent SQL Injections
What Is SQL Injection?
SQL injection is a most common computer attack used to hack a website. It is one of few injection techniques commonly used by attackers to alter or even destroy your backend database. It is used by inserting malicious codes into unsafe entry fields to exploit security vulnerabilities in an application's software or website.
Let say, for example, you have created a plugin which has a database table named secret_members and a user input named nickname at the front-end. And visitors are required to set a nickname. Below is an example of unsafe code which is susceptible to SQL injection. Please take a look at the code below.
As a fair warning, never ever trust user input. You might be wondering why is this code unsafe. Well, let me explain. You might expect that user will insert their nickname perhaps 'John' or 'Sarah' in this user input. But there is a high possibility that attackers will insert, eg, john'; DROP TABLE users;-- in it. This will make you inevitably lose your users table. Please take a look at the image below.
How To Prevent SQL Injection?
You can prevent SQL injections by using a prepared statement. A prepared statement is a function used to execute same or similar SQL statements repeatedly. It works by creating and sending an SQL statement to the server first where the data is substituted by parameters or placeholders.
The database on the server will parse, compile, and perform the query without any data on it, and stores the result without executing it. Next, after the program binds data values to the parameters, the database will finally execute the statement.
The currently supported query parameters are %s (string), %d (integer) and %f (float). Any other % characters will probably cause parsing errors and will need to escape. Please take note that %d will only accept integers. If you need comma values, please use %f as float instead.
Aside from preventing SQL injection, a prepared statement also have other advantages. First, it will reduce parsing time because preparation of the query only needs to be done once even it can be executed repeatedly.
Second, it will reduce server bandwidth because you only need to send parameters each time and not the whole query. Now let's take a look at our prepared code below when it using a $wpdb->prepare();.
Prevent Cross-site Scripting (XSS)
What Is Cross-site Scripting?
Cross-site Scripting or commonly known as XSS is another type of injection attack. It is typically found in web applications. XSS attack occurs when attackers inject malicious scripts into unsafe legitimate web apps.
While SQL injection is a server-side attack which focuses on the database to modify or steal information from it. XSS is an attack geared towards end users to force victim's browser to run codes injected by attackers while loading the page. Let's take a look at the example of unsafe scripts below.
Generally, there are 3 types of XSS attack. It is categorized as Stored, Reflected, and DOM-based. Stored XSS (Persistent) usually occurs when user input is permanently saved on the target server.
For example, an attacker inserts malicious codes into forum messages, comments fields, user profiles, etc. Reflected XSS (Non-Persistent), by far is the most basic web vulnerability. It happens when the injected scripts are reflected off the web server.
Reflected XSS typically delivered to victims via emails or on some other websites; where the attacker sends an innocent-looking URL pointing to a legitimate website but containing the XSS vector.
As for DOM-based XSS, it occurs in the DOM (Document Object Model) instead of part of HTML. See the image 2 below for an example of how XSS attacks occur.
How To Prevent Cross-site Scripting?
There are 3 methods to prevent XSS attack. It is by validating and sanitizing user inputs as well as escaping the outputs. Depending on the situation, you may need to use one, two, or all 3 methods to ensure it is truly secure.
The first method is validation where you check user inputs to ensure the data you request from users matches with what they have submitted. Although input validation is not primary prevention method for XSS attacks, it still helps reduce the effect if in case attackers discover such vulnerabilities.
Next method is by sanitizing user inputs. Sanitization is a process of removing unneeded or unsafe data from user inputs. Depending on sanitization type, it can help by converting characters to entities or stripping all tags. It also can help to remove line breaks and extra white spaces as well as stripping octets and much more.
By sanitizing user inputs, you ensure the data you received is truly safe before saving it into a database.
And the last method is by escaping your outputs. I bet some of us will be wondering what is the need for securing outputs when the data comes directly from our database. Well, there is a term of FIEO (Filter Input Escape Output) which is commonly thought of in securing web apps.
Although we have validated and sanitized all our user inputs, there are still rooms for mistakes. By escaping your outputs, you will prevent characters such as < and > from being rendered in a malicious way, which will cause harm to your website and users. Let's take a look at our code which has been escaped to protect from XSS attack.
Prevent Cross-site Request Forgery (CSRF)
What Is Cross-site Request Forgery?
Cross-site Request Forgery or commonly abbreviated as CSRF is an attack that trick end users to execute a malicious request on an authenticated website. It also known as a one-click attack and sometimes pronounced as sea-surf. CSRF typically occur when logged-in users click on a seemingly harmless link which could be injected via XSS.
And by clicking on this link, users unknowingly changing their account info, transferring money or forwarding email to the attackers. This is few examples of CSRF which seems common but can be seriously exploited.
How To Prevent Cross-site Request Forgery?
To prevent CSRF attack we need to use a nonce. A nonce (number used once) which is act as a unique identifier; is used to verify each request.
This is to ensure that the action being made is requested by an authenticated user rather than an attacker. WordPress nonce isn't just typical numbers but it is hash made up from the combination of letters and numbers. It's not only can be used once but it also has limited "lifetime" which will expire after a certain time.
So, whenever anyone attempts to make a request without having a valid nonce, WordPress will send a 403 Forbidden response with the error message: "Are you sure you want to do this?"
WordPress has provided us few functions which make nonce implementation becomes relatively easy. You can easily create a nonce and add it to URL query string, add it in the form hidden field or some other way.
There are WordPress functions called wp_nonce_url() where you can add a nonce token to the URL as well as wp_nonce_field() where you can add a nonce to a form field. By default, a nonce has a lifetime of one day but you can change it by adding a filter.
Let's take a look at the example code of how to add a nonce below as well as the URL generated with a nonce.
There are no websites that are guaranteed 100% safe from attackers. But with the implementation of 5 basic security practices above will help and make websites more secure. These solutions are simple but always overlooked by developers. Let us start this implementation as a habit and together we create a safer website.
He is a web developer, a programmer, and a computer technician. He obsessed with coding and enjoy learning new things. In his spare time, he likes to play online games, musical instruments or watching anime and movies.