Make WordPress Core

Opened 7 years ago

Last modified 5 years ago

#42481 new defect (bug)

TEST_COOKIE and LOGGED_IN_COOKIE secure flag create issues on non-secure login

Reported by: ravanh's profile RavanH Owned by:
Milestone: Awaiting Review Priority: low
Severity: normal Version: 4.9
Component: Login and Registration Keywords:
Focuses: Cc:

Description

Once a user has accessed the login form over https (possible without a valid ssl license, ignoring the browser warning) the WordPress TEST_COOKIE will have the secure flag set https://core.trac.wordpress.org/browser/trunk/src/wp-login.php#L433

When that user goes back to login over http, this will no longer be possible. The test cookie will be ignored by the browser because of the secure flag.

Without the test cookie, all login attempts will be redirected back to the login form with a warning about cookies not being set by the browser. Most users will not know why this happens and will no longer be able to log in.

The user will have to go back to https, open the developer toolbar, delete the cookie and then back to http. Only then the test cookie will be set again, this time without the secure flag.

A work-around to prevent users from being locked out like this, is to make the test cookie name "http/s aware" with something like this in wp-config.php:

$secure = ( isset($_SERVER['HTTPS']) && 'on' == $_SERVER['HTTPS'] ) ? '_sec' : '';
define( 'TEST_COOKIE', 'wordpress' . $secure . '_test_cookie' );

(using wordpress_sec for secure cookie similar to the auth cookie)

But... the real question is:

Why does the test cookie need the secure flag at all?

There is no sensitive information passed and it's only there to (as the name suggests) test for cookie unaware or blocking browsers. At least as far as I can tell, there would be no possible problem with simply removing this cookies secure flag. This will not affect any sensitive login/session cookies secure flags.

Or am I mistaken? Are there use cases where the browser can be set to accept cookies over https while blocking them over http?

Change History (8)

#1 @RavanH
7 years ago

  • Component changed from General to Login and Registration

#2 @johnbillion
7 years ago

  • Keywords close added
  • Priority changed from normal to low
  • Version trunk deleted

Thanks for the report.

This strikes me as an edge case and something that doesn't really need fixing. It requires a server which responds over HTTPS but which is not configured to serve a particular domain over HTTPS, and a user who manually navigates to an HTTPS login URL, and who then subsequently intentionally ignores the security roadblocks presented by the browser.

If your server is set up to serve HTTPS for the requested domain, WordPress will allow you to log in over HTTPS regardless of the scheme set in the site's URL settings. Therefore, if any one of the three prerequisites above don't apply, then the issue won't occur.

The test cookie is a session cookie. If the user closes their browser, the cookie will be removed.

Regarding the question Why does the test cookie need the secure flag at all?, the answer is that there is no need for it to not use the secure flag when logging in over HTTPS.

#3 @RavanH
7 years ago

Hi @johnbillion it happens also with a valid license (no browser warning) where the admin can be accessed over https but front end is not set to use https by default. Then, on the front end over http, the user cannot log in anymore.

#4 @RavanH
7 years ago

  • Keywords close removed

Hmmm, it seems the issue goes deeper than the test cookie alone. Here https://status301.net/wp-content/uploads/2017/11/login-http.webm is a 1 minute screen cast taken from a multi-site where the primary site (front and admin) is on https but the sub-domain site has both site_url and home set to http.

Video description:

Logged in on the main site on https, from the My Sites page I follow the Visit link of a sub-site on http. This leads me to the front end of the subdomain where I'm not logged in (as expected). I then follow the Log In link in the Meta widget and try to log in. This results in a redirect back to the login form with the fore-mentioned cookie warning.

However, when I then manually type the /wp-admin/ url in the browser, it shows I am logged in after all.

But when I then go to the theme customizer, it will show the login form in the preview pane, this time with an "expired" message. Logging in again will redirect back to the same expired message...

The screen cast stops here but if I use the browser back button, I can get back to the admin without having to log in again. The session has not expired at all!

Hope this demonstrates how annoying this can be, occurring in one singe session without ever having to visit a https page without valid ssl license. Specially lesser gods like sub-site owners that already freak out at the first cookie warning. They might give up there and then, closing the browser. Come back later (session cookie cleared) and be able to log in but then still not be able to use the Theme Customizer or even be logged in on the front-end at the same time (no admin bar).

There really is something wrong in the whole ssl versus non-ssl logic here.

#5 @RavanH
7 years ago

  • Version set to 4.9

Take for instance the LOGGED_IN_COOKIE secure flag handling. In pluggable.php line 833 it sais:

// Front-end cookie is secure when the auth cookie is secure and the site's home URL is forced HTTPS.
$secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME );

Although this seems perfectly logical, there is a problem when on multisite the primary site is on https, but when a new subdomain blog is created it uses http. The new site owner will then visit his new blog but no be logged in on the front end because the logged_in_cookie has the secure flag set when the user logged in on the main site (when creating his/her new account)

This problem is similar to the test_cookie but more persistent because the logged in cookie does not expire after one session. A nasty result of this what can be seen in the second half of the screen cast posted above.

The user will first have to go back to the main site, log out there, then go back to his sub-site, and log back in there... Not very intuitive.

#6 @RavanH
6 years ago

This bug remains a very nasty one even if it would be on 'edge' cases only... And I'm not sure about it being such an edge case actually.

Take this scenario for example: a WP Multisite with subdomains, open for public registration, the main site on domain example.com was created on HTTP but has been moved to HTTPS with a Letsencrypt license. Now new sites that are created from the front end (public) registration will be on HTTP.

But since the main site where registration and first login was done over HTTPS, the new user will soon run into problems:

  • The first, but very minor, issue is he/she needs to do a second login to get into his own fresh new subdomain site admin. Not a big deal, just inconvenient and not as it would be if both primary site and subdomain site are on the same protocol.
  • Then, after being logged in on the admin, the user will appear to be logged out when returning to his site front end (to see changes for example) and have no admin bar or edit links. Returning to the admin can be done by the browser back button or typing /wp-admin/ so this one too is not the most annoying issue.
  • But when he/she tries the theme Customizer, it will fail completely with a "Non-existent changeset UUID" error.
  • And no post Preview, of the soon to be published post...

Specially these last two give a horrible experience for a new user and he/she will likely turn elsewhere to blog.

The only current work-around is either to move the main site back to HTTP or to get an expensive wildcard SSL license (at least as long as Letsencrypt wildcard challenges remain so elusive to meet) ... Is there really no interest in fixing this?

#7 @RavanH
6 years ago

  • Summary changed from Test cookie secure flag prevents non-secure login to TEST_COOKIE and LOGGED_IN_COOKIE secure flag create issues on non-secure login

#8 @RavanH
6 years ago

Update: I created a fairly simple workaround that seems to do the job satisfactory.

The idea is to create separate cookies for SSL and nonSSL requests to simulate how AUTH_COOKIE and SECURE_AUTH_COOKIE are treated. I did this by adding the following lines to wp-config.php

// Treat the cookie domain: strip off any subdomain (including www) if there is one
// so that logged in status carries across different sub-sites and main site after
// logging in on a sub-site, just as it does after logging in on the main site.  
define( 'COOKIE_DOMAIN', implode( ".", array_slice( explode( ".", $_SERVER['HTTP_HOST'] ), -2 ) ) );

// Set up a cookiehash now because we need it next. It may be same as default or anything else.
// Also fixes missing cookiehash bug for sub-network sites in Multi-network!
define( 'COOKIEHASH', md5( 'xxxxxxxxxxxxxxxxxx' ) );

// SSL aware TEST_COOKIE and LOGGED_IN_COOKIE must be set otherwise cookie set on ssl
// will be ignored on non-ssl login. and WordPress sets no hash on mapped domains...
if ( isset($_SERVER['HTTPS']) && 'on' == $_SERVER['HTTPS'] ) {
        define( 'TEST_COOKIE', 'wordpress_sec_test_cookie' );
        define( 'LOGGED_IN_COOKIE', 'wordpress_sec_logged_in_' . COOKIEHASH );
}

This seems to be enough to completely separate secure from non-secure cookies. Once a user is logged in over SSL, cookies with "_sec" in their name will be set with the secure flag. The user will need to do a separate login when on a nonSSL subsite but once that is done, all will work fine because the cookies set at login are with a different (default) name and do not have the secure flag.

Note that the initial COOKIE_DOMAIN define is not strictly needed but has a nice advantage: logging in on a subsite will now behave the same as logging in on the main site. The user will be logged in across the network (as long as on the same protocol!) instead of just the subdomain.

Only thing I'm wondering: would it be better for security to set a different COOKIEHASH for secure versus non-secure cookies? What is that hash for anyway? Does it serve like a public/shared key?

Note: See TracTickets for help on using tickets.