I'm currently building a social network. I got to the point where I need cookies and tokens (in the database) to make people logout and login in.
The problem is that for some reason one of my variables is not getting any data from the query I wrote...
Here's the code of the page:
Cookie_login.php
<?php declare(strict_types=1);
class Login
{
public static function isloggedIn(): ?int
{
if (isset($_COOKIE['SNID'])) {
$user = DB::query(
'SELECT user_id FROM tokens WHERE token=:token',
[':token' => sha1($_COOKIE['SNID'])]
);
if (empty($user) && isset($user[0]['user_id'])) {
$userid = $user[0]['user_id'];
}
if (isset($_COOKIE['SNID_'])) {
return $userid;
}
$cstrong = true;
$token = bin2hex(openssl_random_pseudo_bytes(64, $cstrong));
DB_update::query_update(
'INSERT INTO tokens VALUES (\'\',:token,:user_id)',
[':token' => sha1($token), ':user_id' => $userid]
);
DB_update::query_update('DELETE FROM tokens WHERE token=:token', [':token' => sha1($_COOKIE['SNID'])]);
setcookie('SNID', $token, time() + 60 * 60 * 24 * 7, '/', null, true, true);
return $userid;
}
return null;
}
}
and the DB.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
class DB
{
private static function connect(): PDO
{
$pdo = new PDO('mysql:host=127.0.0.1;dbname=pap;charset=utf8', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
public static function query($query, $params = [])
{
$statement = self::connect()->prepare($query);
$statement->execute($params);
return $statement->fetchALL();
}
}
class DB_update
{
private static function connect(): PDO
{
$pdo = new PDO('mysql:host=127.0.0.1;dbname=pap;charset=utf8', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
public static function query_update($query_update, $paramss = [])
{
$statement = self::connect()->prepare($query_update);
$statement->execute($paramss);
return $statement->RowCount();
}
}
I used the fopen() and fwrite() to see what $userid is returning and it was returning 1... Already check the queries in SQL and they work just fine... So I don't know why the $userid variable is not saving the user_id from the table. I hope you guys can help me...
PS:DB_update is just a name to be different from DB_query
First, of all:
fwrite($fp, print_r($userid));
Function print_r has a second parameter, default sets to false but in your case, you have to explicity set to true:
fwrite($fp, print_r($userid, true));
Otherwise, you will always write to file 1 value.
And the second. It's good idea to use function fclose to close opened file pointer if it's no longer required.
This line generates your "undefined index off 0" error:
$userid = DB::query('SELECT user_id FROM tokens WHERE token=:token',
array(':token' =>sha1($_COOKIE['SNID'])))[0]['user_id'];
Specifically, the [0]['user_id'] bit that you've patched to the end of the query. You should only ever do that if you're sure the function called will always return an array with the necessary fields (not the case with DB queries!). When there is no match, your DB::query method probably returns NULL or false (and you can't have NULL[0] etc. much less NULL[0]['user_id'], thus the error).
You'll want to postpone the offset assignment, and first of all have a logic for situations where no match happens. First put the DB query result into a variable that you can evaluate as being the expected type of resource or not -- only then assign array offsets.
$user = DB::query('SELECT user_id FROM tokens WHERE token=:token',
array(':token' =>sha1($_COOKIE['SNID'])));
if (!empty($user) && isset($user[0]['user_id'])) {
$userid = $user[0]['user_id'];
}
else {
// whatever that you need to do here.
}
On your debugging trouble; your print_r logger is only logging 1 because:
print_r ( mixed $expression [, bool $return = FALSE ] ) : mixed
... "When the $return parameter is TRUE, this function will return a string.
Otherwise, the return value is TRUE."
If you want to both print and log into a file, then use an intermediate variable, along the lines of $pr = print_r($foo, true); echo $pr; fwrite($fp, $pr);. Hope that helps you get it sorted.
Ok, that's whole code with comments:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
class DB
{
private static function connect()
{
$pdo = new PDO('mysql:host=127.0.0.1;dbname=pap;charset=utf8','root','');
$pdo -> setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
return $pdo;
}
public static function query($query,$params=array())
{
$statement = self::connect() ->prepare($query);
$statement ->execute($params);
$data = $statement -> fetchALL();
return $data;
}
}
class DB_update
{
private static function connect()
{
$pdo = new PDO('mysql:host=127.0.0.1;dbname=pap;charset=utf8','root','');
$pdo -> setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
return $pdo;
}
public static function query_update($query_update,$paramss=array())
{
$statement = self::connect() ->prepare($query_update);
$statement ->execute($paramss);
$datas = $statement -> RowCount();
return $datas;
}
}
/**
* Table `tokens` schema:
* CREATE TABLE `tokens` (
* `token` varchar(255) NOT NULL,
* `user_id` int(11) NOT NULL,
* PRIMARY KEY (`token`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8
*/
class Login
{
public static function isloggedIn()
{
if (isset($_COOKIE['SNID'])) {
$userid = DB::query('SELECT user_id FROM tokens WHERE token=:token',array(':token' =>sha1($_COOKIE['SNID'])))[0]['user_id'];
$fp = fopen('problem.txt', 'w');
fwrite($fp, print_r($userid, true));
fclose($fp);
// I don't understand, what is this ? There is no place in your code, to set that cookie in that named key
// It always be false
if (isset($_COOKIE['SNID_']))
{
return $userid;
}
else {
$cstrong = TRUE;
$token = bin2hex(openssl_random_pseudo_bytes(64, $cstrong));
DB_update::query_update('INSERT INTO tokens VALUES (:token,:user_id)',array(':token'=>sha1($token),':user_id'=>$userid));
DB_update::query_update('DELETE FROM tokens WHERE token=:token',array(':token'=>sha1($_COOKIE['SNID'])));
setcookie("SNID", $token, time() + 60 * 60 * 24 * 7, '/', NULL, TRUE, TRUE);
// I don't understand what is this below
setcookie("SNID:", '1', time() + 60 * 60 * 24 * 3, '/', NULL, TRUE, TRUE);
return $userid;
}
}
return false;
}
}
// Uncomment code below to log in
// $userIdToLogIn = 19; // You can change it to any other value
// $cstrong = true;
// $token = bin2hex(openssl_random_pseudo_bytes(64, $cstrong));
// DB_update::query_update('INSERT INTO tokens VALUES (:token, :user_id)', array(':token' => sha1($token), ':user_id' => $userIdToLogIn));
// setcookie("SNID", $token, time() + 60 * 60 * 24 * 7, '/', NULL, TRUE, TRUE);
// ------------------------------
// Uncomment code below to log out
// setcookie("SNID", time() - 3600);
// -------------------------------
// And check
$userId = Login::isloggedIn();
if($userId) {
printf(
"User with id: %s is logger in with token: %s",
$userId,
DB::query('SELECT token FROM tokens WHERE user_id = :user_id', array(':user_id' => $userId))[0]['token']
);
} else {
echo "User is not logger in!";
}
And now:
Uncomment lines for comment: Uncomment code below to log in for first request. Script log in you.
Comment it back for second and nexts requests to check how it works.
Related
I am still working on my own database class with pdo:
class Database {
private $databaseConnection;
public function __construct($path = "", $dbUsername = "", $dbPassword = ""){
$parts = explode('.',$path);
$documentType = array_pop($parts);
if(($path == "") || ((strcmp($documentType, "sq3") !== 0) && (strcmp($documentType, "sqlite") !== 0))) {
throw new OwnException("The Database must bee .sq3 or .sqlite and Path must be stated");
}
$this->databaseConnection = new PDO('sqlite:' . $path, $dbUsername, $dbPassword);
$this->databaseConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->databaseConnection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$this->databaseConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
self::query('CREATE TABLE IF NOT EXISTS User(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(40) NOT NULL UNIQUE,
numberoflogins INTEGER DEFAULT 0,
bannedstatus BOOLEAN DEFAULT FALSE,
dateofjoining TIME
)');//password field coming soon
//self::query('CREATE TABLE...');
//self::query('CREATE TABLE...');
}
private function query($sql, $params = NULL){
$pdoStatement = $this->databaseConnection->prepare($sql);
$pdoStatement->execute(array_values((array) $params));
return $pdoStatement;
}
public function getObjects($objectTable, $searchedForAttribute, $attributeValue){
$pdoStatement = $this->databaseConnection->prepare("SELECT * FROM $objectTable WHERE $searchedForAttribute = ?");
$pdoStatement->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, $objectTable);
$pdoStatement->execute(array($attributeValue));
$resultObjects = array();
while($resultObject = $pdoStatement->fetch()){
array_push($resultObjects, $resultObject);
}
if(empty($resultObjects)){
return false;
}
return $resultObjects;
}
public function getObject($objectTable, $searchedForAttribute, $attributeValue){
//...returns only the first Object from getObjects()
}
public function insertObject($object){
$objectTable = get_class($object);
$objectData = $object->getAttributes();
return $this->query("INSERT INTO $objectTable("
. join(',', array_keys($objectData)) . ")VALUES("
. str_repeat('?,', count($objectData)-1). '?)', $objectData);
}
public function updateAttribute($objectTable, $setData, $searchedAttribute, $searchedAttributeValue){
...
}
public function updateObject($object){
...
}
public function attributeRemoveObject($objectTable, $searchedForAttribute, $attributeValue){
...
}
public function __destruct(){
unset($this->databaseConnection);
}
}
as you can see there is still no data validation for the functions (and no exception handling, work in progress) such like getObjects() so the variables $objectTable, $searchedForAttribute and $attributeValue going direct into the query. This means no protection against SQL injections.
So I thought it would be quite helpful if I use a static function to validate data before inserting into query:
public static function validate($unsafeData){
//validate $unsafeData
return $safeData
}
Because I want to have the ability to search for usernames with similar names and stuff bin2hex() and hex2bin() is a bad choice and for some attributes like the username it is easy to find some starting points for the validation. For instance I would search for empty space, ', " and =...
But how should I validate the content of a forumpost which contains a lot of signs used for SQL queries to? I mean it could also be a a post about sql itself.
I saw a lot of examples for SQL Injections but all of them missing the point that the main manipulation could also be in the content box.
So how does a forum prevent SQL Injections and Errors referring to the content of a post ?
Your weakest point is here:
public function insertObject($object){
$objectTable = get_class($object);
$objectData = $object->getAttributes();
return $this->query("INSERT INTO $objectTable("
. join(',', array_keys($objectData)) . ")VALUES("
. str_repeat('?,', count($objectData)-1). '?)', $objectData);
}
The best way to avoid SQL injection is using PDO::bindParam. It doesn't matter if a string field contains valid SQL as long as you use prepared queries and bound parameters:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pquery = $pdo->prepare(
'INSERT INTO table(column1, column2) VALUES(:column1, :column2)');
// PDO::PARAM_INT, PDO::PARAM_STR, PDO::PARAM_BOOL, etc.
$pquery->bindValue(':column1', $column1, PDO::PARAM_INT); // error if $column1 isn't integer value
$pquery->bindValue(':column2', $column2, PDO::PARAM_STR); // string are sanitized
$pquery->execute();
For an arbitrary object you have to use some sort of metadata to select the correct PDO::PARAM_X value (default is PDO::PARAM_STR):
<?php
class User
{
public $username = 'foo\'; DROP TABLE User; --';
public $email = 'bar#gmail.com';
public $age = 500;
}
function getColumnType()
{
return PDO::PARAM_STR; // just for demo
}
$object = new User;
$ref = new ReflectionObject($object);
$table = $ref->getShortName(); // to avoid FQN
$properties = $ref->getProperties(ReflectionProperty::IS_PUBLIC);
$params = []; $columns = [];
foreach ($properties as $property) {
$params[] = ':'.($columns[] = $property->getName());
}
// in memory db for demo
$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('create table User(id INTEGER PRIMARY_KEY, username VARCHAR(250) NOT NULL,email VARCHAR(250) NOT NULL,age INT)');
// your answer
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pquery = $pdo->prepare("INSERT INTO $table(".implode(',', $columns).") VALUES(".implode(',', $params).")");
foreach ($properties as $property) {
$paramName = ':'.$property->getName();
$paramValue = $property->getValue($object);
$paramType = getColumnType($object, $property); // return PDO::PARAM_X
$pquery->bindValue($paramName, $paramValue, $paramType);
}
$pquery->execute();
// check
$all = $pdo->prepare('select * from User');
$all->execute();
var_dump($all->fetchAll(PDO::FETCH_CLASS, 'User'));
Output:
array(1) {
[0] =>
class User#10 (4) {
public $id =>
NULL
public $username =>
string(25) "foo'; DROP TABLE User; --"
public $email =>
string(13) "bar#gmail.com"
public $age =>
string(3) "500"
}
}
You must implement getColumnType to get the correct column data type, for example, parsing annotated comments. But at this point you better use some ORM like Eloquent or Doctrine.
I'm creating my own custom SessionHandler to store my session information in a postgresql 9.3 database and I'm having a problem where the session data passed to the write() method isn't being written to the database, but the session name is??
Things that I know for a fact
My custom class is handling the sessions when session_start() is called - as tested with echoing output from the various methods and no session files are being created in /tmp
The $session_data arg in write() contains the proper serialized string as shown by echoing the contents in the write() method.
$session_name is being written to the database just fine and so is a BLANK serialized string a:0:{}.
Things I'm confused about:
Echoing the contents of $_SESSION['test_var1'] shows the correct value stored, even if read() is empty or returning no value??
If the session name is saved in the DB just fine, why isn't the session data?
Server Configuration
OS: Gentoo
Database: Postgresql 9.3
Web Server: NGINX 1.7.6
PHP 5.5.18 connected to NGINX via FPM
PHP ini session settings
session.save_handler = user
session.use_strict_mode = 1
session.use_cookies = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.name = _g
session.auto_start = 0
session.serialize_handler = php_serialize
class SessionManagement implements SessionHandlerInterface {
private $_id = '';
private $_link = null;
public function __construct() {
session_set_save_handler(
[$this, 'open'],
[$this, 'close'],
[$this, 'read'],
[$this, 'write'],
[$this, 'destroy'],
[$this, 'gc']
);
}
public function open($save_path, $session_id) {
echo 'open called<br/>';
$this->_id = $session_id;
$this->_link = new PDO('pgsql:host=' . $_SERVER['DB_HOST'] . ';dbname=' . $_SERVER['DB_DB'],
$_SERVER['DB_USER'],
$_SERVER['DB_PASS']);
}
public function close() {
echo 'close called<br/>';
}
public function destroy($session_id) {
echo 'destroying '.$session_id, '<br/>';
}
public function gc($maxlifetime) {
echo 'GC called<br/>';
}
public function read($session_name) {
$name = $this->_id.'_'.$session_name;
$sql = 'SELECT session_data FROM sessions WHERE session_name = :name';
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name])) {
return $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
} else {
return false;
}
} else {
return false;
}
return '';
}
public function write($session_name, $session_data) {
echo 'Session data: '.$session_data.'<br/>';
$name = $this->_id . '_' . $session_name;
$data = $session_data;
$sql = "SELECT 1 FROM sessions WHERE session_name = :name";
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name])) {
if ($rel->rowCount()) {
echo 'Updating...<br/>';
$sql = 'UPDATE sessions SET session_data = :data WHERE session_name = :name';
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name, ':data' => $data])) {
echo 'Update success...<br/>';
} else {
echo 'Update failed...<br/>';
var_dump($rel->errorInfo());
}
}
} else {
echo 'Inserting...<br/>';
$sql = 'INSERT INTO sessions (session_name, session_data) ';
$sql .= 'VALUES(:name, :data)';
if ($rel = $this->_link->prepare($sql)) {
if ($rel->execute([':name' => $name, ':data' => $data])) {
echo 'Insert success...<br/>';
} else {
echo 'Insert failed...<br/>';
var_dump($rel->errorInfo());
}
}
}
}
}
}
}
Test code:
new SessionManagement();
session_start();
$_SESSION['test_var1'] = 'some test data';
session_write_close(); // Making sure writing is finished
echo $_SESSION['test_var1'];
Output via test page
open called
Session data: a:1:{s:9:"test_var1";s:14:"some test data";}
Inserting...
Insert success...
close called
some test data
Relevant database fields
session_name: _g_h8m64bsb7a72dpj56vgojn6f4k3ncdf97leihcqfupg2qtvpbo20
session_data: a:0:{}
I'm not sure if this is a database issue or a PHP issue. I've been messing with this for a few days now and decided it was time to ask the community. Hopefully someone has some insight as to what the problem is.
I think you must initialize PDO object outside of the Open function handler and the class itself
try to access to your PDO Object with a Global value or through a static variable.
This is my implementation with MYSQL for my project :
class Core_DB_SessionHandler implements SessionHandlerInterface
{
protected $options = array(); // Options de la session
protected static $db = NULL; // Acceder a la BDD
public function __construct($options, $pdo) {
$this->options = $options;
self::$db = $pdo;
}
public function open($savePath, $sessionName) {
$now = time();
$req = self::$db->prepare("DELETE FROM tbl_sessions WHERE expire < '{1}' ");
$req->execute(array($now));
return TRUE;
}
public function close() {
$this->gc(ini_get('session.gc_maxlifetime'));
}
public function read($id) {
$now = time();
$stmt = self::$db->query("SELECT data FROM tbl_sessions WHERE sid = '$id AND expire < '$now'");
$result = $stmt->fetchColumn();
return $result;
}
public function write($id, $data) {
if (array_key_exists('TIMEOUT', $_SESSION)) {
$newExp = $_SESSION['TIMEOUT'];
}
else {
$newExp = time() + $this->options['time_limiter'];
}
try {
$req = self::$db->prepare('INSERT INTO tbl_sessions (sid, data, expire) VALUES (:sid, :data, :expire)
ON DUPLICATE KEY UPDATE data = :data, expire = :expire');
$vals = array('sid' => $id, 'data' => $data, 'expire' => $newExp);
$req->execute($vals);
return TRUE;
}
catch (PDOException $e) {
throw new Core_Exception(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
}
}
public function destroy($id) {
$stmt = self::$db->prepare("DELETE FROM tbl_sessions WHERE sid = '{1}'");
$stmt->execute(array($id));
//return ($stmt->rowCount() === 1) ? true : false;
return TRUE;
}
public function gc($maxlifetime) {
$now = time();
$req = self::$db->prepare("DELETE FROM tbl_sessions WHERE expire < '{1}' ");
$req->execute(array($now));
return TRUE;
}
}
and i initialize handler like this :
$handler = new Core_DB_SessionHandler($MyOptions, $MyPDO);
if (PHP5) {
if (!session_set_save_handler($handler, TRUE)) {
throw new Core_Exception('Erreur lors de l\'init des sessions !');
}
}
nb : In your Table structure don't use autoincrement for ID
Well, I've solved my problem, but it's hard to say if this fix was actually the problem in the first place.
In the read method, I changed the follow:
return $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
to
$data $rel->fetch(PDO::FETCH_ASSOC)['session_data'];
return $data;
After this the session was writing $session_data to the database without any problem. That's all well and dandy, but it doesn't explain why it didn't work in the first place. I mainly say this because upon switching the statement back everything continued to work. As in, I can't reproduce the issue now. So it's hard for me to even say that this was the problem in the first place.
Hopefully this helps someone. I've been unable to find more information about it, but it something does show up I'll be sure to add it here.
I am trying to change my database from MySQL to PDO. It is difficult and have to solve a lot of errors.
I don't know how to solve this error.
Fatal error: Call to undefined function UNIX_TIMESTAMP() in /... on line 63.
This is the part with the error
function create_album($album_name, $album_description) {
global $database;
$database->query("INSERT INTO albums(album_id, id, timestamp, name, description)
VALUES (':album_id', '".$_SESSION['id']."',
UNIX_TIMESTAMP(), ':album_name', ':album_description')", array('$_SESSION[id]' => ':session_id',
':timestamp' => UNIX_TIMESTAMP(), ':album_name' => $album_name, ':album_description' => $album_description));//this is line 63
mkdir('uploads/'.$album_name, 0744);
mkdir('uploads/thumbs/'.$album_name, 0744);
}
Why is there a fatal error, do I use unix_timestamp wrong here? And how can I solve this problem?
Thanks.
UNIX_TIMESTAMP() isn't a PHP function. Furthermore, you've already got it in your SQL and there's no :timestamp parameter in the SQL string. So just lose the ':timestamp' => UNIX_TIMESTAMP() and you should be good.
Also, your session_id part is messed up. First you're dumping the session id directly into the SQL string, and then you're adding it to the parameter array, but with the value and key reversed.
UNIX_TIMESTAMP is a MySQL function, not a PHP one. You don't need to bind a param to UNIX_TIMESTAMP, you can just use it in your query.
Get rid of the ':timestamp' => UNIX_TIMESTAMP(), this is your issue. First UNIX_TIMESTAMP is not a PHP function, and :timestamp is nowhere in your query.
Also, ':session_id' should be the key.
UPDATE: You need to use prepare/execute instead of query to run prepared statements.
$stmt = $database->prepare("INSERT INTO albums(id, timestamp, name, description) VALUES (':session_id', UNIX_TIMESTAMP(), ':album_name', ':album_description')");
$stmt->execute(array(
':session_id' => $_SESSION[id],
':album_name' => $album_name,
':album_description' => $album_description
));
I'm assuming album_id is AUTO_INCREMENT. If not, you should add it to the query.
UNIX_TIMESTAMP() is not a PHP function, is from MYSQL. Instead use time()
Rocket Hazmat,
the database comes from member.php;
require_once('config.inc.php');
require_once("database.class.php");
require_once("member.class.php");
require("album.func.php");
require("image.func.php");
require("thumb.func.php");
/* Start an instance of the Database Class */
$database = new database("xxx", "xxx", "xxx", "xxx");
/* Create an instance of the Member Class */
$member = new member();
the database.class.php is;
class database {
public $pdo = null;
public $statement = null;
* Database Constructor
*
* This method is used to create a new database object with a connection to a datbase
*/
public function __construct() {
/* Try the connections */
try {
/* Create a connections with the supplied values */
$this->pdo = new PDO("mysql:host=" . Config::read('hostname') . ";dbname=" . Config::read('database') . "", Config::read('username'), Config::read('password'), Config::read('drivers'));
/*
* Database Query
*
* This method is used to create a new database prepared query
*
* #param string $query The prepared statement query to the database
* #param array|string $bind All the variables to bind to the prepared statement
* #return return the executed string
*/
public function query($query, $bind = null, $fetch = 'FETCH_ASSOC') {
/* Prepare the query statement */
$this->statement = $this->pdo->prepare($query);
/* Bind each value supplied from $bind */
if($bind != null) {
foreach($bind as $select => $value) {
/* For each type of value give the appropriate param */
if(is_int($value)) {
$param = PDO::PARAM_INT;
} elseif(is_bool($value)) {
$param = PDO::PARAM_BOOL;
} elseif(is_null($value)) {
$param = PDO::PARAM_NULL;
} elseif(is_string($value)) {
$param = PDO::PARAM_STR;
} else {
$param = FALSE;
}
/* Bid value */
if($param) {
$this->statement->bindValue($select, $value, $param);
}
}
}
/* Execute Query & check for any errors */
if(!$this->statement->execute()){
$result = array(
1 => 'false',
2 => '<b>[DATABASE] Error - Query:</b> There was an error in sql syntax',
);
return $result;
}
/* Return all content */
if($fetch == 'FETCH_ASSOC') {
$result = $this->statement->fetch(PDO::FETCH_ASSOC);
} elseif($fetch == 'FETCH_BOTH') {
$result = $this->statement->fetch(PDO::FETCH_BOTH);
} elseif($fetch == 'FETCH_LAZY') {
$result = $this->statement->fetch(PDO::FETCH_LAZY);
} elseif($fetch == 'FETCH_OBJ') {
$result = $this->statement->fetch(PDO::FETCH_OBJ);
} elseif($fetch == 'fetchAll') {
$result = $this->statement->fetchAll();
}
return $result;
}
}
I have an odd issue. The first time a visitor comes to the site and I set anything is the session, it doesn't stick. The second and all the following times I try to set something it sticks. After the initial try I can destroy the session and set something and it sticks. Its just the initial attempt to save something fails. I'm trying to save something to the session with $_SESSION['uid'] = $row["Id"];. I know the $row["Id"] is valid and holds data (I echoed it).
I am not using standard sessions. I am saving the session into a database. My session class is below. Is there anything I'm missing or doing wrong to explain this behavior?
Update:
Well I tested the session class on its own and it seems to be working :-/ But when I use it in my larger application _write never gets called, though __destruct does get called. Any idea why that may be?
<?php
include_once('db.php');
class PDOSession
{
protected $pdo;
protected $table = 'SessionData';
public function __construct()
{
// Get a database connection
$db = new PDOConnectionFactory();
$this->pdo = $db->getConnection(true);
// Start session
session_set_save_handler(array($this, '_open'),
array($this, '_close'),
array($this, '_read'),
array($this, '_write'),
array($this, '_destroy'),
array($this, '_gc'));
session_start();
}
public function __destruct()
{
session_write_close();
}
protected function fetchSession($id)
{
$stmt = $this->pdo->prepare('SELECT id, data FROM '.$this->table.' WHERE id = :id AND unixtime > :unixtime');
$stmt->execute(array(':id' => $id, ':unixtime' => (time() - (int)ini_get('session.gc_maxlifetime'))));
$sessions = $stmt->fetchAll();
return empty($sessions) ? false : $sessions[0];
}
public function _open($savePath, $sessionName)
{
return true;
}
public function _close()
{
return true;
}
public function _read($id)
{
$session = $this->fetchSession($id);
return ($session === false) ? false : $session['data'];
}
public function _write($id, $sessionData)
{
$session = $this->fetchSession($id);
if($session === false) {
$stmt = $this->pdo->prepare('INSERT INTO '.$this->table.' (id, data, unixtime) VALUES (:id, :data, :time)');
} else {
$stmt = $this->pdo->prepare('UPDATE '.$this->table.' SET data = :data, unixtime = :time WHERE id = :id');
}
$stmt->execute(array(
':id' => $id,
':data' => $sessionData,
':time' => time()
));
}
public function _destroy($id)
{
$stmt = $this->pdo->prepare('DELETE FROM '.$this->table.' WHERE id = :id');
$stmt->execute(array(':id' => $id));
}
public function _gc($maxlifetime)
{
$stmt = $this->pdo->prepare('DELETE FROM '.$this->table.' WHERE unixtime < :time');
$stmt->execute(array(':time' => (time() - (int) $maxlifetime)));
}
}
$newPDOSessionStartHere = new PDOSession();
I'm a bit of an idiot I guess. I was calling session_destroy() rather than session_unset() to clear things out at the top of my authentication script. The class works fine.
I think that you should start a session after you define your class. Not inside.
i think my problem is a little weird, ive wrote a little class to log in/out user, entire system is based in MVC pattern, and my libraries are custom and self written,
but the problem: when i log in the user with out the $remember flag, no problems, sessions are easily set and unset. but when i activate the $remember and i create a cookie, my logout function does not work, it deletes the session but not the cookie, so cookie remains in browser with complete data, and whenever i reload the page , my class logs me in again because there is a cookie with full authentication details, the problem is with a cookie, i cant send other headers like Location: blah blah from $this->logout; ive tried different ways to unset cookie, but no luck;
this is the class:
<?php
/**
* Pars----
* Classified --- Application
*
* #author Sallar Kaboli (me#sallar.ir)
* #copyright Copyright (C) 2009 - 2010 -----CO (dev#------.us)
* #package Pars---
* #version 1.0
*/
class ParsUser {
public $userID = false;
private $_p = null;
public $userData = null;
public function __construct() {
$this->_p = ParsRegistry::getInstance();
if( $this->_p->sess->isVarSet('parsUser') ) {
$this->loadUser($this->_p->sess->getVar('parsUser'));
}
if( isset($_COOKIE['parsUser']) and !$this->isLoggedIn() ) {
$userFromCookie = unserialize(base64_decode($_COOKIE['parsUser']));
$this->checkLogin($userFromCookie['username'], $userFromCookie['password'], true, false);
}
}
public function checkLogin($username, $password, $remember = true, $hash = true) {
if(!empty($username) and !empty($password)) {
$password = $hash ? $this->_p->valid->hash($password) : $password;
$qData = array('user' => $username, 'pass' => $password);
$query = 'SELECT * FROM people WHERE `username` = :user AND `password` = :pass';
$user = $this->_p->db->getRow($query, $qData);
if(is_object($user) AND !empty($user->id)) {
$this->userID = $user->id;
$this->userData = $user;
if( $hash ) {
$this->_p->db->execute('UPDATE people SET `last_login` = ? WHERE `id` = ?', array( time(), $this->userID ));
}
$this->loginTheUser($remember);
return true;
}
else {
return false;
}
}
else {
return false;
}
}
private function loginTheUser($remember = true) {
$this->_p->sess->setVar('parsUser', $this->userID);
if( $remember ){
$rememberPeriod = $this->_p->conf->c['cookie_remember_period'];
$cookie = array(
'username' => $this->userData->username,
'password' => $this->userData->password
);
$cookie = base64_encode(serialize($cookie));
setcookie('parsUser', $cookie, time() + $rememberPeriod/*, '/', $_SERVER['HTTP_HOST']*/);
}
return false;
}
private function loadUser($userID){
$user = $this->_p->db->getRow('SELECT * FROM people WHERE `id` = ?', $userID);
if( is_object($user) and ( $user->id == $userID ) ){
$this->userID = $user->id;
$this->userData = $user;
$this->_p->sess->setVar('parsUser', $this->userID);
return true;
}
else return false;
}
public function logout($redirectTo = false) {
setcookie('parsUser', '', mktime(12,0,0,1, 1, 1990));
unset($_COOKIE['parsUser']);
$this->_p->sess->sessionDrop();
$this->userData = null;
$this->userID = false;
if ( !empty($redirectTo) ){
$this->_p->core->redirect($redirectTo);
exit;
}
}
public function isLoggedIn() {
//return ( empty($this->userID) OR !$this->userID ) ? false : true;
if( $this->userID > 0 and $this->userID != false and !empty($this->userID) )
return true;
else return false;
}
public function checkAccess($level) {
if($level == 0) {
return true;
}
elseif($this->isLoggedIn()) {
if( $this->userData->level <= $level ) {
return true;
}
else {
return false;
}
}
else {
return false;
}
}
public function getUserData() {
$args = func_get_args();
if( empty($this->userID) )
throw new Exception('User is not loaded.');
if( is_array($args) and count($args) > 0){
foreach( $args as $arg ){
if( !isset($this->userData->$arg) )
throw new Exception('Unknown property: <b>' . $property . '</b>');
else
$props[$arg] = $this->userData->$arg;
}
if( count($args) == 1 )
return $props[$args[0]];
else
return $props;
}
else{
$props = $this->userData;
unset($props->password);
return $props;
}
}
}
sorry for my english.
try to set $path in both cases (create, remove cookie)
You could set the cookie with invalid data:
setcookie('parsUser', 'trash', time() - 3600);
If this doesn't delete the cookie, it should at least create an invalid login token.
I would also recommend against base64 encoding the username and password in the cookie. It's all but plaintext to an experienced attacker. You can easily integrate strong encryption into your application using mcrypt:
http://us2.php.net/mcrypt
If that's not an option, you can use a php-only implementation of xor encrypt with strong keys. There are also php-only implementations of blowfish and Rijndael on the internet. I'd provide links if I could, but my current reputation won't let me add more than one hyperlink.
Sorry for my strange question but why do you set a cookie in history (so it's gets deleted) and then unset a cookie that doesn't exist anymore?
Hmm looks strange if you look at the output of your image...
What is the system time of your server?
Because it said the cookie would expire in 1990.. Looks like your computer/server is way behind if that is still valid :P
By the way, maybe you can't edit the data, because you need to decode the base64 encryption first (because in your code when you set it, you use the base64 function)