The security documentation provided by WordPress and found online for plugin security is sparse, outdated or unclear. This cheat sheet is intended for Penetration Testers who audit WordPress plugins or developers who wish to audit their own WordPress plugins. This cheat sheet can be effectively used to test various WordPress plugins.
Cross-Site Scripting (XSS)
Check if the following global PHP variables are echo'd to pages, or stored in the database and echo'd at a later time without first being sanitised or output encoded.
$_GET
$_POST
$_REQUEST
$_SERVER['REQUEST_URI']
$_SERVER['PHP_SELF']
$_SERVER['HTTP_REFERER']
$_COOKIE
(Note: the list of sources above is not extensive nor complete)
Cross-Site Scripting (XSS) Tips
Unsafe API functionsThe following functions can cause XSS if not secured:
add_query_arg()
remove_query_arg()
See References Below:
DISALLOWUNFILTEREDHTMLWhen doing dynamic testing for XSS the following setting in the wp-config.php file may reduce false positive results as it prevents administrative and editor users from being able to embed/execute JavaScript/HTML, which by default they are permitted to do.
define( 'DISALLOW_UNFILTERED_HTML', true );
SQL Injection
Unsafe API methods (require sanitising/escaping):
$wpdb->query()
$wpdb->get_var()
$wpdb->get_row()
$wpdb->get_col()
$wpdb->get_results()
$wpdb->replace()
Safe API methods (according to WordPress):
$wpdb->insert()
$wpdb->update()
$wpdb->delete()
Safe code, prepared statement:
<?php $sql = $wpdb->prepare( 'query' , value_parameter[, value_parameter ... ] ); ?>
Note: Before WordPress 3.5 $wpdb->prepare
could be used insecurely as you could just pass the query without using placeholders, like in the following example:
$wpdb->query( $wpdb->prepare( "INSERT INTO table (user, pass) VALUES ('$user', '$pass')" ) );
SQL Injection Tips
Unsafe escaping ('securing') API methods:
esc_sql()
function does not adequately protect against SQL Injection - see refs belowescape()
same as aboveesc_like()
same as abovelike_escape()
same as above
<?php $wpdb->show_errors(); ?> <?php $wpdb->hide_errors(); ?> <?php $wpdb->print_error(); ?>
File Inclusion
include()
require()
include_once()
require_once()
PHP Object Injection
unserialize()
Command Execution
system()
exec()
passthru()
shell_exec()
PHP Code Execution
eval()
assert()
preg_replace()
dangerous "e" flag deprecated since PHP >= 5.5.0 and removed in PHP >= 7.0.0.
Authorisation
is_admin()
does not check if the user is authenticated as administrator, only checks if page displayed is in the admin section, can lead to auth bypass if misused.is_user_admin()
same as abovecurrent_user_can()
used for checking authorisation. This is what should be used to check authorisation.
Open Redirect
wp_redirect()
function can be used to redirect to user supplied URLs. If user input is not sanitised or validated this could lead to Open Redirect vulnerabilities.
Cross-Site Request Forgery (CSRF)
wp_nonce_field()
adds CSRF token to formswp_nonce_url()
adds CSRF token to URLwp_verify_nonce()
checks the CSRF token validity server sidecheck_admin_referer()
checks the CSRF token validity server side and came from admin screen
SSL/TLS
CURLOPT_SSL_VERIFYHOST
if set to 0 then does not check name in host certificateCURLOPT_SSL_VERIFYPEER
if set to FALSE then does not check if the certificate (inc chain), is trusted- Check if HTTP is used to communicate with backend servers or APIs. A grep for "http://" should be sufficient.
Further reading/references:
- https://developer.wordpress.org/plugins/security/
- https://codex.wordpress.org/FunctionReference/escsql
- https://blog.sucuri.net/2015/04/security-advisory-xss-vulnerability-affecting-multiple-wordpress-plugins.html
- https://secure.wphackedhelp.com/fixmysite.html
- https://curl.haxx.se/libcurl/c/CURLOPTSSLVERIFYHOST.html
- https://www.owasp.org/index.php/OWASPWordpressSecurityImplementationGuideline
- http://php.net/manual/en/function.preg-replace.php