64

When sending the user to a checkout page, they are switched from http://sitename.com to https://sitename.com.

As a result, $_SESSION variables are lost.

The site has a valid SSL certificate which may or may not be of some use.

Hamza Zafeer
  • 2,360
  • 13
  • 30
  • 42
Allan
  • 2,586
  • 5
  • 26
  • 22
  • 1
    Which browser are you using? Are there any subdomains involved, such as "www."? Can you reproduce the issue elsewhere? – Unsigned Nov 16 '11 at 20:04

16 Answers16

71

When you switch between the HTTP and HTTPS services on the same server, your HTTP session ID is not being passed to the HTTPS session. You can set it by passing the session ID from the HTTP page to the HTTPS page in one of three possible ways:

From PHP: session_start:

session_start() creates a session or resumes the current one based on the current session id that's being passed via a request, such as GET, POST, or a cookie

When you are using sessions, you will normally start your script with session_start(). If the browser has a session ID cookie set, session_start() will use that session ID. If the browser does not have a session ID cookie set, session_start() will create a new one.

If the session ID is not set(in your example, the browser is creating a new session ID cookie for the HTTPS session), you can set it using the session_id() function. session_id() also conveniently returns the session ID as a string. So

...

$currentSessionID = session_id();

...

sets the $currentSessionID variable equal to the current session ID, and

...

session_id($aSessionID);

...

sets the sessionID cookie in the browser to $aSessionID. from PHP: session_id

Here's an example with two scripts. One is accessed via HTTP and the other is accessed via HTTPS. They must be on the same server to maintain session data.

Script 1(HTTP):

<?php

// This script will create a session and display a link to your secure server address
// to transfer your session ID. In this example, the secure page to receive the session
// ID is located at http://www.yoursite.com/safePages/securePage.php

// Start a session using the current session ID stored in a cookie, or create
// a new session if none is set.
session_start();

$currentSessionID = session_id();

// Set a variable that will be retrieved with the HTTPS script.
$_SESSION['testvariable'] = 'It worked';

// $secureServerDomain is the domain of your secure server
$secureServerDomain = 'www.yoursite.com';

// $securePagePath is the path to the page that will receive and set the session ID.
$securePagePath = '/safePages/securePage.php'

echo '<a href="https://' . $secureServerDomain . $securePagePath . '?session="' . $currentSessionID . '">Click here to transfer your session to the secure server</a>';

?>

Script 2(HTTPS):

<?php

// Retrieve the session ID as passed via the GET method.
$currentSessionID = $_GET['session'];

// Set a cookie for the session ID.
session_id($currentSessionID);

// Start a session.
session_start();

// Test retrieval of variable set when using HTTP.
if (!empty($_SESSION['testvariable'])) {
      echo $_SESSION['testvariable'];
} else {
      echo 'It did not work.';
}

?>

For this to work the HTTP and HTTPS servers must use the same session data storage substrate (i.e. for the default files handler, run on the same physical machine with the same php.ini). There are some security flaws here, so I would not use this code to transfer sensitive information. It is just meant as a workable example.

When I ran into this problem before, I came up with the above as a quick fix, but I just remembered the original cause of the problem. I was going from http://www.example.com/page.php to https://example.com/page.php (notice the lack of "www"). Make sure that http://www.example.com/page.php will link to https://www.example.com/page.php and http://example.com will link to https://example.com/page.php.

PS, I didn't actually run these scripts so there may be a typo or two that prevents them from running properly as is.

Nate
  • 26,164
  • 34
  • 130
  • 214
