Photon and HTTPS – Making It Work

Every now and then,  I work on a site that absolutely must be secured 100% with SSL.  This means the styles, scripts, images, and markup are all served over an https connection to prevent nefarious individuals from sitting in the middle and substituting their own versions (thus compromising the overall security of the site).

One of these such sites has been using Jetpack’s Photon service to offload image assets to WordPress.com’s powerful CDN.  Up to a few days ago, this worked like a charm.  Then, on New Year’s Eve, I got a report that things weren’t working any longer. 1

How Things Used to Work

Originally, I configured the site to use standard HTTP for everything.  This was fine during our beta period, but once we added purchasing I wanted to secure the payment pages.  The site uses Stripe.JS to make sure no credit card data hits our server, but delivering that single HTTPS script on a non-HTTPS page isn’t really secure.  Instead, I made the entire payment page require SSL.

As the product evolved, we began adding a persistent customer login indicator throughout the site.  This little header widget eventually added a cart as well, allowing the customer to conceivably check out from anywhere.  The day we allowed customers to purchase from anywhere was also the day I locked the entire site down with SSL.  Every page, every asset, everything is now delivered over HTTPS.

Finally, we integrated Jetpack and Photon to deliver image assets from a CDN (rather than creating a bottleneck with our own, smaller server).  Jetpack even detects that the site is secured and delivers resized/resampled images over HTTP as well.  For a few weeks, things worked perfectly!

What Went Wrong

For new customers, images started disappearing on the website.  A few would still be accessible (if the visitor had cached any of the images by visiting one of our other properties in the past, things would load just fine).  For the most part, though, any image being served from Photon’s CDN was returning a 502 server error.

I dug around a bit more and found this error message was being returned by Photon:

We cannot complete this request, remote data could not be fetched

I searched a bit and found some chatter on the WordPress.org support forums about this same issue.  As I suspected, HTTPS was at fault.  Though the conversation on the forum seemed to indicate I had done things correctly – any request for an HTTP asset would be automatically redirected to its HTTPS counterpart.

According to a friend of mine in the know, this system I had working before … likely shouldn’t have worked in the first place:

Apparently I had been making use of a bug in the platform that has been fixed.  Here’s hoping Photon is able to add images-over-SSL to its repertoire of features in 2014.

How I Fixed It

I am not about to open an already-secure site to regular HTTP traffic.  Instead, I wanted to open just specific files – images – in a specific directory and continue to redirect everything else to the HTTPS version of the resource.

Our server is running Nginx, so this just required a minor tweak of the server configuration file.  Where before I had:

server {
  listen 80;
  server_name example.com www.example.com;
  return 307 https://example.com/$request_uri;
  root /var/www/html;

  access_log /var/log/nginx/example.access.log;
  error_log /var/log/nginx/example.error.log;
}

I now have:

server {
  listen 80;
  server_name example.com www.example.com;
  root /var/www/html;

  access_log /var/log/nginx/example.access.log;
  error_log /var/log/nginx/example.error.log;

  location ~ ^/wp-content/(.*)\.(svg|svgs|jpg|jpeg|gif|png|bmp)$ {
    access_log off;
    log_not_found off;
    expires max;
  }

  location / {
    return 307 https://example.com/$request_uri;
  }
}

As a result, any image requests from the /wp-content directory (plugins, uploads, themes) will be served over both HTTP and HTTPS.  All other requests will be rewritten to HTTPS automatically, keeping the site secure but allowing Photon to fetch our images and serve them from a (still-HTTPS) remote CDN.

Notes:

  1. Update: Since this post was written, the Photon team has fixed things on the server to allow images to be served via HTTPS once again.  So while the suggestions below no longer apply to Photon directly, they’re still useful for other image compression/CDN services.

Comments

  1. says

    Hey Eric,

    I probably shouldn’t admit it in public but security is not my specialty so sometimes I get tripped up on things. That said, help me understand why you would ever need to run images over SSL? I think I understand why it’s important with JS, but not JPG/PNG/GIF.

    Maybe a follow-up post explaining how and why to do SSL and the special cases that can cause problems?

    -Mike
    P.S. 3.5 of 5.

    • says

      There are a couple of reasons. Firstly, an SSL page that loads non-SSL assets (script or images) isn’t secure. It won’t compromise the security of the data you’re exchanging, but it does allow attackers to view what page the visitor is on (based on the Referrer header), so if GET variables are used heavily you can leak information. See this page for more details on the “partially secure” page warning.

        • says

          I will write a longer post on this later. For now, just remember that any non-SSL transaction can be intercepted, viewed, and potentially modified. For scripts and styles this is a huge security vulnerability as a third party (man in the middle) could inject a malicious script into your page. For images, it just means a third party (man in the middle or just someone with a network monitoring tool) can inspect the request and view any information it contains – like request headers.

          9 times out of 10 this won’t be a problem. But when that (arguably) rare tenth case does pop up, you’re leaking potentially sensitive information that can be logged, tracked, and abused by anyone who happens to be listening.

          A WordPress plugin I used in the past, for example, allows you to send private/protected/draft posts to someone else with a specially crafted link that bypasses the “only show to privileged users” protection by affixing a specific query argument to the URL. If I send you this link and you view the page over SSL in a coffee shop, no worries; no one will be able to steal the query arg and scoop my post. If, however, I have some non-SSL images embedded in the article, someone else on the network monitoring your traffic will see the unencrypted request – which will contain a Referrer header referencing the current page URL – and can snatch up this additional information.

          It’s a small vulnerability, but in a few cases is enough of an issue that browser venders will still alert you to “partially encrypted” content and flag the page as insecure.

          • says

            Gotcha. So I think this is my takeaway:

            By itself non-SSL images on an SSL page don’t create a security hole but may create a security hole if combined with some other situation that ‘leaks’ access information.

            Did I get that right?

            • says

              Yep. Thing is, as the application developer we’re not 100% in control of what our clients do with the finished product. So making sure we’re building the app in as secure a way as possible is a must.

              Also, remember that browsers will complain and flag a site as not-fully-secure if you have non-SSL assets on an otherwise SSL page. If this is an e-commerce application, that kind of security flag could be fatal.

  2. says

    Thanks for writing this up! Although they may have fixed this issue earlier, it appears to be back as I ran into it today. I had a site on http and Jetpack’s Photon, Mosaic, and Carousel were all working fine with images that were cached on i0.wp.com, i1.wp.com, i2.wp.com, etc.

    I switched on https and things continued to work for awhile but when I viewed images in the Jetpack Carousel there were broken media icons (I’m guessing due to requesting a different resolution) and on visiting the full image address I got the same message: “We cannot complete this request, remote data could not be fetched” despite manually visiting the image on my address and having the images appear (and having them appear with Shift+Refresh). Searching for the problem brought up many results on WordPress forums where support agents over and over echoed their disbelief that the error was happening and proved unhelpful.

    After coming across your post and reading though a number of sites about Apache I came up with the following for an Apache .htaccess file:

    <IfModule mod_rewrite.c>
      RewriteEngine On
      RewriteCond %{SERVER_PORT} !^443
      RewriteCond %{REQUEST_URI} !^/wp-content/uploads/
      RewriteRule (.*) https://example.com/$1 [R=301,L]
    </IfModule>

    placed above the WordPress generated rules in the .htaccess file.

    I hope the Jetpack team fixes this (again) because I’d prefer to use SSLOptions +StrictRequire and SSLRequireSSL in Apache instead of RewriteRule.

    Thanks again Eric!

Trackbacks

Leave a Reply