Chrome 44 Sending HTTPs Header By Mistake, Breaking (Some) Web Applications

Mattias Geniar, Thursday, July 23, 2015 - last modified: Saturday, August 1, 2015

Update #1: it's less worse than it looked, see details below.
Update #2: chrome got updated, the HTTPS-header is gone, see details below.

Now this is interesting.

In the Chrome 44 release (version 44.0.2403.89) that happened just yesterday, it appears the browser got a small bug significant change.

It's now sending the HTTPS: 1 header on every request by default. This was probably meant as a security improvement, to suggest HTTPs to the server wherever possible, but it's breaking WordPress and other webserver installations all over the place.

chrome_https_mistake_bug

Why? Because most PHP software uses $_SERVER['HTTPS']; to detect if the site is running behind an SSL certificate or not. This includes WordPress, Drupal and any custom PHP software that checks this header.

if ($_SERVER['HTTPS'] || $_SERVER['HTTP_HTTPS']) { 
  // Assume HTTPs, redirect or enable https:// prefixes on all resources (css/js/images/...)
  ...
}

The next planned release of Chrome is scheduled on July 27th, but they're investigating if an emergency patch can be sent out to resolve this issue.

Bugtracker: Issue 505268: Forcing WordPress sites to use https even when not directed.

This is not going to be a fun week for Chrome users.

Update #1: only HTTP_ prefixed headers are affected.

Any request header a browser sends along, gets prefixed with HTTP_ in the $_SERVER global variable. The User-Agent gets transformed into HTTP_USER_AGENT, the Accept-header gets turned into HTTP_ACCEPT, ... and so on.

Here's what those HTTP headers look like according to PHP.

print_r($_SERVER);

