Viewed   64 times

EDIT - 2: See Sam's solution for the case. The old methods i'll still leave here as well as the problem itself, but a new solution was build based on Sam's solution, and it is available in the repository at github on the project i keep

EDIT - 1: SEE THE METHOD destroy(id) for a workaround

Currently I have the need in some occasions in my System that requires other users session to be update with a new value. The current case now needs a single value to be changed in the session file of some users when a certain action is performed. In my project i have created a class SessionHandlerCustom that implements the SessionHandlerInterface, and with that I have implemented a logic that creates a Custom session with an Id for each user. I can access the file in a custom directory, but strangely I can't use file_put_contents or file_get_contents to these Session files. I tried to work around with Session functions, and by using the read() function presented in the SessionHandlerCustom i was able to get all the contents from a user's session using it's SessionId.

The method working, SessionFileGetValueByHashCode, is the one that gets the content from a Session, and with some Key (usually the field name i want in the file), it will get the exactly string of that key with it's values. The second method, not working at all, is the one that will actually change the value in the Session, but it just doesn't. I have tried manipulating the file directly with no success and have tried to use the SessionHandlerCustom->write() method but it has no effect.

Could anyone help me out and explain how is the correct way to change/manipulate the session of other user's ?

A System example: A common user is logged, while in the same time an administrator uses the Admin functions and change some value for this user. If the value is not something obtained from the database every time, the session variable for that current logged in user need to have its value changed.

If you get curious, the System is Open Source, and you can access it here: https://github.com/msiqueira-dev/InfraTools

*
This should be in the class that implements SessionHandlerInterface. This will actually delete the users session and force it to reload the whole session when it makes another request. The $SavePath variable is variable create and stored with the directory folder of the Session.
*/
public function destroy($id)
    {
        $file = $this->SavePath . "/sess_" . $id;
        if (file_exists($file)) 
            unlink($file);
        return true;
    }

SessionFileGetValueByHashCode

//The &$Value will be filled with the whole $Key and its values presented in the Session File. For example: SomeValue|s:0:"";
public function SessionFileGetValueByHashCode(&$Value, $Application, $SessionId, $Key)
{
        $Value = NULL;
        if(isset($Application) && !empty($Application) && isset($SessionId) && !empty($SessionId) && isset($Key) && !empty($Key))
        {
            $file = SESSION_PATH . $Application . "/sess_" . $SessionId;
            if(file_exists(($file)))
            {
                $str = $this->InstanceSessionHandlerCustom->read($SessionId);
                $start = strpos($str, $Key);
                $end=$start;
                while($str[$end] != '"')
                {
                    $Value .= $str[$end];
                    $end++;
                }
                $Value .= '"';
                $end++;
                while($str[$end] != '"')
                {
                    $Value .= $str[$end];
                    $end++;
                }
                $Value .= '"';
                if($Value != NULL)
                    return Config::RET_OK;
            }
        }
        return Config::RET_ERROR;
    }

SessionFileUpdateValueByHashCode

public function SessionFileUpdateValueByHashCode($Application, $SessionId, $OldValue, $NewValue)
    {
        if(isset($Application) && !empty($Application) && isset($SessionId) && !empty($SessionId) 
                               && isset($OldValue) && !empty($OldValue) && isset($NewValue) && !empty($NewValue))
        {
            $file = SESSION_PATH . $Application . "/sess_" . $SessionId;
            if(file_exists(($file)))
            {
                $str = $this->InstanceSessionHandlerCustom->read($SessionId);
                $str = str_replace($OldValue, $NewValue, $str, $count);
                if($count > 0)
                {
                    echo $str . "<br>";
                    if($this->InstanceSessionHandlerCustom->write($SessionId, $str))
                        return Config::RET_OK;
                }
            }
        }
        return Config::RET_ERROR;
    }

 Answers

1

You can modify another users session (see below), although personally, I would recommend against it. As I imagine it can open up a whole world of session hijacking and other vulnerabilities.

With your example use case

