Using the AMP Plugin to Protect Site Visitors and Debug Security Vulnerabilities

Recently I’ve been testing compatibility for all of Jetpack‘s various widgets when used on pages served by the AMP plugin. In the process I ran across a security vulnerability in Jetpack (which I responsibly disclosed and is now fixed), but I never would have noticed the issue if it weren’t for the AMP plugin’s internal validator.

As you may be aware, AMP is both a subset and superset of HTML. The standard HTML elements which can have problems with performance and privacy are not allowed in AMP. At the same time, AMP is also a web components library which provides custom elements that implement performance best practices and support privacy-preserving prerendering. All of the elements and attributes that AMP allows are codified in a specification which is used to programmatically validate AMP pages. Valid AMP pages can be distributed via an AMP Cache and safely prerendered to a user (e.g. in search results).

The AMP plugin internalizes the AMP specification and it uses the spec to catch invalid AMP markup to prevent it from leaking out onto the frontend. The plugin does its best to ensure your site serves valid AMP pages, not only so that Google Search Console doesn’t complain about AMP validation errors, but also in order to give you immediate feedback without having to wait for Googlebot to crawl your site. In contrast to the plugin’s Classic mode, the plugin no longer silently sanitizes the invalid AMP markup when in the Paired/Native modes; you can now be informed of what markup it is removing. This is particularly important when you have a site running ads or analytics, as you need to be alerted when the related script tags are getting stripped out (as AMP doesn’t allow custom scripts, at least not quite yet, though never like this).

So, back to the Jetpack plugin. When I tested the My Community widget, I noticed some strange new AMP validation errors reported by the AMP plugin, including unrecognized attributes: bencowboy, and alman:

New validation errors appearing after adding Jetpack’s My Community widget.

The AMP plugin’s validator stripped out these invalid attributes—being “accepted” for sanitization—so they would not have shown up on the frontend of the site. But where did they come from? Here also the AMP plugin provides a key tool. As shown above, the plugin already identified that Jetpack was the source of the errors. Then by expanding a validation error, the full context for the error including its source information is provided:

Details for an AMP validation error as provided by the plugin’s internal validator.

Here it is clear that the invalid markup is coming from that My Community widget in Jetpack, as can be seen in the source function (Jetpack_My_Community_Widget::display_callback). When I looked at the widget output in a non-AMP version of the page, the issue became clear:

<li>
    <a 
        href="https://en.gravatar.com/978a1a2a80394217a0e39c84f07a7c16" 
        title=""Cowboy" Ben Alman"
    >
        <img alt="" src="https://0.gravatar.com/avatar/978a1a2a80394217a0e39c84f07a7c16?s=96&d=https%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96&r=G" class="avatar avatar-240" height="48" width="48" originals="240" scale="1" />
    </a>
</li>

The problem was title=""Cowboy" Ben Alman". The title attribute (among other attributes) was not being escaped with esc_attr() in Jetpack_My_Community_Widget::get_community():

foreach ( $members as $member ) {
    $my_community .= sprintf(
        '<li><a href="%s" %s><img alt="" src="%s" class="avatar avatar-240" height="48" width="48" originals="240" scale="1" /></a></li>',
        $member->profile_URL,
        empty( $member->name ) ? '' : 'title="' . $member->name . '"',
        $member->avatar_URL
    );
}

This is a persistent cross-site scripting vulnerability (stored XSS). An attacker could have exploited the vulnerability to run arbitrary JavaScript on a site that uses the My Community widget. All they’d have to do to exploit it is change their account “name” to something like:

John Smith"><script>doSomething("EVIL")</script><a class="

Then since the widget lists users who have recently interacted with the site, the attacker would just have to leave a comment and then wait 10 minutes for the transient to flush. At this point the malicious doSomething('evil') would run for every visitor to the site.

I responsibly disclosed this Jetpack security vulnerability to Automattic’s HackerOne, and I got approval to blog about the find. Many thanks to the Jetpack team for being so responsive and including the fix in a release so quickly.

Remember: Never trust external input. Always validate/sanitize all inputs early and escape all output late.

However, this vulnerability would not have been exploitable on an AMP-first site. In the plugin’s native mode there is no non-AMP version of the site (no paired AMP). The AMP plugin removes all custom script (including script tags and on-event handler attributes), so on a fully AMP site the AMP plugin would have prevented this stored XSS vulnerability from being exploited. Furthermore, the AMP plugin also informs the site owner of such invalid markup being removed and where it came from in the first place.

So the AMP plugin is useful for protecting visitors to your site, as well as providing you with tools for finding and debugging security vulnerabilities. To learn more about the plugin, check out amp-wp.org.

Creating Gutenberg Blocks without a Build Step via HTM

If you’ve ever looked into developing a block for the new WordPress editor (Gutenberg), you’ve seen that it’s recommended to code it up with JSX. Blocks are powered by React and the JSX syntax is significantly more readable and less verbose than the ES5-compatible syntax. For example, compare this ES5 code:

