Why is_admin() is totally unsafe for your Wordpress development
Tags: wordpress, php, security, vulnerability
Published: 2019-04-25 20:42:14

---
title: Why is_admin() is totally unsafe for your Wordpress development
published: true
description: The is_admin() Wordpress function is not enough and potentially unsafe when developing Wordpress plugin/themes. Let's find out why.
tags: wordpress, php, security, vulnerability
cover_image: https://thepracticaldev.s3.amazonaws.com/i/o9cv6k6pe55hz9ddsxfv.jpg
---

When developing a Wordpress plugin/theme, [`is_admin()`](https://codex.wordpress.org/Function_Reference/is_admin) is probably one of the most used functions to check whether the current code is running under the admin section. Unfortunately, most developers tend to believe that this is enough to make sure THAT code is run by an user who HAS privileges AND the right access. Of course, this is all wrong.

##The door to Hell
On 21 March, Wordfence Security Team [disclosed two vulnerabilities](https://www.wordfence.com/blog/2019/03/recent-social-warfare-vulnerability-allowed-remote-code-execution/) in Social Warfare, a very popular plugin in the WordPress ecosystem. One was a Stored Cross-site Scripting Attack (XSS) and the other a remote code execution (RCE), both tracked by CVE-2019-9978.

Both vulnerabilities has something in common: the misuse of the [`is_admin()`](https://codex.wordpress.org/Function_Reference/is_admin) function in WordPress. Let's see why:
```
<?php
/**
 * Migrates options from $_GET['swp_url'] to the current site.
 *
 * @since 3.4.2
 */
if ( true == SWP_Utility::debug('load_options') ) {
    if (!is_admin()) { // THE DOOR TO HELL
        wp_die('You do not have authorization to view this page.');
    }
    $options = file_get_contents($_GET['swp_url'] . '?swp_debug=get_user_options');
```
*BLA BLA code*
```
<?php
$options = str_replace('<pre>', '', $options);
$cutoff = strpos($options, '</pre>');
$options = substr($options, 0, $cutoff);
$array = 'return ' . $options . ';';
try {
    $fetched_options = eval( $array ); // WELCOME TO HELL
}

```
Those above are snippets from the Social Warfare plugin. The code reported has the intent to parse a remote text document into a key->value array of options for the plugin to use. However, instead of using a typical data storage format like a JSON, the plugin generated options as an actual block of PHP code, which is crunched by `eval()` into an array.

Eventually, the Wordfence guys explain this all fair well:
>When a malicious injection matches the format used by the plugin normally, as in the attack campaigns running currently, an XSS payload can be injected into one or more of those settings. However, the contents of `<pre>` tags in the remote file are arbitrarily passed to `eval()`, meaning the code within is executed directly as PHP.

So, enough for now. Let's get back to the basics.

## is_admin() purpose
As mentioned in the [Wordpress Codex](https://codex.wordpress.org/Function_Reference/is_admin):
>is_admin() is not intended to be used for security checks. It will return true whenever the current URL is for a page on the admin side of WordPress. It does not check if the user is logged in, nor if the user even has access to the page being requested. It is a convenience function for plugins and themes to use for various purposes, but it is not suitable for validating secured requests.

Does this mean i should not ever use [`is_admin()`](https://codex.wordpress.org/Function_Reference/is_admin)? Nope. At least, not only.

This function is crucial when writing code that is needed only in the Wordpress backend admin area, but it should always be the first step to do. After that, eveything you write must be encapsulated under a security check layer which basically takes into account 2 things: User session and User capabilities. We can then  determine a simple progressive security path like the following:
1. Is the user viewing an admin section?
2. If yes, is the user really logged in (valid session)?
3. If yes, has the current logged in user the capability to view this section?

So, before going further, let me tell you a couple of things.

## User session
Wordpress is an advanced system which takes security very seriously. When developing within its infrastructure, we have all the necessary tools to write good and secure code.

[`is_user_logged_in()`](https://developer.wordpress.org/reference/functions/is_user_logged_in/) is the first function you always need to use when developing under the admin area. It just does what its name is: checks if the user is logged or not. Although this might seem a simple task, when used along with [`is_admin()`](https://codex.wordpress.org/Function_Reference/is_admin), grants a first layer of security based on sessions, [nonce tokens](https://codex.wordpress.org/WordPress_Nonces) and so on.

## User capabilities in Wordpress

Let's blockquote the [Wordpress Codex](https://codex.wordpress.org/Roles_and_Capabilities) again:
>WordPress uses a concept of Roles, designed to give the site owner the ability to control what users can and cannot do within the site. A site owner can manage the user access to such tasks as writing and editing posts, creating Pages, defining links, creating categories, moderating comments, managing plugins, managing themes, and managing other users, by assigning a specific role to each of the users.

Infact, not every part of the admin area should be available to all logged-in users. At the same time, you might want not only admins to get access to that part, so there an handy tool Wordpress puts on the table: **the magic** [`user_can( $user, $capability )`](https://codex.wordpress.org/Function_Reference/user_can)

This function takes the User ID as first parameter and a specific 'capability' as second. If the current logged-in user has this one, the function returns true.

```
<?php

if ( ! is_admin() ) {
    wp_die( 'This code is for admin area only' );
}

if ( ! is_user_logged_in() ) {
    wp_die( 'You shall not pass!' );
}

if ( ! user_can( $id_user, 'my_capability' ) {
    wp_die( 'Yes, you too... shall not pass.' );
}

// Do your stuff
```

Wordpress has a long list of [user roles and capabilities](https://codex.wordpress.org/Roles_and_Capabilities), so it's just up to you to pick up the right choice.

The use of these three functions all together is a good start in writing secure code that, at least, can prevent most of the common vulnerabilities that may affect Wordpress plugins or themes.

**Coding pill**: *Paranoia is Security's best friend*

---

Found a typo? [Make a PR](https://github.com/lucagrandicelli/blog/tree/master/wordpress/02-why-is_admin()-is-totally-unsafe-for-you-wordpress-development)

*Cover image by* [Alena Aenami](https://www.artstation.com/aenamiart)