GithubHelp home page GithubHelp logo

stefangabos / zebra_session Goto Github PK

View Code? Open in Web Editor NEW
168.0 18.0 84.0 263 KB

A drop-in replacement for PHP's default session handler which stores session data in a MySQL database, providing better performance, better security and protection against session fixation and session hijacking

Home Page: https://stefangabos.github.io/Zebra_Session/Zebra_Session/Zebra_Session.html

License: Other

PHP 99.75% Batchfile 0.25%
session-handler session-manager php-sessions mysqli php session-fixation mysql-database pdo

zebra_session's Introduction

zebrajs

Zebra Session  Tweet

A drop-in replacement for PHP's default session handler which stores session data in a MySQL database, providing better performance, better security and protection against session fixation and session hijacking

Latest Stable Version Total Downloads Monthly Downloads Daily Downloads License

Session support in PHP consists of a way to preserve information (variables) on subsequent accesses to a website's pages. Unlike cookies, variables are not stored on the user's computer. Instead, only a session identifier is stored in a cookie on the visitor's computer, which is matched up with the actual session data kept on the server, and made available to us through the $_SESSION super-global. Session data is retrieved as soon as we open a session, usually at the beginning of each page.

By default, session data is stored on the server in flat files, separate for each session. The problem with this scenario is that performance degrades proportionally with the number of session files existing in the session directory (depending on the server's operating system's ability to handle directories with numerous files). Another issue is that session files are usually stored in a location that is world readable posing a security concern on shared hosting.

This is where Zebra Session comes in handy - a PHP library that acts as a drop-in replacement for PHP's default session handler, but instead of storing session data in flat files it stores them in a MySQL database, providing better security and better performance.

Zebra Session is also a solution for applications that are scaled across multiple web servers (using a load balancer or a round-robin DNS) where the user's session data needs to be available. Storing sessions in a database makes them available to all of the servers!

Supports "flash data" - session variables which will only be available for the next server request, and which will be automatically deleted afterwards. Typically used for informational or status messages (for example: "data has been successfully updated").

This class is was inspired by John Herren's code from the Trick out your session handler article (now only available on the Internet Archive) and Chris Shiflett's code from his book Essential PHP Security, chapter 8, Shared Hosting, Pg. 78-80.

Zebra Session's code is heavily commented and generates no warnings/errors/notices when PHP's error reporting level is set to E_ALL.

Starting with version 2.0, Zebra Session implements row locks, ensuring that data is correctly handled in a scenario with multiple concurrent AJAX requests.

Citing from Race Conditions with Ajax and PHP Sessions, a great article by Andy Bakun:

When locking is not used, multiple requests (represented in these diagrams as processes P1, P2 and P3) access the session data without any consideration for the other processes and the state of the session data. The running time of the requests are indicated by the height of each process's colored area (the actual run times are unimportant, only the relative start times and durations).

Session access without locking

In the example above, no matter how P2 and P3 change the session data, the only changes that will be reflected in the session are those that P1 made because they were written last. When locking is used, the process can start up, request a lock on the session data before it reads it, and then get a consistent read of the session once it acquires exclusive access to it. In the following diagram, all reads occur after writes:

Session access without locking

The process execution is interleaved, but access to the session data is serialized. The process is waiting for the lock to be released during the period between when the process requests the session lock and when the session is read. This means that your session data will remain consistent, but it also means that while processes P2 and P3 are waiting for their turn to acquire the lock, nothing is happening. This may not be that important if all of the requests change or write to the session data, but if P2 just needs to read the session data (perhaps to get a login identifier), it is being held up for no reason.

So, in the end, this is not the best solution but still is better than nothing. The best solution is probably a per-variable locking. You can read a very detailed article about all this in Andy Bakun's article Race Conditions with Ajax and PHP Sessions.

Thanks to Michael Kliewe who brought this to my attention!

Features

  • acts as a wrapper for PHP's default session handling functions, but instead of storing session data in flat files it stores them in a MySQL database, providing better security and better performance

  • it is a drop-in and seamingless replacement for PHP's default session handler: PHP sessions will be used in the same way as prior to using the library; you don't need to change any existing code!

  • integrates seamlesly with PDO (if you are using PDO) but works perfectly without it

  • implements row locks, ensuring that data is correctly handled in scenarios with multiple concurrent AJAX requests

  • because session data is stored in a database, the library represents a solution for applications that are scaled across multiple web servers (using a load balancer or a round-robin DNS)

  • has awesome documentation

  • the code is heavily commented and generates no warnings/errors/notices when PHP's error reporting level is set to E_ALL