Jacob
  • 711
  • 4
  • 4
  • 2
    Using additionally a salted hash would secure this procedure. – Gumbo Jan 14 '09 at 16:47
  • I can't see the nature of the security issues here - know any resources that describe them? – bcoughlan Jul 09 '10 at 10:36
  • 9
    Security question: someone could continually hit your site and guess session_id variables, eventually hijacking someone else's session. I don't see how a salted hash would work, as that's a one-way operation; you'd salted hash a password, and do the same for a password attempt, but in this case, that wouldn't quite work; you need the original value. – Dean J Mar 20 '11 at 01:25
  • 2
    @DeanJ - You generate a checksum server-side and parse it along the URL as a parameter. For example you could generate the hash like: `$hash = md5($currentSessionID.'somesecretsalt')`, and then the url should look like ?session='.$currentSessionID.'&hash='.$hash. On the target site, you would then check if md5($_GET['session'].'somesecretsalt') == $_GET['hash']. That way you can tell, that the session_id wasn't just made up. The session id HAS to match the server-generated hash (which is almost impossible to crack when using a long salt value). – Trolley Mar 27 '13 at 14:07
  • +1 for noting the issue "going from http://www.example.com/page.php to https://example.com/page.php" - this was an issue for me as well. I solved it by modifying my .htaccess file to prevent the www entirely: RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC] RewriteRule ^.*$ http://example.com%{REQUEST_URI} [R=301,L] – JSP64 Jun 13 '14 at 17:18
  • Could you also use the same technique for two websites on a different port e.g. mywebsite.com & mywebsite.com:80/index.html ? – StackOverQuestions Jan 11 '15 at 21:49
16

Sounds like the session cookie is set to be secure. Cookies have a "secure" flag which, if set to true, means that the cookie won't be sent to non-https sites. PHP is probably using that for its session cookies. You can change this with the session_set_cookie_params function, or with the session.cookie_secure setting in php.ini.

fooquency
  • 1,575
  • 3
  • 16
  • 29
JW.
  • 50,691
  • 36
  • 115
  • 143
  • The OP specified that the cookie was lost when transferring **to** an encrypted connection. The *secure* flag does not apply here. – Unsigned Nov 16 '11 at 20:03
  • 1
    @Unsigned - If you switch to a secure page and tell the page to use secure session cookies only, it has to create a new secure cookie and can't use the existing unsecure one. – martinstoeckli Nov 16 '11 at 21:36
  • @martinstoeckli - True. However, the default setting is `off`. If it was `on`, cookies would not have been sent to the HTTP page, meaning that the cookie should never have existed there either. Ergo, the cookie is not secure. – Unsigned Nov 16 '11 at 23:23
  • @JW was right, toke me a while to figure out, 'session_start (); setcookie(session_name(), session_id(), NULL, NULL, NULL, 0);' sets a cookie for both http, and https preventing to lose the session. – ontananza Aug 09 '12 at 17:42
12

We had this issue as well. It turned out to be because we were using the suhosin patch on our PHP installation. We fix it by setting suhosin.session.cryptdocroot = Off in /etc/php.d/suhosin.ini.

For the suhosin manual about suhosin.session.cryptdocroot see http://www.hardened-php.net/suhosin/configuration.html#suhosin.session.cryptdocroot.

We originally found the fix from this blog post: http://www.yireo.com/blog/general-news/315-switch-between-http-and-https-looses-php-session.

Tom
  • 14,041
  • 16
  • 64
  • 80
6

The following solution assumes the secure and non-secure servers have access to the same backend services (cache, database store, etc).

We had to deal with this same issue when sending a user to our checkout flow when they were done shopping. To solve this, we put in place a caching layer and cached all the pertinent data. For example, we would glean the product ids and user id from the session values, serialize them, create a hash, and finally store the session data within cache using the hash as the key. We would then redirect user to the secure site with the hash in the url.

When the user ended up on the secure site we would attempt to pull the data out of cache based on the hash. Then with the user id and product ids we could load all the pricing and description data out of the database and present to the user for final checkout review.

There is an inherit risk in that the cache data is volatile, but we have never had any issues with it as the redirect happens quickly.

Mike Purcell
  • 19,847
  • 10
  • 52
  • 89
  • our solutions are similar with one difference. I suggested storing in a database and you implied a cache. how can he create a common memory to use between different sessions? – Uğur Gümüşhan Nov 15 '11 at 23:16
  • @UğurGümüşhan: Nice. Database solution will be slower compared to cache (relative to traffic), but is persistent. As mentioned in my post, we use the hash we generate on the send side (non-ssl), to look up the data on the receive side (ssl). – Mike Purcell Nov 15 '11 at 23:26
  • How does this differ to sending the session ID in the URL (as mentioned in the other answers)? Apart from a hash perhaps being longer and harder to guess? – MrWhite Apr 05 '13 at 13:14
  • 1
    @w3d: Not sure what you mean. You can use the session ID if you prefer, or any character combination. The point is, you are caching the session data on one side, and redirect the user with a lookup key to the other side, so you can create a new session with the old session's data. – Mike Purcell Apr 05 '13 at 16:44