function save( props ) {
    return wp.element.createElement(
        'div',
        {
            id: props.attributes.id 
        },
        props.attributes.content
    );
}

With this equivalent in JSX:

function save( { attributes } ) {
    return <div id={ attributes.id }>
        { attributes.content }
    </div>;
}

The difference is clear. However, a major downside to JSX is that it requires a build step. As the Gutenberg handbook states regarding JavaScript versions and build step:

Additionally, the ESNext code examples in the Gutenberg handbook include JSX syntax, a syntax that blends HTML and JavaScript. It makes it easier to read and write markup code, but likewise requires the build step using webpack and babel to transform into compatible code.

I believe this build step is one of the biggest sources of hesitation toward Gutenberg by the WordPress developer community. WordPress developers have historically developed PHP and JS without any build step, and this has made developing themes and plugins very accessible to newcomers. Without a build step you just edit a file and immediately reload the browser to see the changes; you can also locate and understand the source code more easily since it is not compiled and no source maps are needed. All of the tooling around JavaScript is also very intimidating and an impediment to getting started.

With this in mind, I was excited to see Jason Miller‘s announcement of HTM (Hyperscript Tagged Markup):

While it is possible to write JSX without a build step by loading a standalone Babel into the browser, it is very expensive to do this runtime transpilation and so it’s not recommended in production. In contrast, HTM is small and fast:

It’s built using Tagged Templates and the browser’s HTML parser. Works in all modern browsers.

So HTM offers a third way to write blocks beyond ES5 and JSX. As with ES5 it doesn’t require a build step, while like JSX it has a much more pleasant syntax. Compare the JSX above with the following HTM:

function save( { attributes } ) {
    return html`<div id=${ attributes.id }>
        ${ attributes.content }
    </div>`;
}

I’ve given it a try in my syntax-highlighting Code block which extends the core Code block (forked from mkaz/code-syntax-block). Take a look at code-syntax-block.js for the editor JS file which is enqueued straight into WordPress without any build step.

P.S. Do you realize that you just read an AMP page?

Becoming a Googler

I often see tweets from people in the industry announcing major career changes; I never expected that I would be adding to this stream, but today I am. After more than 8 years at XWP/X-Team, I am starting at Google as of October 1st. I’m joining the Developer Relations team at Google to work on building a stronger web content ecosystem. In my new role I’ll be doing… many of the same things because I’m joining Google for the purpose of continuing to contribute to WordPress. While I have been doing that with the support of XWP, now I’ll be doing so with the backing of Google.

After working heavily on the WordPress 4.8 and 4.9 releases in 2017 (as well as previous core releases), I started transitioning a year ago to working on something very different. XWP started working with Google on a new phase for the AMP plugin and I led the engineering efforts. It was a refreshing change after years of working primarily on the Customizer: I realize now that I was on the verge of burnout at that time, and since we just did a major core release with Customizer improvements and because focus in core shifted fully to Gutenberg, I felt comfortable stepping away for a while to focus on AMP. After several months of working on AMP we then also started working on a PWA feature plugin which aims to bring progressive web app capabilities to core.

Working on AMP and PWA have felt like returning to my roots. Before XWP and before I was involved in WordPress even, I was really interested in open web standards. I contributed (with small acknowledgement) to the HTML5 spec by participating in the mailing list and creating a cross-browser implementation of Web Forms 2.0. I also created polyfills for CSS Transitions and CSS Gradients. I loved learning new cutting edge (progressive) technologies and then finding ways to implement them in projects, often requiring some creative solutions to get them to work in older browsers. (I used to take pride in my knowledge of IE6 workarounds.) I was an early adopter of Ajax, and I was an avid listener of the Audible Ajax podcast on the old Ajaxian blog; I loved that community that Ben and Dion created, and I loved contributing some things I hacked on. (Ben and Dion are both at Google now and I’ll be working in the Chrome team with them.)

My desire is to make as big an impact as possible. This is why I’m passionate about the open web. In publishing some project openly, I know that someone else can benefit from it and build upon it to make something new, just as I have benefited and built upon the projects of others. Everyone can contribute to building a better web. This is also a reason why I love WordPress: not only does it democratize publishing but it also democratizes development.

I’ve loved working at XWP because of our mission to build a better (open) web, and we have been doing so through WordPress. Over the years I’ve also been a big Google fan because of all they’ve done to invest in the open web. But I never thought that I’d get to work at Google, nor even that I’d want to. Nevertheless, this past year of working with Google has been a really great experience. I’ve been able to see first hand their commitment to the open web, and there was such a great alignment with XWP in having a shared mission to make it better. I’ve also been able to work with exciting technologies that serve toward this goal.