📔 Documentation

Check out the awesome documentation!

🎂 Support the development of this project

Your support means a lot and it keeps me motivated to keep working on open source projects.
If you like this project please ⭐ it by clicking on the star button at the top of the page.
If you are feeling generous, you can buy me a coffee by donating through PayPal, or you can become a sponsor.
Either way - Thank you! 🎉

Star it on GitHub Donate

Requirements

PHP 5.5.2+ with the mysqli extension activated, MySQL 4.1.22+

Installation

You can install via Composer

# get the latest stable release
composer require stefangabos/zebra_session

# get the latest commit
composer require stefangabos/zebra_session:dev-master

Or you can install it manually by downloading the latest version, unpacking it, and then including it in your project

require_once 'path/to/Zebra_Session.php';

Install MySQL table

Notice a directory called install containing a file named session_data.sql. This file contains the SQL code that will create a table that is used by the class to store session data. Import or execute the SQL code using your preferred MySQL manager (like phpMyAdmin or the fantastic Adminer) into a database of your choice.

How to use

Note that this class assumes that there is an active connection to a MySQL database and it does not attempt to create one!

<?php
// first, connect to a database containing the sessions table
// either by something similar to
//
// $link = mysqli_connect(host, username, password, database);
//
//  or by using PDO
//
//  try {
//      $link = new PDO(
//      'mysql:host=' . $host . ';dbname=' . $database . ';charset=utf8mb4', $username, $password, array(
//         PDO::ATTR_ERRMODE   =>  PDO::ERRMODE_EXCEPTION,
//     ));
// } catch (\PDOException $e) {
//     throw new \PDOException($e->getMessage(), (int)$e->getCode());
// }

// include the Zebra_Session class
// (you don't need this if you are using Composer)
require 'path/to/Zebra_Session.php';

// instantiate the class
// this also calls session_start()
$session = new Zebra_Session($link, 'sEcUr1tY_c0dE');

// from now on, use sessions as you would normally
// this is why it is called a "drop-in replacement" :)
$_SESSION['foo'] = 'bar';

// data is in the database!

zebra_session's People

Contributors

compwright avatar fernandosavio avatar gui avatar nightsh avatar nolimitm7 avatar pr1ntr avatar roland-d avatar stefangabos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zebra_session's Issues

How do I unserialize an element in the session_data column ?

I'm trying to get some details of what's in the session, but manually calling unserialize is not working ?

$db = getRawMysql();
$rows = $db->query("SELECT session_data from session_data");
foreach ($rows as $row)
{
    if ( $row['session_data'] ) print_r(unserialize($row['session_data']));
}

Maybe a $lock_to_ip extension

First of all: What an awesome Class. I am using it for my project and i am very satisfied with it.
Next: Sorry for my bad english. I am not good at all :)

Now the idea: As in the class description described u should use $lock_to_ip with caution because of the proxie issue or dynamic IP changes which i agree. So it would be nice, if we can pass a parameter [string] to the constructor for extending the hash.

In my scenario i would do a client side fingerprint and send it back to the server (via ajax) for passing it to the ZebraSession constuctor. Then i would be able to determine the session with its client more precise. Is it possible to implement it?

_mysql_ping does not work when using MySQLi's mysqlnd backend

Since PHP 5.4, mysqlnd has been the default backend driver for all MySQL extensions in PHP, including MySQLi.

However, mysqlnd does not support the mysqli::ping method, which Zebra_Session uses in its constructor. The PHP developers do not intend to fix this on their end, as automatic reconnection is considered dangerous.

We should replace the if(this->_mysql_ping()) check in the constructor with something that works with both libmysqlclient and mysqlnd. I chose to replace it with:

if($this->link instanceof MySQLi && $this->link->connect_error === null)

I can make a pull request with this change if you find it acceptable.

keep session even if browser is close

Hi,
I want that my session will stay even if the browser is close so I tried this code:
ini_set('session.cookie_lifetime', 1300000);
$session = new Zebra_Session($pdo, 'sessionSecCode', '', true, false, 1000);