1

Looks like your session cookie is created with the secure flag, but there's something with the url of your checkout page due to which the session cookie isnt being passed over.

Or probably, your session cookie isnt secure - just that the url of the checkout page is different enough (http://mysite.com vs http://www.mysite.com) that the browser isnt sending the cookie.

If you'd like to read more on flipping over from http to https and vice versa - do take a look at at my writeup on selective ssl :-)

1

You can't pass session values between different domains. You must use http post-get or a database to pass your values. For security, you can concat all your values in a string and use

sha1($string)

and post it alongside your values and calculate the sha1 for the values other page gets, then compare the hashes.

Post method on different domains cause browsers to show a security message, so don't use that.

Using url for get method is not safe, you would need to ask for a password on the redirected page for allowing the get parameters in your system.

Do not use cookies if you need security.

The way I am suggesting is, save the values in a database and generate a key, then make your redirection link using your key, forward the users page with a get parameter which has the key, then the page user is redirected to gets that key, fetches the data and removes the key. you can generate a key with sha1

PAGE 1---
$key=sha1($allvalsconcat);
//insert your session values to a database also the key in a column
header("Location: page2.php?key=".$key);

PAGE 2---
// select from database where key=$_GET["key"];
// delete from table where key=$key

this is pretty secure.

the things that can happen: a script entering random values for the parameter "key" to make your website load the data into your memory?

This is not going to happen because you delete the entry after using it. Some common misconception is that get values are unsafe and should always be avoided.

you can set the table engine type to "memory" in mysql if you want performance perfection.

Uğur Gümüşhan
  • 2,455
  • 4
  • 34
  • 62
1

I'd recommend, in addition to what most have stated here about transferring encrypted information, looking at it the same as if you were transferring sensitive information through a 3rd party API. How do you know someone isn't spoofing the request? There are many protocols for truly confirming the authenticity of the request, depending on how sensitive your setup is. You're opening yourself up to accounts being compromised if you're not careful.

Even though it's on the same server, consider this:

When someone is following the link, form action, etc. that passes over the encrypted key, what would prevent someone from sniffing it BEFORE they get to the secured version of your site? If I were at a public WIFI spot, that wouldn't be too far-fetched. I could pretend to be your site, reroute requests to my own laptop, grab the token, and redirect the visitor back to where they came. They would assume it was a glitch, and would have no idea. Now I can login as them, and possibly go buy $10,000 worth of stuff with their credit card on file and ship it somewhere else. The degree of caution you take here should match the degree of sensitivity.

Also, make sure you expire your tokens (one use only, after X number of seconds, etc), but I would also consider using the Post-Redirect-Get pattern on both ends, i.e.:

Don't show the direct link on a page or in the form of the unsecured site, but show a link that will then redirect on the backend (and handle all the token/encryption stuff). When you arrive at the secured version, do the same (don't leave a "?token=asdfjalksfjla" parameter just sitting there in the URL; redirect it).

So, formal token-based systems were designed to solve this very problem, but implementing OAuth just for this might be overkill. Spend some time planning the potential vulnerabilities before executing. Just because it'd be really hard to guess the token doesn't mean it's impossible (or there couldn't be collisions, etc.), so plan accordingly.

You also might need a more sophisticated session management system than PHP's built-in handlers. I don't know if you can force PHP to continue a session across multiple visits (switching protocols is treated that way).

landons
  • 9,502
  • 3
  • 33
  • 46
1

Think about using HTTPS for all pages, that's the easiest way to avoid this problem and it will improve the security of your site.

If SSL for all pages is not an option to you, then you could use this approach: Switching between HTTP and HTTPS pages with secure session-cookie. The idea behind is, that you leave the session cookie unsecure (and therefore available to HTTP and HTTPS pages), but have a second secure cookie to handle the authentication. It's a good way to separate the two concerns "maintaining the session" and "authentication".