A common user is logged, while in the same time an administrator uses the Admin functions and change some value for this user. If the value is not something obtained from the database every time, the session variable for that current logged in user need to have its value changed.

You would be better of updating the value in the database and then just checking to see if it's changed before you process the next page. If you don't want to be checking multiple user fields before each page load then when you update the user in the admin panel, you can build a hash of the values and add it to a new column called session_hash. Then just compare this field on page load

But if you still want to modify another user's session, you can set your current session_id to the targets.

// End my current session and save its id
session_start();
$my_session_id = session_id();
session_write_close();

// Modify our target session 
session_id($target_id);
session_start();
$_SESSION['is_logged_in'] = false;
session_write_close();

// Start our old session again
session_id($my_session_id);
session_start();

EDIT

Example: https://www.samdjames.uk/session_example/index.php

Example Src: https://gist.github.com/SamJUK/c220e3742487567c6262238edf85695e

Monday, December 5, 2022
 
5

I have found over the course of several hours debugging that the referenced articles found on numerous Google searches as well as a significant subset of answers such as here, here and here all provide invalid or outdated information.

Things that can cause [critical] issues with saving session data to a database:

  • While all the examples online state that you can "fill" the session_set_save_handler, none of them state that you must also set the register_shutdown_function('session_write_close') too (reference).

  • Several (older) guides refer to an outdated SQL Database structure, and should not be used. The database structure that you need for saving session data to the database is: id/access/data. That's it. no need for various extra timestamp columns as I've seen on a few "guides" and examples.

    • Several of the older guides also have outdated MySQL syntax such as DELETE * FROM ...
  • The class [made in my question] must implement the SessionHandlerInterface . I have seen guides (referenced above) that give the implementation of sessionHandler which is not a suitable interface. Perhaps previous versions of PHP had a slightly different method (probably <5.4).

  • The session class methods must return the values set out by the PHP manual. Again, probably inherited from pre-5.4 PHP but two guides I read stated that class->open returns the row to be read, whereas the PHP manual states that it needs to return true or false only.

  • This is the cause of my Original Issue: I was using custom session names (actually id's as session names and session id's are the same thing!) as per this very good post and this was generating a session name that was 128 characters long. As the session name is the sole key that is needed to be cracked to compromise a session and take over with a session hijacking then a longer name/id is a very good thing.

    • But, this caused an issue because MySQL was silently slicing the session id down to just 32 characters instead of 128, so it was never able to find the session data in the database. This was a completely silent issue (maybe due to my database connection class not throwing warnings of such things). But this is the one to watch out for. If you have any issues with retrieving sessions from a database first check is that the full session id can be stored in the field provided.

So with all that out of the way there are some extra details to add as well:

The PHP manual page (linked above) shows an unsuitable pile of lines for a class object:

$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();

Whereas it works just as well if you put this in the class constructor:

class MySessionHandler implements SessionHandlerInterface {

    private $database = null;

public function __construct(){

    $this->database = new Database(whatever);

    // Set handler to overide SESSION
    session_set_save_handler(
        array($this, "open"),
        array($this, "close"),
        array($this, "read"),
        array($this, "write"),
        array($this, "destroy"),
        array($this, "gc")
        );
    register_shutdown_function('session_write_close');
    session_start();
    }
...
}

This means that to then start a session on your output page all you need is:

<?php
require "path/to/sessionhandler.class.php"; 
new MySessionHandler();

//Bang session has been setup and started and works

For reference the complete Session communication class is as follows, this works with PHP 5.6 (and probably 7 but not tested on 7 yet)

<?php
/***
 * Created by PhpStorm.
 ***/
class MySessionHandler implements SessionHandlerInterface {
    private $database = null;

    public function __construct($sessionDBconnectionUrl){
        /***
         * Just setting up my own database connection. Use yours as you need.
         ***/ 

            require_once "class.database.include.php";
            $this->database = new DatabaseObject($sessionDBconnectionUrl);

        // Set handler to overide SESSION
        session_set_save_handler(
            array($this, "open"),
            array($this, "close"),
            array($this, "read"),
            array($this, "write"),
            array($this, "destroy"),
            array($this, "gc")
        );
        register_shutdown_function('session_write_close');
        session_start();
    }

