Security updates for our GA and SEO plugins & many others

April 20th, 2015 – 31 Comments

In this post, we’re announcing a security update to both our SEO plugin and our Google Analytics plugin. Chances are, a few of the other plugins you use are affected too. Read on below if you’re interested in the how and why, but make sure you go into your WordPress admin and update. Don’t just update our plugins, update all of them!!

The backstory

There are several issues fixed in these releases, so we’ve got individual release posts for WP SEO and for our GA plugin. The main issue we’re fixing with this release is the wrong usage of add_query_arg and remove_query_arg we had in both our WordPress SEO plugin and our Google Analytics by Yoast plugin.

This issue was responsibly disclosed to us by Johannes Schmitt of Scrutinizer CI (thank you!!), who found it in our SEO plugin. We discussed it with our partners at Sucuri. At first we thought it wasn’t exploitable, later on we found it allowed for XSS. In our case, you needed to be logged in as an admin to be XSS’ed, but still this was an issue to fix.

I, Joost, created the particular problem myself and was wondering how that had gotten by me, when I figured out that both the Codex and the developer documentation on for these functions were missing the fact that you had to escape their output. In fact, the examples in them when copied would create exploitable code straight away. I spoke to Samuel, mostly known in the WordPress community as Otto42, and he fixed the codex. A day later, the developer docs were amended as well.

We were ready to do a security release last Wednesday. I was hesitant as I was guessing that more people had made the same mistake, because of the documentation. I talked to Dion Hulse, one of the people on the plugins team, and started doing a search, together with the team at Sucuri. We quickly found we were far from the only one.

A coordinated security release

As we researched, we quickly identified a few dozen affected plugins, lots of them major; the affected plugins include Gravity Forms, Easy Digital Downloads, Jetpack, WP e-Commerce, All In One SEO pack and that’s just some of the big ones. Based on this info Daniel Cid at Sucuri and myself started reaching out to those plugin developers and coordinating a big security update between all of us. WordPress Slack proved to be very helpful for this kind of coordination.

Some of these plugins had XSS issues on the frontend. We did not, so when the core team offered to do an automatic update, we opted out. The last time we did an automatic update (this is an update your WordPress installs automatically without your intervention), our WordPress SEO plugin got disabled on hundreds of sites and we didn’t want that to happen. Choices like these are tough to make: some sites might have a minor security issue now, but for many sites not having our SEO plugin enabled might actually be worse.

I must say I’m quite proud of the community getting together like this and coordinating a release in such a fluent way. There are in total 44 people in the Slack group coordinating this release, and everyone is being very professional in dealing with it. After all, we’re updating dozens of plugins, and most of them had only 3 to 4 days notice, including a weekend. All these  WordPress plugin developer working together with the WordPress core security team, makes me proud to be a part of this community!

For users: I don’t see the update yet!

If you don’t see the update yet, go to your wp-admin/update-core.php page, under Dashboard → Updates, this will clear the cache for all updates and should then show you the updates for our plugins.

Going to this page will also make sure any automatic updates are done a few seconds later too. Be sure to check your plugins page a minute or so later to see if all the needed plugins are still active.

For developers: how to fix the issue

The short version for developers of how to fix this issue: if you’re using either add_query_arg or remove_query_arg without passing in the URL, it bases the URL it creates off of $_SERVER['REQUEST_URI']. In that process, it URL decodes the parameter names in the request URI, allowing for XSS. The solution is to simply wrap the output in esc_url and you’re done. Not a hard fix, but it has to be done.

If you think your plugin or theme is vulnerable and want to find out, feel free to reach out to me on WordPress Slack (I’m @joostdevalk there) and I’ll show you how to exploit it. As there are still bound to be vulnerable plugins and themes out there, I’m not going to explain that here.