Array
(
    ...
    [HTTPS] => on
    [HTTP_HOST] => ma.ttias.be
    [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    [HTTP_ACCEPT_ENCODING] => gzip, deflate, sdch
    [HTTP_ACCEPT_LANGUAGE] => nl-NL,nl;q=0.8,en-US;q=0.6,en;q=0.4,fr;q=0.2,it;q=0.2
    [HTTP_COOKIE] => some=value
    [HTTP_HTTPS] => 1
    [HTTP_USER_AGENT] => Mozilla/5.0
)

The first HTTPS value in the $_SERVER variable is the one set by the webserver, to indicate HTTPs or not. The second one, HTTP_HTTPS, is the HTTPS-header sent by the Chrome browser that gets transformed into an variable for PHP.

Some PHP code, like the WooCommerce WordPress plugin didn't only check $_SERVER['HTTPS'], but also looked at $_SERVER['HTTP_HTTPS'] to detect HTTPS on the site. This is wrong PHP code and has nothing to do with "bad PHP by design".

The HTTP_ prefix handling done by WooCommerce is probably done as a result of a Apache bug/feature that once existed, where after 301/302 HTTP rewrites some environment variables would always get prefixed with another HTTP_ keyword.

There are also reverse proxy configs (Nginx, Varnish, ...) out there that also pass environment variables along that get interpreted by Apache as headers and thus receive the HTTP_ prefix.

Either way, most plugins have had an update by now, just go to your WordPress/Drupal/... site and update all plugins. Chances are, the problems will be gone, even if Chrome takes a couple more days to get the patch out.

Fixing WooCommerce specifically

Most problems are reported by WooCommerce, the WordPress plugin. Since chances are, you can't log into your WordPress any more, you'll need another fix. Many thanks to Rahul Lohakare in the comments for this fix.

Modify the following file, either download it via FTP and upload it again or modify it directly on the server: wp-content\plugins\woocommerce\woocommerce.php

Comment out these lines:

if ( ! isset( $_SERVER['HTTPS'] ) && ! empty( $_SERVER['HTTP_HTTPS'] ) ) {
    $_SERVER['HTTPS'] = $_SERVER['HTTP_HTTPS'];
}

Once those have been commented (just prefix every line with #), WooCommerce/WordPress won't redirect you anymore.

If that still doesn't work, use Firefox or another browser to log into your site and update the plugin via the official WordPress update method. Since the problem is Chrome-only, you can bypass the redirect by using any other browser, temporarily.

Update #2: Chrome got updated, replaced 'HTTPS' with 'upgrade-insecure-requests'

Chrome pushed out an update, everyone should get it in a few hours. I'm now on version 44.0.2403.107, anything higher will not have this problem anymore.

chrome_update

After the update, the HTTPS header has been removed by the upgrade-insecure-request header.

chrome_header_upgrade_insecure_requests

Good news for everyone!



Hi! My name is Mattias Geniar. I'm a Support Manager at Nucleus Hosting in Belgium, a general web geek, public speaker and podcaster. Currently working on DNS Spy. Follow me on Twitter as @mattiasgeniar.

I respect your privacy and you won't get spam. Ever.
Just a weekly newsletter about Linux and open source.

SysCast podcast

In the SysCast podcast I talk about Linux & open source projects, interview sysadmins or developers and discuss web-related technologies. A show by and for geeks!

cron.weekly newsletter

A weekly newsletter - delivered every Sunday - for Linux sysadmins and open source users. It helps keeps you informed about open source projects, Linux guides & tutorials and the latest news.

Share this post

Did you like this post? Will you help me share it on social media? Thanks!

Comments

Tim Thursday, July 23, 2015 at 17:05 (permalink)

To be fair, the underlying reason is just another bad design decision on PHP’s part.

Reply


    Sameer Thursday, July 23, 2015 at 17:10 (permalink)

    It’s not a bad PHP design decision, it’s a bad design decision of whoever is using it that way in PHP. To be fair that code is actually only reading a HTTP header variable, so its not bad or good, its up to you how you wish to use it. Depending on it and assuming HTTPS is incorect.

    Reply


      Navarr Thursday, July 23, 2015 at 17:22 (permalink)

      No.. no, this is a PHP problem if that header is overwriting $_SERVER[‘HTTPS’].

      per the docs:

      > ‘HTTPS’
      > Set to a non-empty value if the script was queried through the HTTPS protocol.

      Reply


        Mattias Geniar Thursday, July 23, 2015 at 20:30 (permalink)

        Well, to be fair, PHP is inheriting the browser request headers but is indeed prefixing them with HTTP_*.

        However, some code translates HTTP_HTTPS to HTTPS and merges/overwrites them in the $_SERVER array. Not exactly a fault of PHP, the language, more of the framework processing all those values (which is usually put through some kind of validator to catch malicious input).

        Reply


          Olivier Thursday, July 23, 2015 at 22:56 (permalink)

          @Tim and you are both trolls who doesn’t even know PHP. Why do you spread lies?
          Go back using your “perfect” language which doesn’t have a single problem.

          The only websites affected by the problem explained in this article are websites horribly coded. I can’t understand a reason to check for the “HTTP_HTTPS” header.

          Reply


            Tim Friday, July 24, 2015 at 09:03 (permalink)

            Pointing out an obvious flaw doesn’t make one a troll. The fact of the matter is that there are plenty of ways to handle this better, and plenty of platforms that do. It’s just that PHP has an inclination to select the suboptimal approach.

            Reply


              Olivier Friday, July 24, 2015 at 15:13 (permalink)

              Why don’t you want to understand the problem isn’t the PHP language but just a specific piece of code from “WooCommerce”.
              If you continue to say this problem is because of the language, you’re just trolling.


              Tim Friday, July 24, 2015 at 20:48 (permalink)

              My point is that having a CGI-style object that contains an amalgamation of various concepts is bound to result in issues like this one. A sanely designed language or framework will just let you call a function or inspect a clearly defined object to get just the HTTP header(s) that you’re looking for. Back when I was a PHP developer, I would run into obscure design decisions like these all the time, and it’s therefore my opinion that the core language is just broken. You don’t have to agree with me, but your gratuitous namecalling isn’t going to help anyone.


      Tim Thursday, July 23, 2015 at 17:27 (permalink)

      You can’t seriously think having an associative array that contains all of, and I quote, “headers, paths, and script locations” is a good idea.

      Reply


Glen Scott Thursday, July 23, 2015 at 17:28 (permalink)

Are you sure $_SERVER['HTTPS'] can be populated from request headers? If it is sent by the browser, I believe it would be set as an HTTP_* index:

$_SERVER['HTTP_HTTPS'] => 1

As per the documentation (http://php.net/manual/en/reserved.variables.server.php):

‘HTTPS’
Set to a non-empty value if the script was queried through the HTTPS protocol.

Reply


Cash Williams Thursday, July 23, 2015 at 17:39 (permalink)

I’ve checked a number of Drupal sites on a variety of hosting platforms, and from what I can tell Drupal is not effected.

Drupal checks for the value of ‘on’ rather then just a bool value of $_SERVER[‘HTTPS’]

 includes/bootstrap.inc
735:  $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';

Reply


Stefan Thursday, July 23, 2015 at 18:39 (permalink)

Hi Guys, here’s the plugin that fixes this for you. http://www.wdc.me/chrome-ssl-fix.zip

Hope it will help you!

Reply


    Paul Friday, July 24, 2015 at 10:34 (permalink)

    Your a star Stefan. Thank you for this. I will buy you a coffee when my customers pays thier bill.

    Reply


    Syl Friday, July 24, 2015 at 11:27 (permalink)

    Thanks!! Excellent!

    Reply


    VlooMan Friday, July 24, 2015 at 11:28 (permalink)

    Nice work, Stefan. Copying the originally released plugin by IshYoBoy.com, not adding any value to it and filling it with a forced donation notice. You are a true hero ;)

    We just released a condition to ensure the “fix” is applied only to the affected browser version and not on all other browsers and scenarios.

    The Original Plugin and its code without unnecessary garbage can be found on GitHub with a detailed explanation:
    https://goo.gl/D54cWv

    Cheers

    Reply


    Steven Friday, July 24, 2015 at 11:43 (permalink)

    Brillient, well worth a beer

    Reply


    Hardy Monday, July 27, 2015 at 14:54 (permalink)

    ¡Thank you all Stefan!, i was breaking my head with this error.

    Reply


Gap Thursday, July 23, 2015 at 19:43 (permalink)

https://www.ssllabs.com/ssltest/analyze.html?d=ma.ttias.be

You should enable
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)

and disable
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x6b)
TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39)