Community
  • 1
  • 1
martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
1

You can manage session between HTTP to HTTPS or HTTPS to HTTP:

  1. Transmit session ID between page using GET

  2. POST session ID by POST

  3. Use files to save sessions

  4. Use Cookies for sessions

  5. Use database to save session

Below example can be used to transmit using GET….

File : http.php ……………

<?php

session_start();

$sessionID = session_id();

$_SESSION['demo'] = ‘Demo session between HTTP HTTPS’;

echo ‘<a href=”https://www.svnlabs.com/https.php?session=’.$sessionID.’”>Demo session from HTTP to HTTPS</a>’;

?>

File: https.php ……………

<?php

$sessionID = $_GET['session'];

session_id($sessionID);

session_start();

if (!empty($_SESSION['demo'])) {
echo $_SESSION['svnlabs'];
} else {
echo ‘Demo session failed’;
}

?>

IE7 : This page contains both secure and nonsecure items

You have to use relative path for all static resource on page like css, js, images, flash etc. to avoid IE message secure and nonsecure items…

IE Message IE Message

Rohan Patil
  • 2,328
  • 1
  • 16
  • 23
  • 2
    Passing the session id in the URL will leave you open to all kind of attacks. The reason to transfer the session id only with a cookie is, that you can force the sending of the cookie to be encrypted only. – martinstoeckli Nov 17 '11 at 09:57
0

Do you have a dedicated IP? on some shared environments the https and the http are routed through different servers, so switching actually loses access to the cookies since they're on different domains.

solutions would be: dedicated ip

forcing https on all pages at all times

0

I had a similar problem, however, this solution was good for me, perhaps will help others in the future

add this in your php.ini

suhosin.session.cryptdocroot = Off

suhosin.cookie.cryptdocroot = Off

0

I have got a solution by this..Try it.

$_SESSION['test'] = 'test';
session_regenerate_id(true);

header("Location: /");// the header must be sent before session close
session_write_close(); // here you could also use exit();
UWU_SANDUN
  • 1,123
  • 13
  • 19
0

This may not be possible since the cookie seems to be getting lost. The browser you're using must think it's for a completely different domain.

What browser are you using specifically?

Allain Lalonde
  • 91,574
  • 70
  • 187
  • 238
0

By default I would expect a browser to treat connections to http and https as completely different sessions. Although the convention is that http://someUrl/ and https://someUrl/ will point to the same page it isn't guaranteed. You could have completely different sites running on port 80 (http) and port 443 (https).

I don't know PHP, but generally I would not expect session variables to be freely available between secure and non-secure sessions e.g. I wouldn't expect the credit card number from my last checkout to be available to all the subsequent insecure pages I visit.

Forgive the non-authoritative answer but I thought I'd chuck in my 2c since there aren't many answers.

codybartfast
  • 7,323
  • 3
  • 21
  • 25
  • I wouldn't expect that at all. Problem *may* be due to masking issues with paths / secure flags. "Although the convention is that..." - WTF? No, that is a truly dangerous practice. – symcbean Nov 17 '11 at 12:55
  • @symcbean Just because the both URLs point to same page doesn't mean they're both available. Of course a 'secure' page is only available over https. What you don't see is http//someUrl and https//someUrl pointing to completely different content, but it's perfectly possible. A bank is not going to have any http on a secure server but many sites will bind 80 and 443 to the same server just like the questioner. – codybartfast Nov 18 '11 at 06:41
0

I have had this issue moving from http to https and I was able to solve this issue changing the website URL from http://example.com to https://www.example.com

adding the www to the URL solved this issue for me.

Thanks.

Midz Elwekil
  • 441
  • 4
  • 12
-1

Don't worry this is a normal behavior because HTTPS is meant to be secure and it is doing his part.

Below are some tricks through which you can maintain the session while switching from HTTP to HTTPS.

  1. Transmit session ID between page using GET

  2. POST session ID by POST

  3. Use files to save sessions

  4. Use Cookies for sessions

  5. Use database to save session

Hope you will get something through my reply.