    /**
     * Open
     */
    public function open($savepath, $id){
        // If successful
        $this->database->getSelect("SELECT `data` FROM sessions WHERE id = ? LIMIT 1",$id,TRUE);
        if($this->database->selectRowsFoundCounter() == 1){
            // Return True
            return true;
        }
        // Return False
        return false;
    }
    /**
     * Read
     */
    public function read($id)
    {
        // Set query
        $readRow = $this->database->getSelect('SELECT `data` FROM sessions WHERE id = ? LIMIT 1', $id,TRUE);
        if ($this->database->selectRowsFoundCounter() > 0) {
            return $readRow['data'];
        } else {
            return '';
        }
    }

    /**
     * Write
     */
    public function write($id, $data)
    {
        // Create time stamp
        $access = time();

        // Set query
        $dataReplace[0] = $id;
        $dataReplace[1] = $access;
        $dataReplace[2] = $data;
        if ($this->database->noReturnQuery('REPLACE INTO sessions(id,access,`data`) VALUES (?, ?, ?)', $dataReplace)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Destroy
     */
    public function destroy($id)
    {
        // Set query
        if ($this->database->noReturnQuery('DELETE FROM sessions WHERE id = ? LIMIT 1', $id)) {
            return true;
        } else {

            return false;
        }
    }
    /**
     * Close
     */
    public function close(){
        // Close the database connection
        if($this->database->dbiLink->close){
            // Return True
            return true;
        }
        // Return False
        return false;
    }

    /**
     * Garbage Collection
     */
    public function gc($max)
    {
        // Calculate what is to be deemed old
        $old = time() - $max;

        if ($this->database->noReturnQuery('DELETE FROM sessions WHERE access < ?', $old)) {
            return true;
        } else {
            return false;
        }
    }

    public function __destruct()
    {
        $this->close();
    }

}

Usage: As shown just above the class code text.

Saturday, November 19, 2022
4

You know that you've got to write session_start() before you use the $_SESSION variable in any request, right? It looks like you haven't put it in index.php anywhere.

Friday, December 16, 2022
5

Instead of setting the time in ini to a fixed length, remind that session timeout is reset on reload. So create some ajax code that does a request every 5 minutes or so to a file (image or smth). This way the timer is reset every 5 minutes and users can spend a day filling out your forms.

Saturday, November 5, 2022
 
2

A pure batch code with explanatory comments:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "hostspath=%SystemRoot%System32driversetchosts"

rem Initialize the array of our hosts to toggle
for %%a in (
    "127.0.0.1 www.example1.com"
    "127.0.0.1 www.example2.com"
    "127.0.0.1 www.example3.com"
) do (
    set /a numhosts+=1
    set "host!numhosts!=%%~a"
)

>"%hostspath%.new" (
    rem Parse the hosts file, skipping the already present hosts from our list.
    rem Blank lines are preserved using findstr trick.
    for /f "delims=: tokens=1*" %%a in ('%SystemRoot%System32findstr.exe /n /r /c:".*" "%hostspath%"') do (
        set skipline=
        for /L %%h in (1,1,!numhosts!) do (
            if "%%b"=="!host%%h!" (
                set skipline=true
                set found%%h=true
                echo - %%b 1>&2
            )
        )
        if not "!skipline!"=="true" echo.%%b
    )
    for /L %%h in (1,1,!numhosts!) do (
        if not "!found%%h!"=="true" echo + !host%%h! 1>&2 & echo !host%%h!
    )
)
move /y "%hostspath%" "%hostspath%.bak" >nul || echo Can't backup %hostspath%
move /y "%hostspath%.new" "%hostspath%" >nul || echo Can't update %hostspath%
endlocal
pause
Sunday, September 4, 2022
 
tfer2
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :