Two-Step Authentication

At WordPress.com VIP, we require that you enable two-step authentication for your WordPress account if you are an administrator, have SVN access, or have publishing capabilities.

Keeping your sites secure is one of our top priorities. Passwords are the de-facto standard for logging in on the web, but they’re relatively easy to break. Even if you create strong passwords and change them regularly, a data breach can leak them. Two-step authentication is a method of securing accounts that requires you not only know something (a password) to login, but that you also possess something (a mobile device).

The benefit of this approach to security is that even if someone guesses or obtains your password, they need to have also stolen your mobile device in order to break into your account. One of the ways bad actors attempt to compromise sites is to use the credentials of privileged users that may have had passwords leaked as part of a hack on another service. Two-step authentication makes it dramatically more difficult for accounts to be compromised.

Please follow these instructions to enable two factor authentication on your account, either via an authenticator app or via SMS, for their WordPress.com account.

Some users have asked about options for two factor authentication without the use of a mobile device. Authy offers desktop applications that could be used in conjunction with our support for using an authenticator app. You may also be able to set up SMS delivery of two factor codes via VOIP services like Google Voice or Skype, though delivery may not be reliable in all areas and should be tested thoroughly before relying on it. Using this approach is less secure than having a true second-factor like a mobile device, and should be avoided if possible.

SSL for VIP Go Sites

A VIP Go site must have an SSL certificate installed in order to be active. Because every site uses a custom domain for both the front-end and admin area, and because we want each site to have a secure admin area and login process at minimum, SSL is a requirement.

Note that our SSL implementation is SNI based, which means some legacy browsers will not be fully supported in their access to pages served over SSL.

Setup Process

By default, the VIP team will handle the procurement, installation and renewal of SSL certificates for all VIP Go sites, beginning with the initial site setup process.

If you would like to provide your own SSL certificate, please note this during the initial site planning conversations, and open a support ticket noting this during the site setup process. The steps from there include:

  • The VIP team will provide you with a CSR to use in obtaining a certificate.
  • You can obtain the certificate from a certificate authority of your choosing. The certificate needs to include both “www” and the root version of a hostname, so a SAN or wildcard certificate is probably best.
  • Deliver the certificate to us via support ticket. If you want to also provide a private key, please contact us for notes on how to do this securely; please do not attach a private key to a support ticket or regular email message.
  • The VIP team will install the SSL certificate and confirm that it is working as expected.

 

SSL for VIP Sites

As a part of our overall efforts to encrypt sites hosted on WordPress.com, we strongly encourage WordPress.com VIP sites to enable SSL/HTTPS for a custom domain name mapped to your site, and to redirect all site traffic to that secure URL.

By default all domains on WordPress.com have SSL enabled. We have temporarily excluded VIP sites from this requirement to allow time to update themes and site assets so they are SSL-compatible. We’re aware that some third-party ad networks do not yet support SSL for displaying ad content. If your ad networks don’t already support SSL, we hope you will strongly encourage them to do so quickly.

Options for SSL Support

For VIP sites ready enable HTTPS support, we can provide a few options:

  • Secure (default): HTTPS-only. Valid SSL certificate installed, all HTTP traffic redirected to HTTPS.
  • Testing: Valid SSL certificate installed, HTTP traffic NOT redirected to HTTPS. This mode is recommended only for testing and resolving mixed-content issues and is not recommended as a long-term solution.
  • Insecure: Valid SSL certificate installed, HTTPS traffic redirected to HTTP. Not recommended, but can be implemented as a short-term workaround for any issues that might come up in testing.

Important Notes

A few notes about how SSL encryption on WordPress.com VIP works:

  • Our SSL implementation is SNI based, which means some legacy browsers will not be fully supported in their access to pages served over SSL. Consider this when evaluating whether or not to have us globally redirect all requests to HTTPS URLs, and/or in setting the canonical URL to the HTTPS version of pages/posts on your site.

Setup Process

When you are ready to enable HTTPS support for your WordPress.com VIP site using one of the above options, please open a support ticket and include the following information:

  • The domain name(s) for which you want to add HTTPS support.
  • If you want to purchase your own SSL certificate, a request that we generate a CSR.
  • Which support option described above you want to use (Secure, Testing or Insecure).
  • Optionally for the “Secure” option and where you are providing the certificate, a request to use HSTS headers as a part of the redirect. Note that if these are enabled and you later disable HTTPS support, users may not be able to access your site. (HSTS is enabled for all sites in “Secure” mode using using Let’s Encrypt certificates.)

User Security Best Practices

