69 comments

[ 3.5 ms ] story [ 130 ms ] thread
Basically, now Chrome sends the header HTTPS: 1 when issuing a request over HTTP to notify it "prefers" HTTPS.

However, this header is (or was) used by several HTTP servers/proxies (including Apache, I believe) to notify the application that the client connection is established in HTTPS. Wordpress reacts to this header by generating https:// URLs, which may not work if the server doesn't serve HTTPS with a properly configured certificate.

(comment deleted)
Forgive my ignorance, but why would a browser need to say that it prefers https? Doesn't that go without saying nowerdays?

Surely only the most obscure, ancient legacy systems would have problems with being redirected to https?

Indeed, but you can see that a bunch of Wordpress sites are obscure, ancient and insecure by default.
He's talking about a client not supporting HTTPS.

A client may have reasons to prefer HTTP over HTTPS: perfs, plaintext for debug, etc. It's hard to assume that "HTTPS should be the default" in any circumstance.

I'm not sure the wordpress sites are to blame here (for once): SSL isn't free to deploy (yet).

SSL is free to deploy: https://www.startssl.com?app=12

If you have a site that gets enough traffic that you need a better supported or more validated certificate, then I'm sure the $40/yr for a cheapass godaddy cert is worth the money.

Assuming you have time and knowledge to do it. Assuming you own the domain or at least are able to access to its DNS records. Assuming you can use SNI (and all your visitors too) or have your own IPv4 address. Assuming $40 is free...

No, even a free certificate doesn't mean that deploying ssl is free.

Shouldn't the frontend remove the header if it's something that it would normally add? It's a major problem if your application is expecting a header to be set by the frontend web server, but is actually getting the value from the user agent.
The issue here is that the frontend may be behind a reverse proxy, talking to the client in HTTPS but to the frontend in HTTP. In that case, the frontend should probably let the HTTPS header.

In practice, with X-Forwarded-Proto, they don't, unless they can authenticate the downstream hop.

I haven't done web development for a while, but here's how I imagine the frontend server should behave.

Let's say you configure your frontend server to set X-Foo and X-Bar fields. First, it should always send a header to the application indicating that it's messing with X-Foo and X-Bar, say "X-Added-By-My-Webserver: X-Foo, X-Bar". That way, the application can be sure that the frontend server is properly configured. Next, it should strip any X-Foo, X-Bar, and X-Added-By-My-Webserver, to prevent the client from being able to confuse the application with information the application expects to receive from the server.

This is still flaky because the frontend server could fail, and the user could send X-Added-By-My-Webserver, and so on, but if you must use in-band signalling, this seems like the safe route. Just blissfully adding a header is not enough to protect against evil or changing user agents.

Furthermore, it seems like a bad idea to make up your own header and not use an X- prefix.

Yes, it's a bad idea, but an awful lot of bad ideas were the common practice a long time ago, when vendors had to deal with limitations of the HTTP/1.0 and 1.1 RFCs.

In fact, we aren't talking about application headers. The HTTP RFC makes a distinction between application headers and hop-by-hop headers. There are mechanisms that proxies are required to implement, but they still don't.

For instance, a proxy must declare itself by appending its signature to a "Via" header - however most of them don't.

The RFC about forwarded HTTP extension is unclear about how a hop should deal with those headers, there is no one-size-fit-all situations: http://tools.ietf.org/html/rfc7239#section-8.1

The X- prefix recommendation was deprecated a couple years ago. Since everyone used that format, you had to be careful of collisions anyway. That's not to say you can't use X- to start custom headers, it's just not required.
Agree. I'm having a hard time to see how this is Chrome's fault. All discussion i've seen indicate that wordpress and woocommerce treat the HTTPS header in special (and non-standard) ways
It's Chrome fault to use push for a new standardized header without properly checking its impact: the issue has been raised during the beta phase, but they underestimated it and did not rollback.

I believe it is known that some old reverse-proxies used this header before a standard solution emerged (X-forwarded-proto), Wordpress probably had to deal with this surprising behavior at some point.

Also, by naming a header "HTTPS", one could expect collision with a non-standard behavior: this is very common with HTTP/1.1.

I agree. However, non-standard conforming software should continue to use the X- prefix, like if these proxies used a "X-HTTPS" indicating it is a non-standard header, this would be a non-issue.
It's not their fault directly but when it breaks sites hosted with the most common server on the web, Apache, due to an Apache bug it's a lot easier to fix Chrome than every Apache installation.
No.

