Twitter oEmbed is Leaking Data

oEmbeds are fun.  They make it easy to embed third-party content on your site, like tweets, status updates, videos, images, all sorts of stuff.

Unfortunately, to do this, third-party code gets injected into your page.  Don’t worry, this is by design, but it does mean that you should only oEmbed from reputable sites.  WordPress Core is very picky as to the providers that it chooses to accept as oEmbed sources.

Twitter is one of these oEmbed providers.  Here’s an example of an embedded tweet:

Neat, isn’t it?

Now, hover over my name.

See that little url that shows on the bottom left corner of your browser (probably)?  It probably looks just like http://twitter.com/daljo628!

Now, click it.  Don’t worry, I’ll wait.

Did the page you landed on have a bunch of extra cruft appended to the end of it?

Maybe it looked something like https://twitter.com/daljo628?original_referer=http%3A%2F%2Fstephanis.info%2F2014%2F05%2F22%2Ftwitter-oembed-is-leaking-data%2F&tw_i=469505591946129408&tw_p=tweetembed?

If you right-click and inspect the element, the URL is just what you expected! If you right-click and open in a new tab — same thing! But if you click normally and let it trigger a Javascript event, it modifies the link before your browser actually processes it.

After you’ve clicked on it normally once, you can come back and re-inspect it, to see that the URL on the link has now changed to the one with the referer data on it — they’re rewriting it inline and intentionally delaying it so when you first click, you wouldn’t realize that the data was being appended.

This can be a problem because some sites employ concealers for the referer http header (No, I didn’t misspell referrer) like href.li for example. By embedding this in a get parameter forcibly, it’s leaking data in a way very difficult to block, by taking advantage of the trust offered via accepting Twitter as an oEmbed provider.

Decisions

Decisions are never easy.

Even if it’s an entirely trivial matter, it’s still forcing you to do something.

And goshdarnit, I’m lazy.

I also prefer front-loading effort when possible. Ounce of prevention, pound of cure, stitch in time saving nine, and all that.

And I respect other people’s time as much as I value my own. So when I build something, I try to avoid decision points whenever possible. This results in the loss of options occasionally, but I believe a smoother user flow.

Now, occasionally power-users will want to modify functionality. Adding a decision point for all users for the sake of the minority is silly, especially when power-users can leverage other methods — filters, actions, functionality plugins that extend the first plugin — to accomplish their goals.

To each according to their needs. Typical users need a simple, smooth, classy interface. Power users need to get under the hood. Why try to make something that doesn’t work well for either by trying to serve both?

The best middle ground I’ve been able to come up with is offering a secondary ‘under the hood’ plugin that exposes a lot of filters as options. Keep it canonical and clean, but present all the options.

Ideal? Not really. Workable? Probably.

Pressgram Security Concerns

NOTE: This is the first of two posts looking at problems I see with Pressgram. The second, addressing the Terms of Service can be read here.

Hi, folks. Gather round, and let’s have a little chat about password security and transparency.

So Pressgram just released, after a rather successful Kickstarter campaign, and lots of excitement by the community. Hurrah, congratulations, folks! Getting a public release actually shipped is the toughest part of any project, and you’ve got that out. Well done!

I installed the app last night, kicked the tires, and examined how it operates a bit, and I’ve got some concerns that I’d like to voice.

First, though, a bit of background. On the official WordPress Mobile Apps, there’s only so much security that can be reasonably achieved via the XML-RPC API that they (and pretty much all apps) use. With XML-RPC, there are no authentication tokens, you need to send your password in plaintext. Which is normally totally fine, as the password is just stored by your local phone (the security of which you are responsible for yourself), and then stored in a double-hashed and salted form on the server.

It seems that, unlike the WordPress Mobile Apps, the password that you enter in Pressgram isn’t kept private on your own device. Without noting it on a Privacy Policy or in any way notifying you that Pressgram is doing it, your password is stored in plaintext on their server. Which — to be fair — is necessary, if they’re going to be pushing data from the Pressgram Server to your WordPress site, and not going to require having a specialized plugin (like Jetpack) installed on your WordPress site to do it. And I don’t think that Jetpack is necessarily a worthwhile dependency to have for an app like this.

My first concern is that I don’t really like my passwords being stored in plaintext on a third-party server that could be hacked (or for that matter, required to be turned over by an order from a FISA court). Some other applications, such as IFTTT do the same thing, but at least with them, it’s transparent that it’s going to be their server holding your credentials and accessing your WordPress site.

With Pressgram, without further investigation, one would believe that it’s the app directly uploading the files to your WordPress site. After all, that’s what the Kickstarter initially pledged:

I suppose another way you could say it is… it’s your filtered photos published directly to your WordPress-powered blog, when you want, where you want, how you want.

But that’s not the case! For the curious, here’s what I saw when running a test against a honeypot standalone site where I was trapping all the requests sent to it:

Firstly, the App sends two requests to /xmlrpc.php