31 Responses to Security updates for our GA and SEO plugins & many others

  1. JT
    By JT on 3 May, 2015

    Good find but you should always escape any variable/function that is passed to any template for use as a linking url. Safe or not it could change later.

  2. Health Blog
    By Health Blog on 29 April, 2015

    This is why I love to use your plugins. Your team rocks. Thank you guys.

  3. Joe Melberg
    By Joe Melberg on 28 April, 2015

    I’m using these functions, but never echo urls – I’m outputting with wp_safe_redirect() and a _wpnonce – am I still vulnerable? I’m no hacker, so I don’t get this stuff very easily. If I am vulnerable, how does one escape output in a line like this:

    wp_safe_redirect( add_query_arg( $redirect_args, remove_query_arg( array( ‘action’, ‘_wpnonce’ ) ) ) );

  4. kneicy
    By kneicy on 28 April, 2015

    can’t find the update on any of my websites, even after following above advice. :

  5. karen
    By karen on 26 April, 2015

    I have now updated my premium version. All fixed.

  6. ?????
    By ????? on 25 April, 2015

    I don’t understand why they can’t just fix add_query_arg to escape it’s parameters properly. Sure, changing of & into & should be the responsibility of the caller (because add_query_arg can’t possibly know whether you need to use the url in HTML as opposed to an email or HTTP header). But basic urlencoding of the parameter

  7. karen
    By karen on 25 April, 2015

    When I try to update your premium seo plug, it wont update when i click on the update plugin button. Any suggestions how I can achieve the update? Thank you

  8. Liesbeth
    By Liesbeth on 24 April, 2015

    IMHO, it is the function add_query_arg that is at fault here. That is the function that builds a url and adds characters such as =, & and # to glue the paramaters together. Therefore, that is also the function that should deal with escaping (url encoding) those very same characters when they occur inside the parameters.

    Doing the escaping aftrewards with esc_url, doesn’t even work, because afterwards you can’t possible know anymore whether an = is part of a parameter, or serves to separate a parameter name from its value. Just try it: esc_url(add_query_arg(‘param’, ‘value=with=special=characters’)) and you’ll see that the = in your value were not escaped by esc_url().
    I could of course escape my parameter name and value before calling add_query_arg(). But the whole point of a function like add_query_arg is to abstract away the details of how url’s are constructed. If I have to know that = has special meaning in url’s, I might as well just build the url with string functions instead of add_query_arg.

    I don’t understand why they can’t just fix add_query_arg to escape it’s parameters properly. Sure, changing of & into & should be the responsibility of the caller (because add_query_arg can’t possibly know whether you need to use the url in HTML as opposed to an email or HTTP header). But basic urlencoding of the parameter names and values belongs in add_query_arg.

    • Jan de Baat
      By Jan de Baat on 26 April, 2015

      I second that!

      Did you get any response yet?

    • Liesbeth
      By Liesbeth on 24 April, 2015

      Hmm, I needed to have something twice escaped in my last paragraph, the comment system doesn’t like that :(

      In my last paragraph, where I say ‘changing of & into &’ I meant ‘changing & into the &- a–m-p-; (the HTML-entity for ampersand).

  9. Karl
    By Karl on 24 April, 2015

    Thanks for working on this in a responsible way and for alerting others in the WordPress community!

  10. Linda Francetich
    By Linda Francetich on 24 April, 2015

    I had to disable the Yoast SEO plugin because the update removed my page content entirely on my theme. Any suggestions on how to remedy this?

    Thank you!

    • Linda Francetich
      By Linda Francetich on 24 April, 2015

      P.S. It seems to only affect this theme.

      • Gisela Giardino
        By Gisela Giardino on 28 April, 2015

        This happened to me too, using the Divi theme. Complete blank. But I did this on another installation of Divi with no problem at all. What theme are you using?

  11. Mort
    By Mort on 23 April, 2015

    Hello – Thanks for all this great info.

    Question: is the change to admin/views/about.php ( the only place in WordPress SEO that is vulnerable to the add_query_arg or remove_query_arg issue?

  12. Todd Richard
    By Todd Richard on 22 April, 2015

    You peeps are always on top of it, thanks again! All updates went well over here. XD

  13. Alexzender
    By Alexzender on 22 April, 2015

    Thanks for sharing this information. Its very important for me I also doing course of SEO and WordPress from training Institute


  14. Alex H
    By Alex H on 22 April, 2015

    Great update… however, one big request: can you please remove the step where an update or install AUTOMATICALLY takes me to the SEO page? It is incredible annoying to end up where you don’t want to be while doing updates. Please make it stop!

  15. jrf
    By jrf on 21 April, 2015

    Another thing which should be mentioned and which I’ve hardly (or at all) seen mentioned during this whole security update drive is that all variable parameters which are passed to add_query_arg() should be encoded using urlencode().
    Obviously hard-coded parameters which are passed and which do not contain “strange” characters are excepted.

  16. Darlene Hart
    By Darlene Hart on 21 April, 2015

    I am trying to install the update and keep getting the following error: An error occurred while updating WordPress SEO Premium: Could not create directory. wordpress-seo-premium/vendor/composer/installers/src/Composer/Installers. I added the folders manually and continue to get the same error. (C:inetpubwwwrootwpwp-contentpluginswordpress-seo-premiumvendorcomposerinstallerssrcComposerInstallers)

  17. Peter Konstantakos
    By Peter Konstantakos on 21 April, 2015

    We updated the SEO Plug In and the WooCommerce plug in now we are getting errors in the cart. What can we do to fix this? It is happening on two of my sites

    Fatal error: Call to undefined method WPSEO_OpenGraph::image_output() in /home3/tpadgett/public_html/wp-content/plugins/wpseo-woocommerce/wpseo-woocommerce.php on line 612

  18. Sander
    By Sander on 21 April, 2015

    Thanks for not removing .hg from /vendor/xrstf/composer-php52 ….NOT

  19. Leon Ridge-Cooke
    By Leon Ridge-Cooke on 21 April, 2015

    Thanks for the update. Now to update all my clients as well. Cheers Leon

  20. matelo
    By matelo on 21 April, 2015

    Hi, I visited my wp-admin/update-core.php page but I don’t see the update yet, WordPress Seo version is and GA version is 4.3.5 (it seems that also the previous updates don’t show up)

    thank you in advance

    • Joost de Valk
      By Joost de Valk on 21 April, 2015

      Hey matelo,

      that means you’ve been missing out on updates for over a year, might be good to check in with your host.

      • jrf
        By jrf on 21 April, 2015

        It might also mean that the WP install is too old for the newer versions, so that may need to be updated first…

  21. Nigel Abery
    By Nigel Abery on 21 April, 2015

    Thanks guys & girls, the team at Yoast are awesome! Thank you for the great plugins.

  22. Adam
    By Adam on 21 April, 2015

    Well done to the whole community. It’s great to collaboration like this for the good of all.

  23. Claudiu
    By Claudiu on 20 April, 2015

    Thanks again for your great support.

  24. Ollie
    By Ollie on 20 April, 2015

    Thanks for the full disclosure and quick fix.

    I got here through Gravity Forms and it seems that a lot of other plugins were also impacted.

    On the whole, I’m against plugins auto updating, but I would support it for a handful (like Yoast, Gravity Forms etc) that really have their stuff together.

  25. Viktor Nagornyy
    By Viktor Nagornyy on 20 April, 2015

    Good job Joost and team! Thanks for taking care of it quickly. We’ve just finished updating all our clients :)

Check out our must read articles about Analytics