however the moment I closed the browser.. i lost my session. is there a way for me to keep my session even if i close the browser?

get_lock doesn't work

This is probably a local issue but I'm stumped.

function read($session_id) (around line 500) fails with 'Zebra_Session: Could not obtain session lock'

If I dump the contents of result it shows that it contains no data - 0 rows. No error or warning is logged.

Any ideas?

Expiring sessions oddity?

We have this scenario:

session.cookie_lifetime = 5184000

When I visit and check the session, the database session_expire column has an epoch of approximately 7 days into the future?

Also, at some point the table row for the session is removed by Zebra_Session and the cookie is left in the browser and the user is forced to login (long before the 60 day cookie_lifetime setting).

What would be the cause of this? Or, how do I obtain the desired 60 day session? Thank you

request: anti-dos

can this feature an antidos functionality? or its already safe from dos attacks?

How to show alert when session is expired?

Hi, I have successfully implemented the zebra session and it works wonderfully. I would like to add an alert when the session is going to expire or expire. I analyzed your code and tried to add it to the write() function. That didn't work. Kindly need your assistance on this. Thank you in advance :)

Prepare statements a must!

Prepared statements should absolutely replace any mysqli_real_escape_string references. While it appears your usage of mysqli_real_escape_string should not create any sql injection vulnerabilities for most use cases, there are a handful of edge cases that still result in injections.... and those just the ones that are published!

Highly recommend using mysqli::prepare in place of mysqli_real_escape_string.

http://php.net/manual/en/mysqli.prepare.php
https://stackoverflow.com/a/23277864/3101412

Session Lifetime of 0 (zero) Behaves Differently on PHP 8 - Expires Immediately

Hi there, I'm a big fan of Zebra Session and have been using it for quite some time now with no issues until now. The servers I use are being transitioned to PHP 8 and I noticed that my settings, specifically the Session Lifetime setting, no longer works as it has on PHP 7. My current setup looks like this:

$session = new Zebra_Session(
	$link, // Database Connection
	'sEcUr1tY_c0dE', // Security Code
	0, // Session Lifetime
	true, // Lock to User Agent
	false, // Lock to IP
	'', // Garbage Collection Probability
	'', // Garbage Collection Divisor
	'session_data', // Database Table Name
	60 // Session Lock
);

On PHP 8, a Session Lifetime of 0 (zero) seems to expire immediately. I could make the Session Lifetime greater than zero but my goal is to essentially not have sessions expire unless the browser is closed. Any suggestions?

Composer autoload not working

When using composer's autoloader it doesn't seem to be able to find Zebra_Session.

user# cat composer.json
{
  "minimum-stability": "dev",
  "require": {
    "stefangabos/zebra_session": "dev-master"
  }
}

user# ls vendor/stefangabos/zebra_session/Zebra_Session.php
vendor/stefangabos/zebra_session/Zebra_Session.php

user# cat test.php
<?php
include 'vendor/autoload.php';
$session = new Zebra_Session;

user# php test.php
Fatal error: Class 'Zebra_Session' not found in /Users/jaisenmathai/dev/zebra-test/test.php on line 4

Incorrect user-level lock name

stack trace

Warning: mysqli_query(): (42000/3057): Incorrect user-level lock name 'session_cc306b6d344385e4069fb57d1650f23f7df633e10d07cb5896f8bdf113db0e35e13008b78ea137dc5e3bd74be9acfc42c86e632ee3ca5003e2626d5fc8f99dc2'. in /var/www/html/vendor/stefangabos/zebra_session/Zebra_Session.php on line 752
  |  
  | Call Stack:
  | 0.2160     242912   1. {main}() /var/www/html/ah_test_auth.php:0
  | 0.2250     263040   2. Zebra_Session->__construct() /var/www/html/ah_test_auth.php:20
  | 0.2251     267792   3. session_start() /var/www/html/vendor/stefangabos/zebra_session/Zebra_Session.php:269
  | 0.2251     268728   4. Zebra_Session->read() /var/www/html/vendor/stefangabos/zebra_session/Zebra_Session.php:269
  | 0.2251     269344   5. Zebra_Session->_mysql_query() /var/www/html/vendor/stefangabos/zebra_session/Zebra_Session.php:467
  | 0.2251     269392   6. mysqli_query() /var/www/html/vendor/stefangabos/zebra_session/Zebra_Session.php:752