We encourage all users on VIP sites to follow best practices when it comes to securing their devices, accounts and access to VIP tools. Two-factor authentication is required for all users with the ability to publish to a VIP site and we also recommend following at least these basic steps:

  1. Set a login password for all user accounts on your computer.
  2. Set a complex (more than 4 character) passcode to unlock your mobile devices. Do not use fingerprints or patterns.
  3. Enable a screen saver that activates after a short period of time and requires a password to turn off.
  4. Use only strong passwords. Never use the same password in more than one place.
  5. Use a password manager such as 1Password or LastPass.
  6. Never put passwords in text documents, Google Docs, intranet pages, post-it notes or other unencrypted forms of storage.
  7. Use two-factor authentication for any services that support it, including WordPress.com accounts, Google apps such as Gmail, Dropbox, Twitter, Facebook, Github, iCloud, LinkedIn, PayPal and others. Do not store 2FA backup codes anywhere online.
  8. Turn on device locating services such as “Find My Mac” for Apple laptops or “Find My iPhone” for iPhones.
  9. Encrypt your computer’s hard drive, and make sure any backups are encrypted too.
  10. Install and run anti-virus software with the latest virus definitions.
  11. Enable your computer’s firewall.
  12. Ensure that your home and office network routers are running the latest firmware and aren’t using default passwords.
  13. Be suspicious of any unusual requests to share sensitive information, such as usernames, passwords or other personal data. Report any such requests and “phishing” attempts.
  14. If working in public, use a privacy screen to prevent your activity being seen.

If you have any security-related questions about your WordPress.com account, your VIP site or any related service, please contact us via support ticket.

Validating, Sanitizing, and Escaping

Your code works, but is it safe? When writing your theme and plugin code, you’ll need to be extra cautious of how you handle data coming into WordPress and how it’s presented to the end user. This commonly comes up when building a settings page for your theme, creating and manipulating shortcodes, or saving and rendering extra data associated with a post. There is a distinction between how input and output are managed, and this document will walk you through that.

(If you’re interested in more thoughts on why WordPress.com VIP takes these practices so seriously, read The Importance of Escaping All The Things from June 2014.)

Guiding Principles

  1. Never trust user input.
  2. Escape as late as possible.
  3. Escape everything from untrusted sources (like databases and users), third-parties (like Twitter), etc.
  4. Never assume anything.
  5. Never trust user input.
  6. Sanitation is okay, but validation/rejection is better.
  7. Never trust user input.

“Escaping isn’t only about protecting from bad guys. It’s just making our software durable. Against random bad input, against malicious input, or against bad weather.”
–nb

Validating: Checking User Input

To validate is to ensure the data you’ve requested of the user matches what they’ve submitted. There are several core methods you can use for input validation; usage obviously depends on the type of fields you’d like to validate. Let’s take a look at an example.

Say we have an input area in our form like this:

<input id="my-zipcode" type="text" maxlength="5" name="my-zipcode" />