The problem is, the using "HTTPS" HTTP header (that translates to "HTTP_HTTPS" environment variable in CGI terms) is a kludge. It should've been bare "HTTPS" variable in a same manner "REQUEST_ADDR" is, but someone didn't care much (or wasn't able to do things properly in early days) but shared a recipe that became so widespread, it's a nearly de-facto standard now. Or maybe this had evolved when TLS termination proxies were much more common, but due to laziness the recipe had eventually lost mandatory sanitization of X-Forwarded-For and HTTPS headers.

Still, if webserver is unaware of HTTPS (say, it had never ever had anything with TLS) it's not really webserver's fault it's not dropping some request header.

Hm, shouldn't apache be ensuring that clients _can't_ set the flag that indicates "this is an HTTPS request"?

Otherwise app code that's _checking_ the flag, to ensure that a sensitive URL is only being accessed via HTTPS can be spoofed by the client simply saying it's HTTPS when it's not. That doesn't seem right.

If that's what's going on, it seems like a bug in apache, with security consequences.

?

The current title of this post (”Chrome 44 breaks any wordpress site not able to serve in HTTPS”) seems exaggerated.

This issue does not render all non-HTTPS WP sites useless. I tried on a friend’s site and it served fine in Chrome version 44.0.2403.89.

After skimming the bug report it seems to mostly affect the login functionality and not the user-facing parts of WP. Still problematic but less severe than what it sounds like.

If the admin can't log in to do anything I think it's fair to say the site is broken.
Yep. But many Wordpress sites are rarely updated, and the owners of those sites need not panic :)
It does break frontends too: assets and links URL are generated with an https:// scheme, which results in assets not loaded and broken links.
> I tried on a friend’s site and it served fine in Chrome version 44.0.2403.89.

Their site must have an unused HTTPS version, then.

It seems crazy to:

1) Implement a draft header from the W3 at this stage (which I think this is from what I can see.. there's even some fun debate about how many characters long it should be: https://github.com/w3c/webappsec/issues/216). Do they really expect webservers to handle this correctly?

2) Despite having a bug like this open in Beta, allow this to get out to Prod! Surely it's obvious this is a major breaking change?!

edit: here's the working draft document: http://www.w3.org/TR/upgrade-insecure-requests/#preference

1) Well, web servers are always evolving, think of HTTP/2 :)

2) During the beta, they didn't receive enough feedback to evaluate the criticality of the problem, they assumed that the fix could wait 6 weeks.

The header is 9 days old, and now Chrome is sending it by default: https://github.com/w3c/webappsec/commit/eeac3922418bfa6cb254...

Header bloat, ~28 extra bytes per request from every Chrome user in the world.

The whole idea of the header is odd, it should be something the server could send to the client, if needed, not something the client should announce support for.

Crazy indeed.

Yeah I read the draft and the rationale seems very weak. What's wrong with just sending the Content-Security-Policy header in responses and letting UAs do what they will? Nothing. Same as redirecting to HTTPS if you support it. But for some reason, they had to combine things and want to know if it's "safe" to use HTTPS. They didn't appear to list any real scenarios for this behavior. (Maybe there are, it's not readily apparent though.)
Chrome isn't breaking the applications. The applications are breaking the applications
(comment deleted)
Shouldn't a header called HTTPS end up in $_SERVER['HTTP_HTTPS'] and not 'HTTPS'?
Yup and this sounds like a massive security hole in PHP if you can change $_SERVER elements like 'REMOTE_ADDR' with headers.
(comment deleted)
It's not in PHP, its in Apache. Apache's the one in charge of reading the request and passing to PHP the server variables, it's the one who made the contract that HTTPS means the request was made over HTTPS, it's the one who send to the called handler the value of HTTPS and it's also the one who put the wrong value in it.

It wrongly put the browser-sent header in there overwriting it's own variable (or creating it when it shouldn't).

It depends on the web server and runtime environment in use. WooCommerce for example examines both of those variables, as either or both might be set.
This is a web browser. Bug-for-bug compatibility with 2+ decade old content servers is a fundamental requirement.

Yes, standards are great. But you can't break sites.

I'd say it's more like a courtesy than a fundamental requirement. Obviously Chrome has overstepped the mark here, but you can't just perpetually support entrenched bugs otherwise you end up with a browser like internet explorer.
Everything is relative, and surely there are some misfeatures that simply can't be handled gracefully. But broadly, no: if some user wants to hit a archived site from 1995 using Chrome 44, I think virtually everyone would expect that to work and not fail.

To take your extrapolation in the other direction: you're arguing that if we want to hit old stuff that we need to do it in a VM running Windows 98 and IE4.

Sounds more like a language issue.

    $_SERVER is an array containing information such as headers, paths, and script locations.

    You may or may not find any of the following elements in $_SERVER.
    'HTTPS'
    Set to a non-empty value if the script was queried through the HTTPS protocol.