Reply


    Olivier Thursday, July 23, 2015 at 22:50 (permalink)

    @Gap: do you work for NSA and try to spy on users more easily?
    Why do you suggest enabling crappy ciphers and disabling good ones?

    The only important thing to fix, is to generate a bigger Diffie-Hellman parameter.
    Well, OK, removing “TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39)” doesn’t hurt.

    Reply


    Mattias Geniar Friday, July 24, 2015 at 12:16 (permalink)

    Hi Gap,

    Thanks for the heads-up, I’ve updated my SSL configs and I’m back to an A-rating, instead of the B.

    Much appreciated!

    Reply


Trouble Thursday, July 23, 2015 at 19:47 (permalink)

I don’t know much about web stuff, but as far as I can tell, “beta” means you definitely shouldn’t expect them to work? (Compare with “release”, which seems to mean “probably won’t catch fire”).

Reply


    Mattias Geniar Thursday, July 23, 2015 at 20:25 (permalink)

    I referenced a slightly older article in my post, but version 44 of Chrome is what’s in use by pretty much everyone by know. It’s the release channel, the version everyone that has auto-updated enabled is on by now (release > 24 hours ago).

    Reply


Lucinda Brown Friday, July 24, 2015 at 05:12 (permalink)

Stefan’s plugin worked. Thank you so much. I bought you a beer.

Reply


    VlooMan Friday, July 24, 2015 at 11:24 (permalink)

    Stefan’s plugin is basically a rude copy of the originally released plugin by IshYoBoy.com filled with a forced donation notice. He didn’t even try to make it better.

    We just released a condition to ensure the “fix” is applied only to the affected pages.

    The Original Plugin and its code without unnecessary garbage can be found on GitHub with a detailed explanation:
    https://goo.gl/D54cWv

    Cheers

    Reply


Kelvin Friday, July 24, 2015 at 09:54 (permalink)

How did you get the plugin to work!?… One needs to be able to sign in to WordPress to enable it – and the problem is that one cannot sign in as Google Chrome redirects to https and therefore the error message appears:
SSL connection error
ERR_SSL_PROTOCOL_ERROR
Hide details
Unable to make a secure connection to the server. This may be a problem with the server, or it may be requiring a client authentication certificate that you don’t have.

Reply


Moreno Friday, July 24, 2015 at 11:19 (permalink)

Thanks for the post, I have been contacting my hosting provider for my site which seemed to intermittently break, and they eventually pointed me here.