Environment:

  • php.ini: session.hash_function=sha512

  • to store the bigger session key you need: ALTER TABLE session_data CHANGE session_id session_id VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '';

  • mySQL Version: >=5.7.5

Test Cases
maximum valid key length: 64 characters
SELECT GET_LOCK('1234567890123456789012345678901234567890123456789012345678901234', '5');

fails with everything bigger:
SELECT GET_LOCK('12345678901234567890123456789012345678901234567890123456789012345', '5');

mysql error: Incorrect user-level lock name '12345678901234567890123456789012345678901234567890123456789012345'.

aditional information
the key length got smaller since the new implementation of GET_LOCK in mySQL 5.7.5
https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
go-sql-driver/mysql#385

quick fix
aheissenberger@f84d0df

But I think it would better to create an extra column which contains this key to remove the constant creation of this key on every read request.

Additional I would suggest to add the information about different possible session key length based on the php.ini setting session.hash_function as this is very often set by the provider.

Question: session_id($id) does not change sid

Hi, Thank you for providing this code.

I am using this code for Single sign on app as below. I don't need to write require because I use autoloader of framework.

class 1:
$session = new Zebra_Session($link, 'sEcUr1tY_c0dE');

session_start();
$id = session_id();

.... // I want to use this id in another timing...

class 2:
session_id($id);
$started = session_start();

I already changed session.use_strict_mode from true to false. but it still does not work.
How should I change session id using session_id()?
Please give me your advice if you have any ideas. Thank you!

session_regenerate_id(true) on PHP 7 caught in infinite loop

We recently updated a site to PHP 7.0.8. After the update Zebra_Session caused an internal server error.

We managed to track it down to session_regenerate_id(true) being called by read(). It gets caught in some sort of loop and gets called over and over.

Setting session_regnerate_id(false) instead solves the problem, but of course that means the old session is retained.

It appears the problem occurs on brand-new sessions that don't have any data stored in them. When I switch back from false to true in an already active session, it seems to work fine.

this session class can be deleted!

I used this session, issues will note replayd and upgraded the live time but still everytime session lost.

new session will created without data!

Empty Session

Sometimes, random or I cannot make it reproduceable, it happens the session is empty when I change page. To make it works again, I've to phisically delete the related row in the mysql Table.
I've a login, make a post call on the same page, session set correctly. Redirect to another page, session empty.

  • login page - session not set
  • post action, same page, session set correctly. In the DB the cell session_data is correctly updated
  • redirect to index page, session empty. In the DB the cell session_data just shows flash_messages|a:0:{}

What should I check to debug? Unfortunately I am not able to reproduce the problem. If I delete the row in the DB it works flawlessy.

Some more info:

  • The Zebra Session is created just after loading config files and creating the mysql connection, in all files.
  • If I modify the session_expire field with an older (3 days older) date, the problem remains over the time (default is 10 minutes, passed to the Zebra session constructor)

Pdo insted of mysqli

Can it be adapted to work with pdo as connection.
This way i have two active connections to DB

session_start gives an unkown error

As i am trying to integrate your session class on my one of a PHP project. Which i am unable to use your

if($session_start){ session_start(); }

After this code, nothing works, I don't understand the reason i followed like what you are instructed here .

Divide by zero error when calling get_settings()

When calling get_settings(), line 477:
'probability' => (int)$gc_probability / (int)$gc_divisor * 100 . '%',

Triggers a divide by zero error when running on a host that has 'session.gc_divisor' set to 0.

Thanks so much for this useful tool by the way! :)

ini_set('session.cookie_lifetime', 0);

On lines 235-237 you have this code:

// make sure session cookies never expire so that session lifetime
// will depend only on the value of $session_lifetime
ini_set('session.cookie_lifetime', 0);

That statement does not make "session cookies never expire." It makes the cookies expire when the browser is closed. In my case this caused authenticated users to be logged off upon termination of the session/browser.

Is this a bug or the intended behavior?

Logging Out Users

Hi Just curious what I did wrong. For some reason it keeps longing out users before the official session expires. not sure what I did wrong.

Any idea what could have caused it?

transition to PDO ?

any plans to transition this class to PDO ?