For many months I resisted the idea of applying at Google. I’ve invested many years working at  XWP and helping it to grow, and I have many relationships there which I value greatly. I’ve been able to contribute to building a better web at XWP and I could certainly continue to do so there. However, after Google I/O and WordCamp Europe I realized that at the current place in my career, I believe I’ll be able to grow more personally and have a greater impact if I start to contribute from Google while leveraging its support and resources. Additionally, there are others at XWP who can take my place and do more than I ever could to lead the company in technology and engineering; I have total confidence in them. While my relationships with XWPeople will change, they won’t end as I’ll be continuing to work with them on AMP, PWA, WordPress core, and other projects in the future. Read more about this new season for XWP.

So I’m going from working with Google to working at Google. For more see my Googler colleague Alberto Medina’s post about Web Content Ecosystems @ Google. I’ll be based out of Google’s Portland office so I’ll continue to be in PDX. I’m excited about this next chapter in my career and season in my life. Strangely enough, I’m really looking forward to taking TriMet and riding my bike each day to the office, as I’ve been working from home for the past 8 years (which I have loved, don’t get me wrong). But more so I’m looking forward to seeing how Google can build a better open web by investing in WordPress. I’m excited to be a part of it.

WordCamp Europe 2018 Recap: AMP and PWA

Recently I attended WCEU 2018 in Belgrade with quite a few colleagues from XWP. We were there in large part to promote the adoption of progressive technologies in WordPress. We spent a lot of our time at the Google booth where we had an area to talk about contributing to WordPress across a wide range of roles. I spent most of the time in the booth stationed at the AMP area talking about the new capabilities we recently published in the plugin’s v1.0-alpha1 release, and since then we’ve followed up by releasing v1.0-beta1.

I’m really excited about how the AMP plugin is turning out. It now enables you to create AMP-compatible themes in the WordPress way; your theme can render your site in AMP using the same templates and stylesheets you would use normally on a non-AMP site. There is complete visual parity between your AMP pages and your non-AMP pages, aside for some differences in embeds (compare this post with AMP and without AMP). This being the case, you don’t even need to have a non-AMP version of your site anymore (the Paired mode), as the Native mode can just serve your entire site in AMP (such as xwp.co). AMP restricts what HTML you can use in order to guarantee performance and security, and the plugin never serves a response that contains invalid AMP in it. The plugin has a validation workflow to identify what the AMP validation errors are, where they are coming from in the page, and which theme/plugin is to blame. Please try it out and refer to the wiki for all the details on how to leverage the new features, especially Adding Theme Support and Implementing Interactivity.

My colleagues Alberto Medina (of Google) and Thierry Muller (of XWP) gave a great talk on Progressive WordPress which also dove into why AMP is important and how the AMP plugin brings its benefits to WordPress:

And we were interviewed on the topic of Progressive WordPress by Sarah Gooding at WP Tavern:

WCEU Panel Discusses Progressive WordPress Themes, AMP, and Gutenberg

On the topic of Progressive Web Apps, after Matt Mullenweg’s keynote someone asked during the Q&A about about a future where WordPress could be used to create to create apps. Matt responded:

There’s very exciting technologies coming out of two big corporations, one of which is a sponsor of WordCamp Europe, thank you Google. […] The other thing coming from Google which is very very exciting since they also contribute to the largest open source browser which is chrome—progressive web apps. So there’s lots of technologies around there that I think could drastically speed up how wp-admin works in a way that makes it a much more app-like experience without it actually needing to be completely rewritten from the ground up. So I feel like there is an opportunity to get… sort of almost like a JavaScript app-like performance increases from the wp-admin in a mostly backwards-compatible way, with progressive web app technology. So that’s very very exciting. It’s also open standards. They’re being supported by many places. So I feel like there’s a time there when it looked pretty dark to be honest for the web, particularly around performance. Things were just going slower and of course we know users start to tend toward things that are faster. So the apps were winning. But between AMP, progressive web apps, and just all the improvements and optimizations that we’re making including things coming under the hood like PHP7, basically doubling performance of WordPress overnight. […] These things are making it so that we can a really competitive experience and I’m excited about the future of the web.

As I just tweeted, there is now a PWA feature plugin on the WordPress.org directory. Its purpose is to curate Progressive Web App capabilities for proposed merging into WordPress core: service workers, the web app manifest, and improved HTTPS support.

PWA

This PWA feature plugin is intended to equip and facilitate other plugins which implement PWA features. It’s not intended to negate any existing plugins with these features, but rather to allow such plugins (and themes) to work together seamlessly and expand upon them. The plugin’s first release (v0.1.0) includes support for the web app manifest and an API for themes and plugins to register scripts for service workers, of which two are installed: one for the frontend (scope: ~/) and one for the admin (scope: ~/wp-admin/). A next step for service workers in the PWA feature plugin is to integrate Workbox to provide a declarative WordPress PHP abstraction for managing the caching strategies for routes, with support for detecting conflicts. You can follow development and contribute to the plugin on GitHub.

Photos not taken by me are courtesy of Ryan Kienstra, Alberto Medina, and Paul Bakaus.