Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to handle native sessions #346

Closed
Alex-Nabu opened this issue Aug 6, 2015 · 9 comments
Closed

How to handle native sessions #346

Alex-Nabu opened this issue Aug 6, 2015 · 9 comments

Comments

@Alex-Nabu
Copy link

I want to use php's native session management so I'm writing a small class to just use the PHPSESSID cookie sent from the browser and get the session information. I call and use it in this way but while testing I noticed all the connecting clients have the same id. I thought connections were unique ?

    <?php
    namespace my_project\helpers;
    use Exception;

    class session {

      // the id of the client session;
      private $id;

      // take a session Id and continue a session if empty createa new session
     public function __construct($PHPSESSID = ''){

     // if we have a valid session id containing letters and numbers then use it
    // @note add check for weird characters later
    if (strlen($PHPSESSID) > 5 && preg_match('/[A-Za-z]/', $PHPSESSID) && preg_match('/[0-9]/',$PHPSESSID)){
      session_id($PHPSESSID);
    }

       @session_start();
      $this->id = session_id();
      session_write_close();
     }
    }
    ?>

And I'm using it like this to start session when the client first connects in my handler

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $conn->session = new \my_project\helpers\session($conn->WebSocket->request->getCookie("PHPSESSID"));
        $this->clients->attach($conn);

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
      var_dump($from->session);
    }

All giving me the same id back when I send a message to the server. What should I attach my session object to in order to make them unique to each connection

@cboden
Copy link
Member

cboden commented Aug 6, 2015

From the docs:

The SessionProvider will not work with any of the Native* session handlers

@Alex-Nabu
Copy link
Author

well. I want to use native sessions and so far as getting the information goes. it works and I have access to the session information. Could you please help me solve my problem at hand. I understand that you don't offer official support which is why i'm not using the SessionProvider. I'v read the docs carefully.

@Alex-Nabu
Copy link
Author

Ok so I wrote a helper class to do this if anyone wants else is interested in something like this, which I highly suspect would be the case.

      class session {

        // the id of the client session;
        private $id;

        // take a session Id and continue a session if empty create a new session
        public function __construct($PHPSESSID = ''){

          // if we have a valid session id containing letters and numbers then use it
          // @note add check for weird characters later
          if (strlen($PHPSESSID) > 5 && preg_match('/[A-Za-z]/', $PHPSESSID) && preg_match('/[0-9]/', $PHPSESSID)){
            session_id($PHPSESSID);
          } else{
            session_regenerate_id(); // so we get a new session id for the new user
          }

          @session_start();
          $this->id = session_id();
          unset($_SESSION); // so we cant attempt to use this directly
          session_write_close();
        }

        // return the value of a session variable or null
        public function get($var_name){
          session_id($this->id);
          @session_start();

          if(isset($_SESSION[$var_name])){
            $var =  $_SESSION[$var_name];
          } else{
            $var = NULL;
          }

          unset($_SESSION);
          session_write_close();

          return $var;
        }

        // set the value of a session variable
        public function set($var_name, $var_value){
          session_id($this->id);
          @session_start();

          $_SESSION[$var_name] = $var_value;

          unset($_SESSION);
          session_write_close();
        }

      }

And I use it like this

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);

        // get session ID and store session info on the connection obj
        $conn->session = new session($conn->WebSocket->request->getCookie("PHPSESSID"));
        echo "The session id passed in was : ".$conn->WebSocket->request->getCookie("PHPSESSID")."\n";

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
      foreach ($this->clients as $client) {
        if($from == $client){
          $client->session->set("test", "Yes we have recorded you");

          $client->send($client->session->get("test"));

          // test sessoin set from our main website
          if($client->session->get('id')){
              $client->send("hi. Your user ID is {$client->session->get('id')}");
          }
        }
      }
    }

The problem from before was because the server is a single php script running it was reusing the same session id for each client whenever I did session start and that was fixed by asking it to generate a new one. Also because the use of the $_SESSION super global would be a bad thing due to single script and multiple potential clients I'v unset it. the warning suppression is necessary because websocket headers have already been sent before we can do session start.

@Alex-Nabu
Copy link
Author

@cboden forgive my ignorance but only a implementer could truly know. But why did you choose not to implement native session ? I think enough people would want it. Also I suspect there may be some security hole in my code but I dont see any at the current time. Maybe if the socket were to send back the set-cookie headers to the client it may send all the cookie ids i'v generated but I don't think web sockets do that after the connection is fully establised? Any security madness you can find there would be good to know. thanks

@Alex-Nabu
Copy link
Author

session_regenerate_id(); // so we get a new session id for the new user

That line of code sometimes refuses to work with error :

Warning: session_regenerate_id(): Cannot regenerate session id - headers already sent

I tested and this only happends after I completely close the broswer window. simply closing the connection wont make the error happen. So i'm figuring sockets actually do send out header info when the browser window is closed ?

In order to make this secure against this. I need to replace that with something that gurantees me a new id gets generated or generate one myself with some other function. Any ideas ?

@Alex-Nabu
Copy link
Author

replaced

session_regenerate_id(); // so we get a new session id for the new user

in the session constructor with

session_id(bin2hex(openssl_random_pseudo_bytes(32)));

which I hear is even a bit more secure than what php was using by default to generate the session id's with session_generate_id and it still works with native php sessions and the calling code is still the same. so the full working class is : (just changed that mentioned bit in the constructor)

class session {

  // the id of the client session;
  public $id;

  // take a session Id and continue a session if empty create a new session
  public function __construct($PHPSESSID = ''){

    // if we have a valid session id containing letters and numbers then use it
    // @note add check for weird characters later
    if (strlen($PHPSESSID) > 5 && preg_match('/[A-Za-z]/', $PHPSESSID) && preg_match('/[0-9]/', $PHPSESSID)){
      session_id($PHPSESSID);
    } else{
      session_id(bin2hex(openssl_random_pseudo_bytes(32)));
    }

    @session_start();
    $this->id = session_id();
    unset($_SESSION); // so we cant attempt to use this directly
    session_write_close();
  }

  // return the value of a session variable or null
  public function get($var_name){
    session_id($this->id);
    @session_start();

    if(isset($_SESSION[$var_name])){
      $var =  $_SESSION[$var_name];
    } else{
      $var = NULL;
    }

    unset($_SESSION);
    session_write_close();

    return $var;
  }

  // set the value of a session variable
  public function set($var_name, $var_value){
    session_id($this->id);
    @session_start();

    $_SESSION[$var_name] = $var_value;

    unset($_SESSION);
    session_write_close();
  }

}

Really hope I helped somebody. And if any guru's come later on down the line and see any problems please let us know.

@cboden
Copy link
Member

cboden commented Aug 7, 2015

@Alex-Nabu Native support wasn't implemented originally because of a technical limitation. I've looked at it on more than one occasion and couldn't overcome that limitation. I can't remember what it was now, it's been so long.

@manix
Copy link

manix commented Sep 11, 2015

@Alex-Nabu I don't know if it is too late but I decided to share my findings. I recently had the same need and it turned out to be quite an easy job if you implemented the SessionHandlerInterface. Please note that the standard SessionHandler class will not work, as it is not intended to be used this way, however you can create your own class implementing the interface which will make it really easy getting the encoded session data, decoding it is still a little hacky, because session_decode() puts the result in $_SESSION and doesn't provide an option to return, but still you don't need to call session_start() and suppress errors every time.

@cboden cboden closed this as completed Oct 10, 2015
@mohsen159
Copy link

mohsen159 commented Apr 4, 2023

@manix can u provide code examples of your sulotion i can't find my way here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants