PHP session HTTP to HTTPS problem

2019-01-24 05:32发布

问题:

I have a (HTTPS) login.php page which remains HTTPS (ie once user logged in goes to account dashboard). Now the problem is say the user whilst logged on to the secure dashboard clicks onto a non-sensitive page like (HTTP) about-us.php page, the session is not transmitted over HTTP as I have session.cookie_secure=1, meaning the user appears logged out on HTTP pages.

However when the user goes back to dashboard page or any sensitive account page I have been told he should still be logged in (ie from HTTP back to HTTPS)? However this is not the case and he appears logged out on the HTTPS connection too?

I believe I am missing something which is causing this problem. Here is my code:

This is PHP header file which is called to start session on login.php page:

session_start();
session_regenerate_id(true); /*avoid session fixation attempt*/

/*Create and check how long session has been started (over 5 mins) regenerate id - avoid session hijack*/
if(!isset($_SESSION['CREATED'])) 
{
    $_SESSION['CREATED'] = time();/*time created session, ie from login/contact advertiser/email_confirm only ways for new session to start*/
} 
elseif(time() - $_SESSION['CREATED'] > 300) 
{
    /*session started more than 5 mins(300 secs) ago*/
    session_regenerate_id(true); /*change session ID for the current session and invalidate old session ID*/
    $_SESSION['CREATED'] = time(); /*update creation time*/
}

/*Check if user is logged in*/
if(!isset($_SESSION['loggedin']))
{
    $_SESSION['loggedin']=1;/*used to track if user is logged in on pages*/
}

/*if return false browser supports standard ob_start();*/
if(ob_start("ob_gzhandler")){ob_start();}

This is PHP header file required on every page to check if session initiated already:

session_start(); 

$session_errors=0;/* if>0 user not logged in*/

/*check if session is already initiated*/
if(isset($_SESSION['CREATED'])) 
{
    if(time() - $_SESSION['CREATED'] > 300) 
    {
        /*session started more than 5 mins(300 secs) ago*/
        session_regenerate_id(true); /*change session ID for the current session and invalidate old session ID*/
        $_SESSION['CREATED'] = time(); /*update creation time*/
    }
}
elseif(!isset($_SESSION['CREATED'])){$session_errors++;}/*user not logged in*/

/*Check if user is logged in*/
if(!isset($_SESSION['loggedin'])){$session_errors++;}/*user not logged in*/

if(ob_start("ob_gzhandler")){ob_start();}

Also if any use this is the code to turn HTTPS of on non-sensitive pages such as about-us.php

if ($_SERVER['SERVER_PORT']!=80)
{
$url = "http://". $_SERVER['SERVER_NAME'] . ":80".$_SERVER['REQUEST_URI'];
header("Location: $url");
}

My php.ini file cookie settings

session.cookie_secure=1
session.cookie_httponly=1
session.use_only_cookies=1
session.cookie_lifetime = 0
session.save_path = /tmp
session.save_handler = files

回答1:

Answered to help people who might stumble across this

As the the answer at Session lost when switching from HTTP to HTTPS in PHP has concluded, since you are using session.cookie_secure = 1 the cookie that contains the session ID is not transferred when the connection switches from HTTPS to HTTP. At HTTP connection, when you session_start(), PHP creates a new session id, which replaces the previous session id.

The answer also suggests a solution, pass the session id using query string, which is then picked up by the page. This smells of bad of security flaw. Don't forget the reason why we used HTTPS in the first place!

So the solution I suggest to you is that you redirect all http request to https counterparts. Use HTTPS for everything in your site, from css, images, to mundane static html pages. This is actually something that every application that is serious about security does. For example, visiting github page using HTTP will return:

HTTP/1.1 301 Moved Permanently
Server: nginx/0.7.67
Date: Sun, 08 May 2011 15:43:01 GMT
Content-Type: text/html
Content-Length: 185
Connection: close
Location: https://github.com/

<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/0.7.67</center>
</body>
</html>

Remember why you used HTTPS in the first place, if you want to be totally secure, use HTTPS for everything.

Detect if the request is HTTPS or not (See this question) at bootstrap.

If the request is HTTP, either redirect all requests to HTTPS home page, or you can try parsing $_SERVER['REQUEST_URI'] and redirecting HTTP request to their HTTPS counterpart using parse_url and http_build_url.


Second Alternative Solution

If you really really don't want to use HTTPS for everything, then don't session_start() on pages that are accessed with HTTP. Secure cookies will be retained when you do this.


Third Alternative Solution

The other solution is to try and detect the user by IP addresses and user agent. This is not guaranteed to be accurate, so what I suggest is just use HTTPS for everything. Paypal, for example, always use HTTPS even for mundane static pages.



回答2:

The description by @rickchristie is good, but I think there's a better solution that he doesn't suggest. If you don't always want to use HTTPS (which does make sense sometimes; the about_us page doesn't need to be secure), you can follow the advice on the session_start page and use named sessions to continue a previous session. This is simple to use; just include the session_start calls with

session_name("MySession"); // replace with whatever makes sense
session_start();

on all secure pages.