XXX.XX.XXX.XXX - - [06/Sep/2013:02:51:51 +0000] "POST /xmlrpc.php HTTP/1.1" 200 904 "-" "John.Saddington.Pressgram/1.0 (unknown, iPhone OS 6.1.4, iPhone, Scale/2.000000)"
[Fri Sep 06 02:51:51 2013] [error] [client 174.59.108.165] <?xml version="1.0"?><methodCall><methodName>system.listMethods</methodName><params></params></methodCall>
XXX.XX.XXX.XXX - - [06/Sep/2013:02:51:52 +0000] "POST /xmlrpc.php HTTP/1.1" 200 512 "-" "John.Saddington.Pressgram/1.0 (unknown, iPhone OS 6.1.4, iPhone, Scale/2.000000)"
[Fri Sep 06 02:51:52 2013] [error] [client 174.59.108.165] <?xml version="1.0"?><methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value><string>admin</string></value></param><param><value><string>password</string></value></param></params></methodCall>

These two requests firstly make sure that the site is there and is a WordPress install, and secondly makes sure that the credentials work — and if it’s a multisite install, returns the available blogs.

So far, so good. The User Agent strings are clear as to what they are and what they’re accomplishing.

Then, ten requests come in:

YY.YYY.YYY.YYY - - [06/Sep/2013:02:53:27 +0000] "POST /xmlrpc.php HTTP/1.1" 200 1845 "-" "-"
[Fri Sep 06 02:53:27 2013] [error] [client YY.YYY.YYY.YYY] <?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
<methodName>wp.getTerms</methodName>
<params>
<param>
<value>
<int>1</int>
</value>
</param>
<param>
<value>
<string>admin</string>
</value>
</param>
<param>
<value>
<string>password</string>
</value>
</param>
<param>
<value>
<string>category</string>
</value>
</param>
<param>
<value>
<array>
<data/>
</array>
</value>
</param>
</params>
</methodCall>

and nine others very much like it. If anyone is curious to see them, tweet me, and I’ll post them for folks to review. Checking existing taxonomies, creating new terms, uploading the photo, and creating the post.

XXX.XX.XXX.XXX is the IP address of my phone. YY.YYY.YYY.YYY is the IP Address of the Amazon Cloud Server that Pressgram works off of. I’ve anonymized these just for the sake of privacy. They’re easy enough to find, but it’s not my business to release them. I’ve also removed the base64 encoded image data.

I’ve also captured the request that the Pressgram App uses to send your password up to the Pressgram server — it looks something like this:

URL: https://api.pressgr.am/index.php/Api/postPhotoData
Data:
{
	"social": {},
	"post_content": "Pic",
	"sessionId": "00000000000000000000000000000000",
	"blog": [{
		"title": "Pic",
		"password": "password",
		"login": "admin",
		"url": "honeypot.example.com"
	}],
	"identifier_photo": "0000000000.0000000000"
}

So at least it’s being sent to the Pressgram server over HTTPS.

You’ll notice that the requests coming from the Pressgram Server have no User Agent to identify what they’re there for. They’re all signed with username and password — meaning that the Pressgram servers now have my username and password in plaintext, with not even a notification to me in their Privacy Policy or Terms of Service that this is being transferred up to their servers.

So what does this all mean?

Well, it means that Pressgram is storing your credentials in plaintext (or potentially encrypted alongside a decryption key) on your behalf, without notifying you or doing anything publicly to indicate that this is the case. No matter how high entropy your passwords may be, if you hand it to someone and they get hacked, it doesn’t matter. You are vulnerable — doubly so if you use that password for other accounts as well.

To some folks, this may be a worthwhile tradeoff. But as I look at it, I don’t see it as a necessary tradeoff. Your credentials could just as easily be kept private between the app on your phone, and your WordPress site. Just have your phone upload the photo directly to your WordPress install. It wouldn’t be difficult to do, it’s already making XMLRPC requests to the server. And it fulfills the initial Kickstarter promise of “your filtered photos published directly to your WordPress-powered blog”. It also would provide the added security that if Pressgram is eventually shut down or sold off, the app would still function, as it’s not needlessly dependent on the Pressgram Servers.

To protect yourself, you may want to consider making a seperate account for your WordPress site with the Author role, and using those credentials with Pressgram, and make sure you’re using a distinct password — as well as with any service that you provide a password to.

Conclusion

So in the end, what am I calling for?

Ideally, I’d like to see Pressgram give users the option of simply taking photos, and uploading them directly from the app to their WordPress blog. No servers in the middle with potential vulnerabilities for your data. In short — make the account creation and login optional. Give folks a choice! That sounds a lot more like what the Kickstarter was proposing. If you’d like to build a new social network on top of it (if I had a dime every time a potential client tried building that), make it optional!

Do I see that happening? Well, I hope so, but I’ve found that companies don’t normally like to make themselves less integral to a process. So at the very least, notify your users that their credentials are going to be stored on your servers. To take them as Pressgram has without any such public warning I see as morally questionable, and totally contrary to the values of the WordPress community, which embraces transparency, and not forcing unnecessary service dependencies between you and your site.