https://secure.php.net/manual/en/reserved.variables.server.p...
But then the environments violate that specification if they set this variable off a random HTTP header?
If the language spec says that $_SERVER['HTTPS'] is an indicator of whether the request was made over HTTPS, then it should ensure that it adheres to its own spec (I.E. not overwrite $_SERVER['HTTPS'] with the value from a header, if it's present).
It does. The problem is in Apache, not the language.

Apache's says to look into the HTTPS variable (that it adds itself before passing the call to the handler) to know if the call was made over https, but then it overwrite that variable with the browser-sent header if there is one (or more exactly, it uses the same variable name for that header and it's own HTTPS indicator; so it overwrites if both are present, and if only the header is present then it creates it when it shouldn't -- the issue in this post).

That's not an issue with PHP itself. $_SERVER maps directly to CGI variables (or equivalents provided by the web server in non-CGI environments).
> This is not going to be a fun week for Chrome users

Disagreed! This is a great week to see which sites break because of this in order to avoid them in the future. (Yes, leading a life without Flash, PHP, Node and all the other horrible technologies that came out of the web is possible!)

Why does it matter if someone's site is running PHP or Node on the backend? Doesn't change anything about your client side experience.
What do you consider great technologies?
(comment deleted)
I don't understand this in the context of Wordpress and PHP. As a relic of the CGI spec, a header called "Foo" would be turned into an array index called "HTTP_FOO".

For a header called "HTTPS" to be turned into $_SERVER['HTTPS'] as opposed to $_SERVER['HTTP_HTTPS'] would require quite an impressive misconfiguration of the web server which also requires additional work compared to default settings (including possibly even patching PHP itself depending on SAPI).

(comment deleted)
(comment deleted)
From the Google bug report (https://crbug.com/505268), it looks like WooCommerce was looking explicitly for $_SERVER['HTTP_HTTPS'], and this code was removed when the Chrome behavior was discovered:

https://github.com/woothemes/woocommerce/issues/8479

So "breaking web applications everywhere" is an exaggeration.

It also looks like this is the implementation of the W3 "upgrade-insecure-requests" spec, so "Chrome 44 sending HTTPs header by mistake" is also not true. It's intentional.

http://www.w3.org/TR/upgrade-insecure-requests/

But it sounds like they're going to implement a different header / propose changes to the spec based on real-world feedback like this.

(comment deleted)
> For a header called "HTTPS" to be turned into $_SERVER['HTTPS'] as opposed to $_SERVER['HTTP_HTTPS'] would require quite an impressive misconfiguration of the web server which also requires additional work compared to default settings (including possibly even patching PHP itself depending on SAPI).

I'd just like to say that this, indeed, appears to be the case. Apache bug most likely.

> As a relic of the CGI spec, a header called "Foo" would be turned into an array index called "HTTP_FOO".

Usually, yes. But CGI has some headers that don't get that prefix, things like "CONTENT_LENGTH".

It looks to me like whoever added the HTTPS variable feature to Apache implemented it as an unprefixed header rather than a proper CGI variable, for some reason, and that's the issue.

That, or it is a CGI var, but some error in Apache allows HTTP headers to set it.

I would have said that content_length is special in that it's supposed to be the actual content length, not the value sent by the client in the header.

But I definitely defer to your wisdom as I'm just a user who hasn't done pure CGI in 15 years. As somebody who works on PHP, you are probably infinitely more qualified

You have a point there. If the request body was weird and lacked the header somehow, I suppose CONTENT_LENGTH would still be there.
What Unit test could have cought that ? Is this a Bug at all ?

Chrome doesn't do anything wrong, but somehow the fix still has to be done inside Chrome.

Interesting how it all starts looking like one giant app, were a small change in one part leads to failures in completely unpredictable places.

It's a spec bug: they wrote a draft spec, implemented it and pushed it to a stable release too soon.
No unit test suite can cover all situations, you actually have to "use it in anger" before shipping it to something like 50% of all web users.

Chrome is "breaking the internet" for its users, so Chrome is doing something wrong.

That's what Canary Releases are for. You roll it out to (say) 2% of your users and see whether it shits the bed. If it does, NBD, you roll it back or patch. If it's good you roll it out to everyone.
Yeah, some fairly large sites being affected by this. Especially in regards to the "too many redirects" symptom.
Just to summarize, from what I've read this affects only Apache installations (possibly only Apache + mod_php).

I just stood up a WordPress site on Sunday running on nginx + HHVM (FastCGI), and it's not experiencing any issues.

Hi guys, i just want to share a fix for this issue with you. You can download the plugin from www.wdc.me/chrome-ssl-fix.zip I hope this will help you.
Maybe if they focused less on inverting binary trees then they'd ship less busted software.