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.
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).
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.
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.
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?!
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.
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.)
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).
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.
$_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.
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).
> 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!)
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).
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:
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.
> 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
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.
The reason this is happening is bad PHP code implementations, checking HTTP_HTTPS instead of HTTPS. Most likely the result of proxy configs, that prefixed "legit" headers with HTTP_.
69 comments
[ 3.5 ms ] story [ 130 ms ] threadHowever, 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.
Surely only the most obscure, ancient legacy systems would have problems with being redirected to 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).
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.
No, even a free certificate doesn't mean that deploying ssl is free.
In practice, with X-Forwarded-Proto, they don't, unless they can authenticate the downstream hop.
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.
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
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.
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.
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.
?
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.
Their site must have an unused HTTPS version, then.
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
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.
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.
It wrongly put the browser-sent header in there overwriting it's own variable (or creating it when it shouldn't).
Yes, standards are great. But you can't break sites.
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.
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).
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!)
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).
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.
Has anyone actually confirmed the problem on a non-WooCommerce site? or a site without bad PHP code?
I'd just like to say that this, indeed, appears to be the case. Apache bug most likely.
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.
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
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.
Chrome is "breaking the internet" for its users, so Chrome is doing something wrong.
I just stood up a WordPress site on Sunday running on nginx + HHVM (FastCGI), and it's not experiencing any issues.
More technical info here: https://ma.ttias.be/chrome-44-sending-https-header-by-mistak...