UPDATE: Pressgram has updated their Terms of Service to indicate that your content may travel through their network to get to your blog.  Unfortunately, it says nothing in the TOS or its Privacy Policy about your username and password being stored on or passing through its servers.

Events Custom Post Type Proposal for Multisite

This is intended for the Make.WordPress.org series of blogs.  There are a number of needs, from weekly chat schedules for some Make blogs, to WordCamps for others.  Each site needs to be able to display their own events, and the main Make site would need to be able to display an aggregate of all (or some) of the sub-sites.

I see the implementation of the output (data structures will be addressed separately) being done via a shortcode, as follows:

[super-spiffy-event-calendar]

which would do stuff roughly like:

$events = Super_Spiffy_Event_Calendar::get_events();
Super_Spiffy_Event_Calendar::render_plugins( $events );

Not really tricky.  That will display any events from the multisite blog that you happen to be on.  However, for the aggregate, I see something more akin to this:

[super-spiffy-event-calendar blog_ids="2,3,4,5,6,13,14,19,22"]

which would be more akin to:

$original_blog_id = get_current_blog_id();
$events = array();
foreach ( $blog_ids as $blog_id ) {
	switch_to_blog( $blog_id );
	$events = array_merge( $events, Super_Spiffy_Event_Calendar::get_events() );
}
switch_to_blog( $original_blog_id );
Super_Spiffy_Event_Calendar::render_plugins( $events );

A couple things we’d need to add in that aren’t noted here:

  • Caching. Shove it in a transient, so we’re not doing an expensive operation with blog switching on every page load.
  • Sorting. After building the aggregate, it’s probably worth sorting the events chronologically.
  • Display. I’d like to use http://arshaw.com/fullcalendar/ or something similar to handle the output.

Get Remote Part — a handy theme function

Handy little code snippet if your theme or plugin ever needs to regularly check a remote url’s contents, but you want to cache it for a bit:

function get_remote_part( $url, $minutes_to_save = 60 ) {
	$transient_name = 'get_remote_part_' . substr( md5( $url ), 16 );
	if ( false === ( $value = get_transient( $transient_name ) ) ) {
		$value = wp_remote_retrieve_body( wp_remote_get( $url ) );
		if( $value ) {
			set_transient( $transient_name, $value, ( MINUTE_IN_SECONDS * $minutes_to_save ) );
		}
	}
	return $value;
}

My Two Cents on Two Factor

Two-factor authentication should (imho) be in core, but core can’t always provide the best ways to accomplish it, for example, text messaging which requires external APIs.

What I see the best fit being, is this:

There is a framework for Two-Factor Authentication in core, that provides two free no-api-required methods for users to select to validate:

  • Email (with a warning that it’s not as secure)
  • Time-based One-time Password Algorithm (TOTP)
    • This is what Google Authenticator / Authy use.
    • IETF RFC6238

Beyond this, Core would offer a filter to permit plugins to register other authentication methods, for example, Duo Security’s push-based request system, or Jetpack could provide a gateway for text-messages, just as they are sent from WordPress.com.

We would also need to allow a define( 'DISABLE_TWO_FACTOR_AUTH', true ); line in wp-config.php that would switch it off, in case a site owner lost their phone and needed to disable it temporarily.  I could also see use for a customized define to only disable it for a given user.  Ideally this would add a warning to the adminbar for all users that have manage_options() to notify them that it has been disabled.

Other dependencies that would need to be in core:

  • Application Passwords
    • For systems where the user cannot be prompted for a two-factor auth code (XMLRPC, etc), disallow their normal password for authentication, and force them to use a generated application password that is stored in usermeta.
    • For systems where the user can be prompted for a two-factor auth code (wp-login.php) don’t permit the use of application passwords.
  • Backup Auth Codes
    • Saved in usermeta, not terribly much interesting here.

ComicPress and Jetpack Photon

Howdy, all! Just a bit of a reminder if you’re a webcomic creator, and you’re running your webcomics on WordPress, you can get a pretty big performance improvement (and savings on bandwidth costs) if you activate the Photon module in Jetpack.

http://jetpack.me/support/photon/

Photon is a free Image Content Delivery Network hosted by WordPress.com. For most content images (depending on how your theme is serving them up), it will just swap out a CDN url of the image automagically, nothing to configure.

If you’re using ComicPress, though, it’s got some funky ways of outputting images just due to legacy code.  It’s pretty easy to fix, though:

Just upload this as a new file entitled comicpress-photon.php to your /wp-content/mu-plugins/ folder — or add it into your theme (or preferably child theme)’s functions.php file (but without the opening <?php)

It’s a huge savings on your hosting account because when serving images, your shared host has to keep talking to the client the entire time that the image is downloading, which can occasionally take longer than creating the page that the image is embedded in! So if your webserver has less load, it behaves better, your hosting company is probably happier with you, it’s not getting choked with serving up images when it could serve up HTML or the like, and you’ll instantly become 200% more attractive! (Okay, I lied on the last one)