NoWar11 Posted September 21, 2017 Share Posted September 21, 2017 (edited) i am putting together a basic site with a basic shopping cart, we plan to store sensitive information in the sessions in the future so getting this sorted now is a smart choice. Everything works fine without the custom session handler, for some context the session handling code is below: function decrypt($edata, $password) { $data = base64_decode($edata); $salt = substr($data, 0, 16); $ct = substr($data, 16); $rounds = 3; // depends on key length $data00 = $password.$salt; $hash = array(); $hash[0] = hash('sha256', $data00, true); $result = $hash[0]; for ($i = 1; $i < $rounds; $i++) { $hash[$i] = hash('sha256', $hash[$i - 1].$data00, true); $result .= $hash[$i]; } $key = substr($result, 0, 32); $iv = substr($result, 32,16); return openssl_decrypt($ct, 'AES-256-CBC', $key, true, $iv); } function encrypt($data, $password) { // Set a random salt $salt = openssl_random_pseudo_bytes(16); $salted = ''; $dx = ''; // Salt the key(32) and iv(16) = 48 while (strlen($salted) < 48) { $dx = hash('sha256', $dx.$password.$salt, true); $salted .= $dx; } $key = substr($salted, 0, 32); $iv = substr($salted, 32,16); $encrypted_data = openssl_encrypt($data, 'AES-256-CBC', $key, true, $iv); return base64_encode($salt . $encrypted_data); } class SecureSessionHandler extends SessionHandler { protected $name, $cookie; private $key; public function __construct($key, $name = 'MY_SESSION', $cookie = []) { $this->key = $key; $this->name = $name; $this->cookie = $cookie; $this->cookie += [ 'lifetime' => 0, 'path' => ini_get('session.cookie_path'), 'domain' => ini_get('session.cookie_domain'), 'secure' => isset($_SERVER['HTTPS']), 'httponly' => true ]; $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256- cbc')); $this->setup(); } private function setup() { ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); session_name($this->name); session_set_cookie_params( $this->cookie['lifetime'], $this->cookie['path'], $this->cookie['domain'], $this->cookie['secure'], $this->cookie['httponly'] ); } public function start() { if (session_id() === '') { if (session_start()) { return mt_rand(45, 99) === 55 ? $this->refresh() : true; // 1/5 } } return false; } public function forget() { if (session_id() === '') { return false; } $_SESSION = []; setcookie( $this->name, '', time() - 42000, $this->cookie['path'], $this->cookie['domain'], $this->cookie['secure'], $this->cookie['httponly'] ); return session_destroy(); } public function refresh() { return session_regenerate_id(false); } public function write($id, $data) { $data = encrypt($data, $this->key); return parent::write($id, $data); } public function read($id) { $data = parent::read($id); if (!$data) { return ""; } else { return decrypt($data, $this->key); } } public function isExpired($ttl = 30) { $last = isset($_SESSION['_last_activity']) ? $_SESSION['_last_activity'] : false; if ($last !== false && time() - $last > $ttl * 60) { return true; } $_SESSION['_last_activity'] = time(); return false; } public function isFingerprint() { $hash = md5( $_SERVER['HTTP_USER_AGENT'] . (ip2long($_SERVER['REMOTE_ADDR']) & ip2long('255.255.0.0')) ); if (isset($_SESSION['_fingerprint'])) { return $_SESSION['_fingerprint'] === $hash; } $_SESSION['_fingerprint'] = $hash; return true; } public function isValid() { return ! $this->isExpired() && $this->isFingerprint(); } public function get($name) { $parsed = explode('.', $name); $result = $_SESSION; while ($parsed) { $next = array_shift($parsed); if (isset($result[$next])) { $result = $result[$next]; } else { return null; } } return $result; } public function put($name, $value) { $parsed = explode('.', $name); $session =& $_SESSION; while (count($parsed) > 1) { $next = array_shift($parsed); if ( ! isset($session[$next]) || ! is_array($session[$next])) { $session[$next] = []; } $session =& $session[$next]; } $session[array_shift($parsed)] = $value; } } $key = file_get_contents('./key.pem', FILE_USE_INCLUDE_PATH); $session = new SecureSessionHandler($key); ini_set('session.save_handler', 'files'); session_set_save_handler($session, true); session_save_path(__DIR__ . '/sessions'); $session->start('cheese'); if ( ! $session->isValid(5)) { $session->destroy('cheese'); } There is the full session handler and encryption/decryption method, everything works fine overall but the problem is when going through my shopping cart code and changing all the $_SESSION variables into the $session->get objects, i managed to replace every $_SESSION then the last one i need to change throws a fatal error: changing " $_SESSION['cart_contents'] = $this->cart_contents;" to "$session->get('cart_contents') = $this->cart_contents;" results in: Fatal error: Can't use method return value in write context in C:\xampp\htdocs\teck\Cart.php on line 375 The rest of the important code is mentioned below, i made sure nothing was left out, its better to have a long post with all details than getting flamed for a small post with a question missing all details lol. The page that you can adjust the quantity on is viewcart.php, the code thats important from this page is below: <input type="number" class="form-control text-center" value="<?php echo $item["qty"]; ?>" onchange="updateCartItem(this, '<?php echo $item["rowid"]; ?>')"> and the script on the same page (viewcart.php) that throws the alert is below: <script> function updateCartItem(obj,id){ $.get("cartAction.php", {action:"updateCartItem", id:id, qty:obj.value}, function(data){ if(data == 'ok'){ location.reload(); }else{ alert('Cart update failed, please try again.'); } }); } </script> Now finally cartaction.php, the code for the action "updatecartitem" is below: }elseif($_REQUEST['action'] == 'updateCartItem' && !empty($_REQUEST['id'])) { $itemData = array( 'rowid' => $_REQUEST['id'], 'qty' => $_REQUEST['qty'] ); $updateItem = $cart->update($itemData); echo $updateItem?'ok':'err';die; } The update function thats in cart.php: public function update($item = array()){ if (!is_array($item) OR count($item) === 0){ return FALSE; }else{ if (!isset($item['rowid'], $this->cart_contents[$item['rowid']])){ return FALSE; }else{ // prep the quantity if(isset($item['qty'])){ $item['qty'] = filter_var($item['qty'], FILTER_VALIDATE_INT); // remove the item from the cart, if quantity is zero if ($item['qty'] == 0){ unset($this->cart_contents[$item['rowid']]); return TRUE; } } // find updatable keys $keys = array_intersect(array_keys($this- >cart_contents[$item['rowid']]), array_keys($item)); // prep the price if(isset($item['price'])){ $item['price'] = filter_var($item['price'], FILTER_VALIDATE_FLOAT); } // product id & name shouldn't be changed foreach(array_diff($keys, array('id', 'name')) as $key){ $this->cart_contents[$item['rowid']][$key] = $item[$key]; } // save cart data $this->save_cart(); return TRUE; } } } And last but not least the save_cart function in cart.php also: protected function save_cart(){ $this->cart_contents['total_items'] = $this->cart_contents['cart_total'] = 0; foreach ($this->cart_contents as $key => $val){ // make sure the array contains the proper indexes if(!is_array($val) OR !isset($val['price'], $val['qty'])){ continue; } $this->cart_contents['cart_total'] += ($val['price'] * $val['qty']); $this->cart_contents['total_items'] += $val['qty']; $this->cart_contents[$key]['subtotal'] = ($this->cart_contents[$key] ['price'] * $this->cart_contents[$key]['qty']); } global $key; global $session; // if cart empty, delete it from the session if(count($this->cart_contents) <= 2){ $session->forget(); return FALSE; }else{ // original code below $_SESSION['cart_contents'] = $this->cart_contents; // code below is what i thought would work if swapped with the line above but it throws the // fatal error as stated at the start of the post. $session->get('cart_contents') = $this->cart_contents; return TRUE; } } The cart code above is in the same file as the session handler, A bit more of an explanation, with " $_SESSION['cart_contents'] = $this->cart_contents;" (the original code), i press the button +1 to add extra item into cart, the text field increases 1, and the error alert pops up saying "Cart update failed, please try again", i click ok and the alert goes away and nothing happens, the item qty in the text box stays updated but the actual real cart qty doesnt update until i refresh the page or click a link. Without the custom session handler in place and just the original "$_SESSION" in place of "$session->" this doesnt happen, everything works perfectly (and of course all the other session variables changed back to "$_SESSION" aswel). Is it a must that i change this $_SESSION['cart_contents'] variable to $session->get('cart_contents')? I should be able to change that alert script to update the cart properly without throwing the alert but will my custom session handler work flawlessly if i leave that one session variable and dont replace it with the session handler class? Im very sorry for the long post but i wanted to make sure everything is as clear as possible for other people to also understand if they are having similar problems. The code does seem quite messy from this point of view but if you were to go over the entire site everything would make much more sense. I have been on php for about 2 months max now trying my best to pick up as much as possible so please bear with me, obviously most of the code is not as clean as it should be, but this is production. I have spent the past 3-4 days on this session handler, i really dont want to scrap it and just go back to the standard _session. All & any advice is welcome, constructive criticism will be taken lightly! I really appreciate the time you have taken to go through this, Thanks for your effort! Edited September 22, 2017 by requinix please use [code] tags when posting code Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.