Half-Elf on Tech

Thoughts From a Professional Lesbian

Category: How It Works

  • Access: Denied or Granted?

    Access: Denied or Granted?

    One of the topics I discuss regularly with developers it that of access. When writing any code, we must always consider who should have access to it. Not in the ‘who can look at my code?’ aspect, but that of who can run the code.

    This happens a lot with plugins and themes when people create their own settings pages. While I’m the first to admit that the settings API in WordPress is a bag of wet hair that makes little logical sense, using it gives you access to a lot of built in security settings, such as nonces.

    But as I often tell people, a nonce is not bulletproof security. We cannot rely on nonces for authorization purposes.

    What is a Nonce?

    A nonce is a word or expression coined for or used on one occasion. But in security, a nonce is an arbitrary number or phrase that may only be used once. Due to it’s similarity to a nonce word, it reuses the name. In WordPress, it’s a pseudo-random number created on the click of (say) a save button, to ensure that someone had to press the button in order to run the function.

    For example. If your settings page has a nonce, then the nonce is generated on click and passed to the function(s) that validate and sanitize and save. If the nonce doesn’t check out, then WordPress knows someone didn’t press a button, and it won’t run the save. This prevents someone from just sending raw data at your code.

    Why isn’t that enough?

    With just a nonce, anyone who has access to the page can save data. This is okay when you think about something like a comment form or a contact form. Those are things that need to be accessible by non logged in users, right? What about the WordPress General settings page? The one that lets you change the URL of the website? Right. You only want that to be accessible to admins. Imagine if all logged in users could change that one. Yikes!

    In order to protect those pages, you have to consider who should have access to your code.

    1. Are the users logged in or out?
    2. What user permissions should be required?
    3. How much damage can be caused if the data is changed?

    That’s it. That’s all you have to ask. If it’s a contact form, then a nonce is all you need. If it’s a site definition change, then you may want to restrict it to admins or editors only.

    The Settings API

    When you use the settings API, some of this is made pretty straightforward for you:

    add_action( 'admin_menu', 'my_plugin_admin_menu' );
    function my_plugin_admin_menu() {
        add_options_page( 'My Plugin', 'My Plugin', 'manage_options', 'my-plugin', 'my_options_page' );
    }
    

    In that example, the third value for the options page is manage_options which means anyone who can manage site options (i.e. administrators) can access the settings page.

    The problem you run into is if you want a page to do double duty. What if you want everyone to see a page, but only admins can access the settings part? That’s when you need to use current_user_can() to wrap around your code. Just check if a user can do a thing, and then let them in. Or not.

    What Permissions are Best?

    In general, you should give admins access to everything, and after that, give people as little access as possible. While it may be annoying that an editor can’t, say, flush the cache, do they really need to? You have to measure the computational expense of what’s happening before you give access to anyone. It’s not just “Who should do a thing?” but “What are the consequences of the thing?”

    Look at the cache again. Flushing a cache, emptying it, is easy. But it takes time and server energy to rebuild. By deleting it, you force your server to rebuild everything, and that will slow your site down. An admin, who has access to delete plugins and themes, should be aware of that. An editor, who edits posts and menus, may not. And while some editors might, will all?

    This gets harder when you write your code for public use. You have to make hard decisions to protect the majority of users.

    Be as prudent as possible. Restrict as much as possible. It’s safer.

  • On Uninstalling WordPress Plugins (and Data)

    On Uninstalling WordPress Plugins (and Data)

    Someone asked me why WordPress always says it’s going to delete files and data when it only removes the files and not the database options. There are two parts of the answer to this, one being a little historical and the other being a bit unhelpful.

    The Message: Delete Files and Data

    Once upon a time, when you uninstalled a WordPress plugin, it looked something like this:

    The Delete Plugin Screen

    That was a very simple screen. You were asked to delete the plugin files, you clicked that you were, done.

    Now you see this thanks to Shiny Updates:

    Shiny Updates - Delete Hello Dolly

    It’s a different message, telling you it’s deleting data!

    What Is The Data?

    The part you don’t see is that WordPress would also remove all the data as well as those files.

    Any time WordPress showed you that message to delete files and data, it was saying that it found a file called uninstall.php which would, presumably, delete the data set by the plugin. By this I mean the options and settings you chose for your plugin. Some plugins have data and others don’t. For example, Hello Dolly has no data, just files. It doesn’t need an uninstall file. On the other hand, a plugin like Jetpack has a lot of settings it should remove from the database on cleanup.

    Why Do We See ‘and Data’ If There’s None?

    Okay, so if Hello Dolly has no data to delete, why did we see that message? In part, this stems from the following idea:

    Delete plugin files AND data

    We wanted it to be more clear as to what was being deleted when you delete, and that was part of a proposed change to WordPress core to tell you if and when database settings are removed on uninstall, and let you leave it alone if needed. Wouldn’t that be nice? Letting you pick which way to go?

    Well. There’s a problem with that dream, and the name of the problem is “Plugin Frameworks.” No, not the CMB2 stuff, I mean the boilerplate plugin frameworks that are oh so popular.

    I hate them. Because they always have an uninstall, and most of the time people leave it alone. That’s right, your brilliant boilerplate will flag an alert that it’s deleting data when it’s not. This doesn’t impact the functionality of the base idea, but it does change the message.

    So Why Does It Say Data?

    Because when you uninstall a plugin, if it was well written, it removes the files and the data.

  • Mailbag: When Do I Use “If Exists”?

    Mailbag: When Do I Use “If Exists”?

    Someone pinged me on slack about this. Bear in mind, I tell people “If you got an email from plugins, please press reply and email back, don’t slack me.” because it allows me to keep track of the whole history of a conversation, but also because if I’m sick, someone else can reply to you and you’ll wait less. While, in this case, the answer should have been very short, it grew to be a lot longer than he’d expected because he didn’t quite understand what I meant.

    Unique Prefixes

    One of the requirements of the Plugin Directory is that all plugins have unique function names, defines, and classnames. This is for a pretty obvious reason: it will prevent your plugin from conflicting with other plugins or themes.

    • For plugins, use a function or classname that matches the plugin name: class ThisPluginName{} or function ThisPluginName_FUNCTION()
    • For specific specific sites, use a function class for the domain: class HalfElfOrg{} or class HELF{} or function helf_FUNCTION

    This extends to all things, including defines, namespaces, enqueue’d handles, and on and on. As I tell people, “A namespace or class of MyPlugin is not actually all that unique…” Defines are extra tricky since sometimes you want to define them and allow them to be replaced. But more on that in a minute. No matter what, this is bad: define( 'PLUGIN_PATH', plugins_url( __FILE__ ) );

    Since it’s 2016, I have to tell people that they shouldn’t use two letter slugs anymore as all the good ones are taken. I also tell them not to use wp_ or __ as a prefix.

    And then I say this:

    If those are intended to be in shared libraries, please detect IF the code is already included and not re-include it, as doing so will cause conflicts if two people call the same defines and functions.

    Yeah. It’s messy.

    If Define Exists…

    This is used a lot. Let’s say you know you need the plugin basename (the folder name) in multiple sub folders of you plugin, and it’s a pain in the ass to do plugins_urls( 'foo.css' , dirname( dirname( __FILE__ )) ); to go up two folders. That’s a case where a global define starts to make sense and it’s a case where rarely would you want to bother checking if it exists.

    The code is just this: define( 'MY_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );

    But what if you want to have a default value for an API Key and allow people to override it? Then you can one of these:

    // Option 1:
    if ( ! defined( 'MY_PLUGIN_APIKEY' ) )
        define( 'MY_PLUGIN_APIKEY', 'default key value' );
    
    // Option 2:
    defined('MY_PLUGIN_APIKEY') or define('MY_PLUGIN_APIKEY', 'default key value' );
    

    What those do is check “Does the define already exist?” for you.

    Now keep in mind, I wouldn’t do that. I’d do this:

    $apikey = ( defined('MY_PLUGIN_APIKEY') ) ? MY_PLUGIN_APIKEY : 'default key value' ;

    The best example of this in core WP is how WP_DEBUG is handled. If you delete that from your wp-config.php file, it doesn’t turn on debugging. The code checks for the existence and if it’s not there, it assumes no. For the most part, if you name your defines properly, there’s no need to allow them to be overwritten because you’re not defining them in advance. But in case you do, this code is a wise idea.

    If Function (or Class) Exists …

    Lately I’ve seen a lot of people do this:

    if ( ! function_exists( 'my_awesome_function' ) ) {
    	function my_awesome_function() {
    		//function stuff
    	}
    }
    

    Okay look. This sounds like a great idea until you realize the fatal flaw. What happens when (not if, when) someone else has a function with the same name and their plugin or theme loads first?

    As Lilu Dallas would say “Big badda boom.”

    This is a terrible idea unless we’re talking about shared libraries.

    Use if-checks With Shared Libraries

    That’s my answer here folks. The only time you should be checking if-exists is WHEN you are using a shared library. If your plugin includes a common PHP library, check for it before you include it.

    It’s a little different with Javascript and CSS though. Let’s say you’ve got Bootstrap. This is a commonly used library. A lot of plugins and themes have it, and you’re a good person. You want to make sure you only load it one time no matter how many people include it in their plugins and themes. In this moment, you have to do something paradoxically brilliant.

    Don’t use a unique name for your enqueues.

    function mypluginname_resources() {
        wp_enqueue_style( 'bootstrap', plugins_url( 'css/bootstrap.min.css', __FILE__ ), array(), VERSION );
        wp_enqueue_script( 'bootstrap', plugins_url( 'css/js', __FILE__ ), array(), VERSION );
    }
    add_action('wp_enqueue_scripts', 'mypluginname_resources');
    

    The magic here is specific to how WordPress handles enqueues. It knows “There’s already something enqueued as bootstrap! I don’t need this!” and you’ve sped up sites. Of course there’s a risk here when someone might have an older version loaded first. As far as I know, the enqueue system isn’t clever enough to detect what version is included, and use the newest one (I wish it was), so you may have to do some convoluted checks. Also making this worse is that people don’t name things properly. I’ve see people using bootstrap-js and bootstrap-css for the enqueue names, which is totally unnecessary. WordPress will handle that for you.

    Remember: When in doubt, if coding for WordPress do it the WordPress way.

  • PHP Ternary Operations

    PHP Ternary Operations

    I mentioned a few days ago that I finally got to use ternary operations in my code and I was excited.

    I have a tendency to write my code out long form, which stems from how I actually sit down and think about my code. For example, when I need to check if specific parameters are set, I write it out like this:

    If variable X is equal to FOO, then do BLAH. Else do DEFAULT!

    When I translate that to PHP code, it becomes this:

    if ( $variable == 'FOO' ) {
        BLAH;
    } else {
        DEFAULT;
    }
    

    There’s nothing particularly wrong with it, but it can get a little long. And then consider code like this:

    if($_POST['variable'] == 'no' || $_POST['variable'] == 'yes'){              
        $variable = $_POST['variable'] ;
    }
    

    That has two issues. The first is that I’m trusting my data a little more than I should and not sanitizing when I save the variable. The second is I’m not validating smartly. If I want to be smarter, I do this:

    if( $_POST['variable'] == 'yes'){              
        $variable = 'yes ;
    } else {
        $variable = 'no';
    }
    

    At this point, I’m no longer allowing my code to be anything but yes or no, and if someone puts in ‘maybe’ as a value for the POST data, that’s nice but I’m forcing no.

    Another alternate way is this:

    $variable = 'no';
    if( $_POST['variable'] == 'yes'){              
        $variable = 'yes ;
    }
    

    There I default to ‘no’ and only pick ‘yes’ if it’s set to yes. This code is somewhat better because it only has one check and it’s obvious what my default is.

    But we can be even smarter! The trick is that I have to rethink how I’m checking for things. If all I care about is if a comparison is true or false, and I want to save a specific value to a variable based on that, then I can do my code all in one line:

    $variable = ( $_POST['variable'] == 'yes' )? 'yes' : 'no';
    

    This is the magic of the ternary operators. With it I get one line that checks for a specific result and defaults when that’s not it. You can make it more complicated, as long as the result is true/false. Like here:

    $schema = ( is_ssl() || get_setting( 'plugin-force-ssl' ) )? 'https' : 'http';
    

    Obviously they won’t work in all situations, and there are some cases where the two possibilities are going to make the code harder to read, but they can be downright nifty if you do them right.

  • Can AMP and SVGs Be Friends?

    Can AMP and SVGs Be Friends?

    Spoiler: Not at the moment, no.

    Warning: Fix errors on your AMP pages

    On January first, I got a scary email with that subject:

    Google systems have detected that some of your AMP pages do not meet our guidelines and will therefore not show in Google Search AMP-related features.

    Yikes! I ran over to Google Webmaster Tools and saw I had well over 400 pages with critical issues. Happy New Year to me, right? Looking at them, most of the problems were exactly the same:

    • The tag ‘pgfref’ is disallowed.
    • The tag ‘pgf’ is disallowed.
    • The attribute ‘i:extraneous’ may not appear in tag ‘g’.
    • The attribute ‘xmlns:x’ may not appear in tag ‘svg’.
    • The tag ‘switch’ is disallowed.

    Right away, I knew this was from my SVGs. On this site, I use SVGs instead of PNGs because they look crisper on retina screens and they can be colored on the fly. Basically I really like the power of SVGs. And, as far as I could tell, SVGs worked on AMP pages. I read the details on SVGs in AMP/HTML and it’s listed there, after all.

    But in reading the errors and looking at my SVGs in a text editor, I quickly saw what was wrong.

    Most SVGs have Extra Data

    I use the Block Bundle from Symbolicons for my SVGs. They’re awesome, but once in a while I have to edit them and clean up some extraneous data within. Adobe Illustrator, for example, puts in a lot of junk at the top of my SVGs:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    

    But in the case of the SVG having a problem, it had … well. This:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN">
    <svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
    	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 28 28"
    	 enable-background="new 0 0 28 28" xml:space="preserve">
    <switch>
    	<foreignObject requiredExtensions="&ns_ai;" x="0" y="0" width="1" height="1">
    		<i:pgfRef  xlink:href="#adobe_illustrator_pgf">
    		</i:pgfRef>
    	</foreignObject>
    	<g i:extraneous="self">
    		<g>
    			<defs>
    				<rect id="SVGID_1_" width="28" height="28"/>
    			</defs>
    			<clipPath id="SVGID_2_">
    				<use xlink:href="#SVGID_1_"  overflow="visible"/>
    			</clipPath>
    			<path clip-path="url(#SVGID_2_)" fill="#231F20" d="M24.286,20h-6.757c-1.229,0-2.259,0.69-2.568,1.615
    				[...]
    				C26,20.651,25.234,20,24.286,20"/>
    			<path clip-path="url(#SVGID_2_)" fill="#231F20" d="M27,16H1c-0.553,0-1,0.447-1,1c0,0.553,0.447,1,1,1h26c0.553,0,1-0.447,1-1
    				C28,16.447,27.553,16,27,16"/>
    			<path clip-path="url(#SVGID_2_)" fill="#231F20" d="M21.699,2.046c-0.384-1.711-1.927-2.513-3.427-1.781l-1.544,0.752
    				c-1.5,0.732-3.956,0.732-5.456,0L9.728,0.265c-1.5-0.732-3.042,0.07-3.427,1.781L4.29,10h19.42L21.699,2.046z"/>
    			<polygon clip-path="url(#SVGID_2_)" fill="#231F20" points="23.935,11 4.066,11 3.167,15 24.833,15 			"/>
    		</g>
    	</g>
    </switch>
    <i:pgf  id="adobe_illustrator_pgf">
    	<![CDATA[
    		eJzVfelCMsuu6H0B3gFUBGTqidGZyQkVxXlCkFb5RMAG1trf+XGf/aaq56G6qxs859y99nJBd5FUpVKpJJVUopFWO73fH/fENJ9hwqFotCqJ3dlYKofx0/DRcDifziT0KH6ZCLOFDAON9o+KHaXhjShNB+NRGb1i8csG+nX8eCz9DV92J59dcZgIxxPw4mowG4rwajr5m5n03xMqNvh5rTuDN3y2mGWFcL4slMKtU/S6O/qnO50O/[...]==	
    	]]>	
    	<![CDATA[	
    		k3D32Uw5IYV5ef6oHOpoLUjz3J9shIS0J71kk7fOSM8qidXrybCJkYaiFrTT1Zd8hYT0cOeiMLpxRiok7pKbb+enTkhD0elK40XQ0VqQMo3KyR4BaX51bSQltglI71+YxvtZGyMFHrOMdeUgsrXOHneuHJEeHHJXRPJyDztXrxgpsH2vriNF62Vzeis9zXeaCG3CPqvxe/5xyCYAqTC2stJzk1HJ24rFLEhzue/OBCMNRS1oEdKO9Pw5uiIg3evmC4011hHpy+7rpRPSUBTP6hof33xyGqskbUWep6uf4qUz0otqYudn7bvphDS5OSzvYKRIWlrHml9d/Zk/[...]==	
    	]]>	
    	<![CDATA[	
    		dcrskeHXSl9Zs0fmmNYjE7Jee2ZNHFkrryzktsCJTQbLwl96jj3t62SdzndBkZ6zVmZj/odmsSu/jxf2yKA0NKvjIcB9Ssf+PTJOGakoDW1RjwzOzrPccReQMDna5Bz1vnFCsMsxMT3HV3KOdspTbs2seai711GPaBoaZRl0udXFcxJPKCIZtFubPKyWycki94aZd+TyRXzxoTmE7OkeRUoOLV9EqEIXTPxpuxMSzTlNMLBnXp0xaNZ6N5SPvDofsesEKwkRhiqFxDMWA9GmQMXJlGHrcYdz1Lb3fX10Z2UU+XA09/[...]++foLQ
    	]]>
    </i:pgf>
    </svg>
    

    I’ve truncated the code because it’s insanely long. In fact, it’s so long that with all of it, the file is 25k larger!

    Let me explain. The SVG there has ‘switch’ code. The switch code is awesome because it can change what’s displayed based on what the file detects. For example if I wanted to make different text show based on the detected system language, I would do this:

    <switch>
        <g systemLanguage="en-UK">
            <text x="10" y="20">I say, bravo!</text>
        </g>
        <g systemLanguage="en">
            <text x="10" y="20">Arright!</text>
        </g>
    </switch>    
    

    The problem is that AMP? Doesn’t like that.

    AMP Doesn’t Trust SVGs

    AMP really doesn’t like fancy things. This makes sense. It’s meant to be faster for mobile browsers, so it’s streamlined and simplified. Fine. It wants simple SVGs. Obviously one fix here is I could clean up all my SVGs. The other one would be to do what AMP suggests and that is to whitelist attributes. Except you can’t do it. Yet.

    There is a reason for this, and it’s similar to the same reason WordPress doesn’t (yet) allow SVGs to be uploaded by default. Unlike images, SVG files contain code. An user who can upload an SVG has the ability to upload code files which can be executed in the victim’s browser. Picture this. An attacker uploads an image to a forum. If the forum links to the image with <img> then the end-user’s browsers load the SVG and run the code. Yaaaaaay.

    SVGs are dangerous. And to help this, AMP says SVGs with special tags are not welcome.

    The Fix?

    Edit your SVGs or, if you’re me, ditch the images all together. I decided to go with the ditching since I wanted the pages to load fast. It’s the point of AMP after all.

  • EasyApache 4, PHP, and WP-CLI

    EasyApache 4, PHP, and WP-CLI

    Way back in March, I upgraded my server to use EasyApache 4. I did this because I wanted to have multiple versions of PHP on my server and be able to segregate it by domain. I also wanted to be able to upgrade PHP without having to rebuild Apache.

    For the most part, it’s been great. There were a couple odd snafus, like when it accidentally installed the wrong wp-cli on a minor upgrade for me, but it magically fixed it.

    PHP Breaks WP-CLI

    The problem was when I ran wp-cli commands, I got this error:

    PHP Warning:  array_slice() expects parameter 1 to be array, null given in phar:///usr/local/bin/wp/php/WP_CLI/Runner.php on line 610
    PHP Warning:  Invalid argument supplied for foreach() in phar:///usr/local/bin/wp/php/WP_CLI/Configurator.php on line 132
    PHP Warning:  proc_open(): Descriptor item must be either an array or a File-Handle in phar:///usr/local/bin/wp/php/commands/help.php on line 111
    PHP Warning:  proc_close() expects parameter 1 to be resource, boolean given in phar:///usr/local/bin/wp/php/commands/help.php on line 111
    Content-type: text/html; charset=UTF-8
    

    Scary! I tried reinstalling and that didn’t work

    I took a note of what version of PHP I had running on command line and so something weird:

    PHP 7.0.10 (cgi-fcgi) (built: Aug 22 2016 20:34:53) Copyright (c) 1997-2016 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies with Zend OPcache v7.0.10, Copyright (c) 1999-2016, by Zend Technologies

    The Wrong PHP

    PHP 7.0.10 (cgi-fcgi) sounds like it should be okay, except I knew two thing:

    1. Fast CGI (fcgi) isn’t supported on EA 4 yet. It’s not even supported today. They’re working on it but it shouldn’t have been a thing installed.
    2. That should be cli not cgi-fcgi!

    I pinged a friend at cPanel who said “Oh no!” and promised to look into it for me. The next day I got an email that there was a minor PHP update to my server last night. I knew that meant PHP had been rebuilt. When I went to look, wp-cli was working again.

    Ergo – Something was wrong in PHP 7.0.10. It pushed the wrong version of PHP for command line somehow.

    Risk vs Reward

    Obviously this is a risk, having a server auto-update itself. It could install a rogue package or someone could typo or worse. At the same time, having PHP apply it’s own security patches for me means I don’t have to worry that I’m in Japan for a week and forget to do something. The risk, knowing that EA will roll out a fix for me, is less than the reward of being secure.