Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sessions in AngularJS and PHP application

I have an AngularJS application that I am updating to use PHP 7. Currently I have a custom session handler setup for sessions:

Custom Session Handler (session.php)

function sess_open( $path, $name ) {
    return true;
}

function sess_close( ) {
    $sessionId = session_id(); 
    return true;
}

function sess_read( $id ) {
    $db = dbConn::getConnection();

    $stmt = "SELECT session_data FROM session where session_id =" . $db->quote($id);
    $result = $db->query($stmt);
    $data = $result->fetchColumn();
    $result->closeCursor();
    return $data;
}

function sess_write( $id, $data ) {
    $db = dbConn::getConnection();

    $tstData = sess_read( $id );
    if (!is_null($tstData)) {
        // if it does then do an update
        $stmt = "UPDATE session SET session_data =" . $db->quote($data) . " WHERE session_id=" . $db->quote($id);
        $db->query($stmt);
    }
    else {
        // else do an insert
        $stmt = "INSERT INTO session (session_id, session_data) SELECT ". $db->quote($id) . ", ". $db->quote($data) . " WHERE NOT EXISTS (SELECT 1 FROM session WHERE session_id=" . $db->quote($id) . ")";
        $db->query($stmt);
    }

    return true;
}

function sess_destroy( $id ) {

    $db = dbConn::getConnection();

    $stmt = "DELETE FROM session WHERE session_id =" . $db->quote($id);
    setcookie(session_name(), "", time() - 3600);
    return $db->query($stmt);
}

function sess_gc( $lifetime ) {

    $db = dbConn::getConnection();

    $stmt = "DELETE FROM session WHERE timestamp < NOW() - INTERVAL '" . $lifetime . " second'";
    return $db->query($stmt);
}

session_name('PROJECT_CUPSAW_WEB_APP');
session_set_save_handler("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc");
session_start();
ob_flush();

In my app.js I have a continuous check to see if the user is authenticated and can access the application.

App.js

/*
 * Continuous check for authenticated permission to access application and route
 */
app.run(function($rootScope, $state, authenticationService, ngToast) {
    $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {
        authenticationService.isAuthenticated()
            .success(function () {
                if(toState.permissions) {
                    ngToast.dismiss();
                    event.preventDefault();
                    $state.go("logout"); // NEEDS TO CHANGE - Unauthorized access view
                    return;
                }
            })
            .error(function () {
                ngToast.dismiss();
                event.preventDefault();
                localStorage.clear();
                $state.go("authentication"); // User is not authenticated; return to login view
                return;
            });
        ngToast.dismiss();
    });
});

In the code above, isAuthenticated runs isUserAuthorized.php

isAuthenticated

/*
 * Check if user is authenticated; set role/permissions
 */
this.isAuthenticated = function() {
    return $http.post(baseUrl + '/isUserAuthorized.php');
};

isUserAuthorized.php

<?php

require_once 'session.php';
// Check to ensure user is authenticated to initiate request
if (array_key_exists('authenticated', $_SESSION) && $_SESSION['authenticated']) {
    return http_response_code(200);
} else {
    // Clear out all cookies and destroy session
    if( array_key_exists('HTTP_COOKIE', $_SERVER)){
      $cookies = explode(';', $_SERVER['HTTP_COOKIE']);
      foreach($cookies as $cookie) {
          $parts = explode('=', $cookie);
          $name = trim($parts[0]);
          setcookie($name, '', time()-1000);
          setcookie($name, '', time()-1000, '/');
      }
    }
    session_destroy();

    return http_response_code(401);
}

The session should be started when session.php is required. It appears that this is not happening though. Upon accessing the application, the login page is displayed, but isUserAuthorized.php is throwing a warning:

Warning: session_start(): Failed to read session data: user (path: /var/lib/php/mod_php/session) in session.php

When I select the Login button, login.php is called, but the user gets brought right into the application, despite incorrect credentials.

login.php

<?php

require_once '../database.php';
require_once 'session.php';
require_once 'ldap.php';

$_SESSION['authenticated'] = false;

//$conn = connect_db();