It would interesting to know if there’s a fix for WordPress that can be applied to patch the problem…

Reply


    Simon Archer Friday, July 24, 2015 at 11:34 (permalink)

    I think you just need to upgrade your version of WooCommere. Version 2.3.13 removes the offending code. You can also add the line $_SERVER[‘HTTPS’] = ”; to your wp-config.php if you don’t have SSL enabled on your server.

    Reply


VlooMan Friday, July 24, 2015 at 11:23 (permalink)

Stefan’s plugin is basically a rude copy of the originally released plugin by IshYoBoy.com filled with a forced donation notice. He didn’t even try to make it better.

We just released a condition to ensure the “FIX” is applied only to the affected pages.

The Original Plugin and its code without unnecessary garbage can be found on GitHub with a detailed explanation:
https://goo.gl/D54cWv

Cheers

Reply


Simon Archer Friday, July 24, 2015 at 11:30 (permalink)

I came across this myself. Is it actually a Chrome bug? It’s only affecting the $_SERVER[‘HTTP_HTTPS’] variable, and not the HTTPS one. Is Chrome effectively querying if there’s SSL enabled so it can redirect there if so?

I’m totally not clear on what the HTTP_HTTPS variable is for, but given it’s a different value from HTTPS, surely it serves a different purpose? This is bad form of the WooCommerce guys to add such a ‘fix’ without fully understanding what they’re fixing.

Reply


David Morles Friday, July 24, 2015 at 12:02 (permalink)

Thank you for the information.

I solved this upgrading WooCommerce to version 2.3.13.

Reply


Rahul Lohakare Friday, July 24, 2015 at 14:23 (permalink)

Hi All,

I have a solution for this problem. If you are wordpress user and if you have used woocommerse plugin then this problem will occur.
To solve this problem just go to wp-content\plugins\woocommerce\woocommerce.php and comment this

if ( ! isset( $_SERVER['HTTPS'] ) && ! empty( $_SERVER['HTTP_HTTPS'] ) ) {
    $_SERVER['HTTPS'] = $_SERVER['HTTP_HTTPS'];
}

around line 473.

Here you go….

Reply


    pablo Friday, July 24, 2015 at 19:36 (permalink)

    Man you saved my life
    Kudos for the discovery

    Reply


mainoko Friday, July 24, 2015 at 16:31 (permalink)