(is a very good class that that I would love to use with postgres)

HUGE number of session rows

Any reason for a HUGE number of session rows? I would expect 50,000 - 300,000 maybe, but I have 1,469,143, and the table is 1.1Gb.

I do have expiration set to 60 days, but still, this seems excessive.

I also have slowlog entries like issue # 15 (GET_LOCKs) - if related?

Any suggestions? Thank you.

explanation request

I want some info what it means all the variables after $db and the secure code?

$session = new Zebra_Session($db, 'sEcUr1tY_c0dE', '', true, false, 1000);

Greetings Theo

String too long

I just started getting this error (after upgrading to PHP 8.3.3):
Uncaught PDOException: SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column 'session_data' at row 1 in C:\Apache24\htdocs\bluedots2\vendor\stefangabos\zebra_session\Zebra_Session.php:775

Zebra was working great for me in 7.4 and the same code that generated the error above, bid not generate an error when I was running PHP 7.4.

The database column definitions have not changed on my system.

What can be done to fix this?

$session->set_flashdata() not working

Hi, can you confirm if this is not working,

Files:

  1. ini.php with new Zebra_Session($link, $code);
  2. index.php with vardump($_SESSION);
  3. logout.php that will delete sessions and with $session->set_flashdata('myvar', 'myval');

Step:

  1. access /logout.php (cookies and session are deleted via setcookie() and $session-stop() and execute $session->set_flashdata('myvar', 'myval') and redirect to index.php
  2. index php var_dump($_SESSION) does not show myvar variable with or without adding session_start() prior the set_flashdata()

both index and logout have require_once 'ini.php';

PHP 8.2 throws deprecation notices for two dynamic properties

I greatly enjoy this library and have been using it for quite a while. I am getting deprecation error notices for two dynamic properties - $session_lock and $security_code. This is quite an easy fix, though.

Thanks for sharing such an awesome library. Merry Christmas

Slow queries from Zebra_Session

The server has 16G of memory and a quad core Xeon @ 3.20GHz.

Our MySQL slowqueries.log is full of things like this now, even with a server load of 0.42:

Query_time: 34.210791 Lock_time: 0.000077 Rows_sent: 0 Rows_examined: 0
SET timestamp=1500342862;
INSERT INTO
session_data (
session_id,
hash,
session_data,
session_expire
)
VALUES (
"l99gu2r3t6tto9bhpf4r9fhec3",
"06dfe788b292aaf30b1e048300bc99cf",
"www.example.com
|O:17:"Authentication":10:{s:9:"auth_type";N;s:6:"status";N;s:14:"login_function";s:8:"Login";s:14:"login_callback";N;s:22:"login_failure_callback";N;s:15:"logout_callback";N;s:26:"provider_mismatch_callb
ack";s:19:"ProviderMismatch";s:27:"\0RB_Authentication\0identity";N;s:29:"\0Authentication\0csrf_token";s:60:"xxx";s:24:"password_change_callback";s:17:
"PasswordChange";}",
"1502762028"
)
ON DUPLICATE KEY UPDATE
session_data = "www.example.com
|O:17:"Authentication":10:{s:9:"auth_type";N;s:6:"status";N;s:14:"login_function";s:8:"Login";s:14:"login_callback";N;s:22:"login_failure_callback";N;s:15:"logout_callback";N;s:26:"provider_mismatch_callb
ack";s:19:"ProviderMismatch";s:27:"\0Authentication\0identity";N;s:29:"\0Authentication\0csrf_token";s:60:"xxxxxxxxx";s:24:"password_change_callback";s:17:
"PasswordChange";}",
session_expire = "1502762028";

And these (more numerous):

Query_time: 11.333438 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0
SET timestamp=1500408648;
SELECT GET_LOCK("session_2e70si6agkejccs7ptd71n3i24", 60);

I'm using the schema specified in the install/session_data.sql.

Security issues

Use prepared statements

mysql_real_escape_string is not a way of preventing SQL injection.

Can't connect Zebra Session with PDO?

Trying to connect Zebra Session with active PDO connection and just keeps saying 'Fatal error: Zebra_Session: No MySQL connection! in C:\xampp\htdocs\assets\components\zebra_session\Zebra_Session.php on line 298' - using latest PHP and PDO rather than using Zebra Database - any help please - thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.