Just like that, we’ve limited my user to five characters of input, but there’s no limitation on what they can input. They could enter “11221” or “eval(“. If we’re saving to the database, there’s no way we want to give the user unrestricted write access.

This is where validation plays a role. When processing the form, we’ll write code to check each field for its proper data type. If it’s not of the proper data type, we’ll discard it. For instance, to check “my-zipcode” field, we might do something like this:

$safe_zipcode = intval( $_POST['my-zipcode'] );
if ( ! $safe_zipcode )
$safe_zipcode = '';
update_post_meta( $post->ID, 'my_zipcode', $safe_zipcode );

The intval() function casts user input as an integer, and defaults to zero if the input was a non-numeric value. We then check to see if the value ended up as zero. If it did, we’ll save an empty value to the database. Otherwise, we’ll save the properly validated zipcode.

Note that we could go even further and make sure the the zip code is actually a valid one based on ranges and lengths we expect (e.g. 111111111 is not a valid zip code but would be saved fine with the function above).

This style of validation most closely follows WordPress’ whitelist philosophy: only allow the user to input what you’re expecting. Luckily, there’s a number of handy helper functions you can use for most every data type.

Sanitizing: Cleaning User Input

Sanitization is a bit more liberal of an approach to accepting user data. We can fall back to using these methods when there’s a range of acceptable input.

For instance, if we had a form field like this:

<input id="title" type="text" name="title" />

We could sanitize the data with the sanitize_text_field() function:

$title = sanitize_text_field( $_POST['title'] );
update_post_meta( $post->ID, 'title', $title );

Behind the scenes, the function does the following:

  • Checks for invalid UTF-8
  • Converts single < characters to entity
  • Strips all tags
  • Remove line breaks, tabs and extra white space
  • Strip octets

The sanitize_*() class of helper functions are super nice for us, as they ensure we’re ending up with safe data and require minimal effort on our part.

In some instances using WP_KSES and it’s related functions might be a good idea as you can easily clean HTML while keeping anything relevant to your needs present.

Escaping: Securing Output

For security on the other end of the spectrum, we have escaping. To escape is to take the data you may already have and help secure it prior to rendering it for the end user. WordPress thankfully has a few helper functions we can use for most of what we’ll commonly need to do:

esc_html() we should use anytime our HTML element encloses a section of data we’re outputting.

<h4> <!-- <?php echo esc_html( $title ); ?> --> </h4>

esc_url() should be used on all URLs, including those in the ‘src’ and ‘href’ attributes of an HTML element.

<img alt="" src="<?php echo esc_url( $great_user_picture_url ); ?>" />

esc_js() is intended for inline Javascript.

var value = '<?php echo esc_js( $value ); ?>';

esc_attr() can be used on everything else that’s printed into an HTML element’s attribute.

<ul class="<!--<?php echo esc_attr( $stored_class ); ?>-->">

wp_kses() can be used on everything that is expected to contain HTML.  There are several variants of the main function, each featuring a different list of built-in defaults.  A popular example is wp_kses_post(), which allows all markup normally permitted in posts. You can of course roll your own filter by using wp_kses() directly.

<?php echo wp_kses_post( $partial_html ); echo wp_kses( $another_partial_html , array(
    'a' => array(
        'href' => array(),
        'title' => array()
    ),
    'br' => array(),
    'em' => array(),
    'strong' => array(),
);) ?>;

As an example, passing an array to wp_kses() containing the member

'a' => array( 'href' , 'title', )

means that only those 2 html attributes will be allowed for a tags — all the other ones will be stripped. Referencing a blank array from any given key means that no attributes are allowed for that element and they should all be stripped.

There has historically been a perception that wp_kses() is slow. While it is a bit slower than the other escaping functions, the difference is minimal and does not have as much of an impact as most slow queries or uncached functions would.

It’s important to note that most WordPress functions properly prepare the data for output, and you don’t need to escape again.

<h4><?php the_title(); ?></h4>

rawurlencode() should be used over urlencode() for ensure URLs are correctly encoded. Only legacy systems should use `urlencode()`.

<?php echo esc_url( 'http://example.com/a/safe/url?parameter=' . rawurlencode( $stored_class ) ); ?>

Always Escape Late

It’s best to do the output escaping as late as possible, ideally as data is being outputted.

// Okay, but not that great
$url = esc_url( $url );
$text = esc_html( $text );
echo '<a href="'. $url . '">' . $text . '</a>';

// Much better!
echo '<a href="'. esc_url( $url ) . '">' . esc_html( $text ) . '</a>';

This is for a few reasons:

  • It makes our code reviews and deploys happen faster, because rather than hunting through many lines of code, we can glance at it and know it’s safe for output.
  • Something could inadvertently change the variable between when it was first cast and when it’s output, introducing a potential vulnerability.
  • Future changes could refactor the code significantly. We review code under the assumption that everything is being output escaped/cast – if it’s not and some changes go through that make it no longer safe to output, we may incorrectly allow the code through, since we’re assuming it’s being properly handled on output.
  • Late escaping makes it easier for us to do automatic code scanning (saving us time and cutting down on review/deploy times) – something we’ll be doing more of in the future.
  • Escaping/casting on output simply removes any ambiguity and adds clarity (always develop for the maintainer).

Escape on String Creation

It is sometimes not practical to escape late. In a few rare circumstances you cannot pass the output to wp_kses since by definition it would strip the scripts that are being generated.
In situations like this always escape while creating the string and store the value in a variable that is a postfixed with _escaped, _safe or _clean. So instead of $variable do $variable_escaped or $variable_safe.
Functions must always return “safe” html that do not rely on them being late escaped.This allows you to do echo my_custom_script_code(); without needing the script tag to be passed thru a version of WP_KSES that would allow such tags.

Case Studies and FAQs

We know that validating, sanitizing and escaping can be a complex topic; we’ll add some specific case studies and frequently asked questions here as we think they might be helpful.

Q: Doesn’t a function like WP_Query handle sanitizing user input before running a query for me? Why do I need to also sanitize what I send to it?

A: For maximum security, we don’t want to rely on WP_Query to sanitize our data and hope that there are no bugs or unexpected interactions there now or in the future. It’s a good practice to sanitize anything coming from user-land as soon as you begin to interact with it, treating it as potentially malicious right away.

Q: Isn’t WP_KSES_* slow?
A: Even on large strings WP_KSES_* will not add a significant overhead to your pageload. Most of your pageloads should be cached pageloads and the first thing to focus on should be to make sure as many of your end users as possible are getting cached pages. Slow SQL Queries as well as Remote requests are often next on the list. Escaping is often negligible compared to those items.

Zack Tollman wanted to know more about wp_kses functions, so he did a pretty thorough investigation about them here. https://www.tollmanz.com/wp-kses-performance/. He found that wp_kses functions can be 20-40x slower than esc_* functions on PHP 5.6, but the performance hit is much smaller when using HHVM. The post was written before PHP 7 came out, but PHP 7 is likely to have similar performance to HHVM, meaning that wp_kses functions aren’t as much as a performance drain as they used to be, at least on PHP 7 servers. WordPress.com is using PHP 7.

Q: Why do I need to escape these values? It is impossible for them to be unsafe.
A: It is currently impossible for them to be unsafe. But a later code change could easily make it that the variable is modified and therefore can no longer be trusted. Always late escaping whenever possible makes the code much more robust and future proof.

Conclusion

To recap: Follow the whitelist philosophy with data validation, and only allow the user to input data of your expected type. If it’s not the proper type, discard it. When you have a range of data that can be entered, make sure you sanitize it. Escape data as much and as late as possible on output to avoid XSS and malformed HTML.

Take a look through the Data Validation Plugin Handbook page  to see all of the sanitization and escaping functions WordPress has to offer.

Shortcodes

For security, only certain HTML tags are allowed in content and widgets on WordPress.com. However, with our Protected Embeds feature for VIPs, you are able to include iframes, objects and scripts. In addition, VIP Partners can create customized shortcodes – a type of placeholder tag that can optionally accept arguments.

For example, say you want to embed a video from a website that we do not support via one of our existing WordPress.com shortcodes, and does not work within our Protected Embeds feature. To include it, you would create your own shortcode for this embed with arguments for the video ID, width, height, and so forth.

Check with us before creating a shortcode as another VIP Partner may already have a shortcode for the same web service. We also like to review shortcodes before you commit them.

Shortcode Best Practices

Shortcode names (and functions) should be prefixed to avoid future collisions with new WordPress.com-wide shortcodes. Using something related to your site is preferred. For example: if your company is called Fred’s Refrigerators, your shortcode should be named “fred-coolvideosite.”

As with all user supplied data (the shortcode parameters in this case), it should be thoroughly validated. If someone were able to guess your account’s password, you wouldn’t want them using your shortcode to do malicious things. If you’re expecting a number, then make sure you got a number. If you’re expecting a URL, then make sure you got one of those too.

Security Overview

The security of your content and the WordPress.com platform are matters that we take very seriously. This page provides a few notes on security at WordPress.com VIP.

Secure Users and Access

  • Ask all of your users to go through our User Security Best Practices checklist and confirm that they’ve taken these steps.
  • Consider having us force-enable two-step authentication for users on your site. Two-step authentication requires anyone accessing your site to use their password and a code generated on their mobile device or sent by text to their phone; without access to a user’s device, an attacker cannot login to their account.
  • Review the users who have “Administrator” access on your VIP site(s), since they can change key settings. Try to keep this number to a minimum and use other roles to grant users the minimum permissions they need for their work on your site. If an Administrator hasn’t logged in recently, downgrade their access until they need it again.
  • Remind your users to set strong passwords for their WordPress.com accounts and to never store their password information (or related details such as two-step backup codes) anywhere that could be accessed by others.
  • Consider enabling our New Device Notification plugin so you can be aware when a user logs in from a new or unexpected location.
  • Perform regular audits of users and their roles on your VIP site(s), removing inactive users. VIPs with larger networks of sites can request periodic CSV exports of all users/roles so they can automate user list audits.

Secure Code

Your code works, but is it safe? When writing code for the WordPress.com VIP environment, you’ll need to be extra cautious of how you handle data coming into WordPress and how it’s presented to the end user. Here are some tips to get your started:

  • Don’t trust user input or third-party APIs. Always make sure to validate and sanitize all data that can be populated from “outside.” Review our document on Validating, Sanitizing, and Escaping to learn how.
  • Try to exploit your code with common XSS methods as a test. Visit http://ha.ckers.org/xss.html for a list of basic XSS attacks and make sure pasting these snippets to input fields or url parameters will not harm your site.
  • Make sure to implement nonces to avoid duplicate submission of forms and fraud activities. See https://developer.wordpress.org/plugins/security/nonces/
  • When adding menus or registering your plugins, make sure that you use an unique handle or slug other than __FILE__ to ensure that you are not revealing system paths.

Security Scans

We recognize that VIP sites may wish to perform security scans on their sites, or have 3rd parties do so on their behalf. While this is almost always okay, our systems team may block any traffic that is considered suspicious. This may affect your scan results.

If your team plans to run recurring or intense scans of your site, please open a support ticket in advance of the first scan to provide us with details such as:

  • the anticipated IP or block from which the scan will originate
  • the frequency and/or duration of the scan

Ready to get started?

Tell us about your needs

Let us lead the way. We’ll help you select a top tier development partner. We’ll train your developers, operations, infrastructure, and editorial teams. We’ll coarchitect your deployment processes. We will provide live support for peak events. We’ll help your people avoid dark alleys and blind corners, and reduce wasted cycles.