Ver.2.3.8 use
a lot of arrangement
can not woocommerce plugin update…:(

Want a list of PHP should be replaced

Reply


Rob W. Friday, July 24, 2015 at 18:01 (permalink)

Thanks for posting this.
I couldn’t enter one of my websites and started stressing (I was using Chrome).

The quickest solution I found (without having to play with the PHP code) was to open up SAFARI, enter the admin section of the site and update the WooCommerce and any other plugins there.

Cheers for letting me know it wasn’t just my site with problems.
Now I can let out a deep sigh of relief.

Reply


Andrew Revinsky Friday, July 24, 2015 at 19:15 (permalink)

@Mattias, is it possible for you to mark this post with a timestamp? It was not hard to determine the news is fresh.. Yet, it would be helpful.

Thank you for the article!!

Reply


dario Friday, July 24, 2015 at 20:38 (permalink)

Did they fix it? I updated one of my Woocommerce sites, and before i could updted the rest, they started working again. Weird?

Reply


Otto Friday, July 24, 2015 at 21:03 (permalink)

Ultimately, this is a Chrome misfeature in many ways. The thing is that different servers and sets of configurations behave differently, and so PHP apps that have to be portable have little tricks like these to handle output correctly.

The way it is supposed to be is that PHP will set $_SERVER[‘HTTPS’] to “on” if the incoming request is an https request. Simple.

However, a lot of people don’t actually run https directly on the server. They use a proxy server to do all the SSL bits. So while the site is actually serving over plain http, all the generated links still need to be https, because that’s how the browser is talking to the proxy server. This is not an uncommon setup, even CloudFlare can do this for you with their SSL offering.

Any header sent by a request to a server gets HTTP_ prefixed in front of it and passed in through the $_SERVER variable. Historically, proxy servers that are receiving https connections pass that along that fact in their request to the server. This header has invariably simply been called “HTTPS”, for obvious reasons.

So, the working standard is that if you receive a request header with HTTPS, then that’s a proxy server telling you that it’s relaying your response, whatever the hell it may be, back to the browser over an HTTPS connection. So, you, the webapp, should generate https: links in your content. The way this is detected is simple: $_SERVER[‘HTTP_HTTPS’].

Now, Chrome comes along and implements this without actually looking at all the possible scenarios. When Chrome, a browser, sets an HTTPS: 1 request header, then it’s triggering a lot of that same code. So, the PHP apps dutifully do what they have always done, and generate https: links in their output. Naturally, if your server doesn’t actually do https or have an SSL-certificate, then this is a problem. Site broken. Thanks Chrome.

It’s a misfeature that should not have made it into Chrome. Yes, it can be fixed by the various webapps in question, but this has been the standard case for years already. There is a notable difference for a webapp to be told that it’s actually communicating on an HTTPS connection vs. whether it is actually on a normal HTTP connection going through an HTTPS proxy. The HTTP_HTTPS variable has been used for this by basically everybody for many moons. If you set up an nginx ssl-proxy in the documented ways, this is how you do it.

So Chrome breaking that defacto situation is definitely Chrome’s fault. Quick fixes can be made by webapps, but the bottom line is that they should not have to do so. Chrome needs to stop doing that.

Reply


Waffleman Friday, July 24, 2015 at 21:12 (permalink)

Thank you very much for this, Mattias.

You’re a real lifesaver!

Cheers.

Reply


Rodrigo Garcia Saturday, July 25, 2015 at 00:27 (permalink)

Just to be clear, in my installation of Woo, I have the following:

if ( ! isset( $_SERVER['HTTPS'] ) ) {
       if ( ! empty( $_SERVER['HTTP_HTTPS'] ) ) {
               $_SERVER['HTTPS'] = $_SERVER['HTTP_HTTPS'];
       } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {
               $_SERVER['HTTPS'] = '1';
       }
}

I understand that we have to comment out the if statement. Im guessing we should then address the left over elseif statement. PHP will fail otherwise.

leaving the following uncommented and slightly modified with if instead of elseif:

if ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' ) {
        $_SERVER['HTTPS'] = '1';       
}

Reply


jay Saturday, July 25, 2015 at 02:52 (permalink)

Anything using the https() function of perl’s CGI.pm is also affected
https://metacpan.org/source/LEEJO/CGI-4.21/lib/CGI.pm#L3127

Reply


Mike Gill Tuesday, August 11, 2015 at 01:16 (permalink)

The issue is still happening on devices where the certificate is not installed or when accessing a device over https like a firewall or appliance. For example if I try to access a Fortinet firewall internal to a network that the cert is not installed by IP address it returns the same net::ERR_INSECURE_RESPONSE error when trying to load the .js pages. This make managing the firewall in chrome IMPOSSIBLE unless you have the cert installed and access it by name. A good deal of companies do not always purchase certs for internal device management or install a cert server. So in trying to manage any of these devices in chrome is hit or miss, sometimes it works for a few minutes then crashes with the “sad face” or sometimes it fails as soon as you log into the device.

Reply


rlamrod Wednesday, February 8, 2017 at 07:18 (permalink)

How can resolve in java application.

I have an application that receives HTTP/HTTPs requests on Apache Web Server and passes it on to tomcat.

Recently, I’ve been facing problems with sessions and redirection loops on Google Chrome and Firefox browsers after some user logs out the expiration of sessions. I managed to trace down the problem and found that the browser is not clearing the JSESSIONID cookie. After manually clearing that, it starts to work again.

I have checked the log files of Apache Web Server and I found this:

172.16.254.157 – – [06/Feb/2017:05:23:27 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:29 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:29 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:29 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:30 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:30 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:30 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:31 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:31 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:31 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:32 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:32 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:32 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:34 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:34 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:34 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:35 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
172.16.254.157 – – [06/Feb/2017:05:23:35 -0600] “GET /preview/login.admin HTTP/1.1” 302 20
122.176.45.206 – – [06/Feb/2017:05:24:33 -0600] “GET /preview/login.admin HTTP/1.1” 302 20

Are the internet standards changing? Nonetheless, how can I fix this?

Reply


Leave a Reply

Your email address will not be published. Required fields are marked *

Inbound links