try {

    $data = json_decode(file_get_contents('php://input'));

    $username = strtolower($data->username);
    $password = $data->password;

    // Check domain credentials; return user token if verified
    if(ldap_authenticate($username, $password)) {
        $_SESSION['authenticated'] = true;
    }
    else {
        echo('Invalid username and/or password!');
        return http_response_code(400);
    }
}

catch(PDOException $e) {
    return http_response_code(400);
}

I'm not entirely sure what's causing this odd behavior, and why the session isn't being created. Do I need to explicitly call the sess_write function?


Update

I discovered that removing the require_once 'session.php' from login.php causes the proper behavior. The user is able to login when they provide valid credentials. However, the session data is still never being written to the database. Any idea why?

like image 678
Sara Fuerst Avatar asked Jul 14 '17 17:07

Sara Fuerst


People also ask

What are sessions used for in PHP?

A session is a way to store information (in variables) to be used across multiple pages. Unlike a cookie, the information is not stored on the users computer.

How are sessions stored in PHP?

PHP Session Start By default, session data is stored in the server's /tmp directory in files that are named sess_ followed by a unique alphanumeric string (the session identifier).

How many types of session are there in PHP?

In the PHP session lifecycle, there are different stages like open, read, write, and close. Additionally, there are two more stages: destroy and garbage collection.

Does PHP support session management?

PHP's session manager is adaptive by default currently. An adaptive session manager bears additional risks. When session. use_strict_mode is enabled, and the session save handler supports it, an uninitialized session ID is rejected and a new one is created.


1 Answers

The issues came down to my session handler. As of PHP 7, the sess_read function must return a string. This was causing the warning:

Warning: session_start(): Failed to read session data: user (path: /var/lib/php/mod_php/session) in session.php

I fixed this by returning '' when $data was null.

This caused issues with my sess_write function knowing when to insert and when to update. I fixed this by changing the SQL.

Ultimately I ended up making the session handler a class, as shown in the final result:

<?php

require_once ('../database.php');

class CustomSessionHandler implements SessionHandlerInterface{
    public function open( $path, $name ) {
        return true;
    }

    public function close( ) {
        return true;
    }

    public function read( $id ) {
        $db = dbConn::getConnection();

        $stmt = "SELECT session_data FROM session where session_id =" . $db->quote($id);
        $result = $db->query($stmt);
        $data = $result->fetchColumn();
        $result->closeCursor();
        if(!$data){
                return '';
        }
        return $data;
    }

    public function write( $id, $data ) {
        $db = dbConn::getConnection();

        //Works with Postgres >= 9.5
        //$stmt = "INSERT INTO session (session_id, session_data) VALUES (" . $db->quote($id) . ", " . $db->quote($data) . ") ON CONFLICT (session_id) DO UPDATE SET session_data=" . $db->quote($data) . ";";

        //Works with Postgres < 9.5
        $stmt = "UPDATE session SET session_data=" . $db->quote($data) . " WHERE session_id=" . $db->quote($id) . ";";
        $db->query($stmt);

        $stmt = "INSERT INTO session (session_id, session_data) SELECT ". $db->quote($id) . ", ". $db->quote($data) . " WHERE NOT EXISTS (SELECT 1 FROM session WHERE session_id=" . $db->quote($id) . ");";
        $db->query($stmt);

        return true;
    }

    public function destroy( $id ) {
        $db = dbConn::getConnection();

        $stmt = "DELETE FROM session WHERE session_id =" . $db->quote($id);
        setcookie(session_name(), "", time() - 3600);
        $data = $db->query($stmt);

        return true;
    }

    public function gc( $lifetime ) {
        $db = dbConn::getConnection();

        $stmt = "DELETE FROM session WHERE timestamp < NOW() - INTERVAL '" . $lifetime . " second'";
        $data = $db->query($stmt);
        return true;
    }
}

session_name('PROJECT_CUPSAW_WEB_APP');
$handler = new CustomSessionHandler();
session_set_save_handler($handler, false);
session_start();
ob_flush();
like image 121
Sara Fuerst Avatar answered Sep 21 '22 14:09

Sara Fuerst