I'm testing the implementation of a security check in my PHP sessions. I can successfuly detect whether the session was started from another IP address and I can successfully start a new session. However, the data from the old session gets copied into the new one! How can I start a blank session while preserving the previous session data for its legitimate owner?
This is my code so far, after lots of failed attempts:
<?php
// Security check
if( isset($_SESSION['ip_address']) && $_SERVER['REMOTE_ADDR']!=$_SESSION['ip_address'] ){
// Check failed: we'll start a brand new session
session_regenerate_id(FALSE);
$tmp = session_id();
session_write_close();
unset($_SESSION);
session_id($tmp);
session_start();
}
// First time here
if( !isset($_SESSION['ip_address']) ){
$_SESSION['ip_address'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['start_date'] = new DateTime;
}
The official documentation about sessions is terribly confusing :(
Update: I'm posting some findings I got through trial and error. They seem to work:
<?php
// Load the session that we will eventually discard
session_start();
// We can only generate a new ID from an open session
session_regenerate_id();
// We store the ID because it gets lost when closing the session
$tmp = session_id();
// Close session (doesn't destroy data: $_SESSION and file remains)
session_destroy();
// Set new ID for the next session
session_id($tmp);
unset($tmp);
// Start session (uses new ID, removes values from $_SESSION and loads the new ones if applicable)
session_start();
Just call session_unset after session_regenerate_id to reset $_SESSION for the current session:
if (isset($_SESSION['ip_address']) && $_SERVER['REMOTE_ADDR']!=$_SESSION['ip_address']) {
// Check failed: we'll start a brand new session
session_regenerate_id(FALSE);
session_unset();
}
when a new user connects to your server, the script should only be able to access that user's session variables. you will want to store other info in a hashed session variable to verify that the session is not being jacked. if it is being jacked, no reason to start a new session, maybe just exit the script with a warning.
here is the function a lot of people use for fingerprinting a session:
function fingerprint() {
$fingerprint = $server_secure_word;
$fingerprint .= $_SERVER['HTTP_USER_AGENT'];
$blocks = explode('.', $_SERVER['REMOTE_ADDR']);
for ($i=0; $i<$ip_blocks; $i++) {
$fingerprint .= $blocks[$i] . '.';
}
return md5($fingerprint);
}
Use this
unset($_SESSION['ip_address'])
instead of 'unset($_session)'
You can also use session_destroy.
session_destroy will destroy session data. For example,
session_start();
$_SESSION["test"] = "test";
session_write_close();
session_start();
// now session is write to the session file
// call session_destroy() will destroy all session data in the file.
session_destroy();
// However the you can still access to $_SESSION here
print_r($_SESSION);
// But once you start the session again
session_start();
// all session data is gone as the session file is now empty
print_r($_SESSION);
will output
array([test] => "test")array()
Related
I am creating a small login facility. i would like it to be simple but also secure.
I wanted to timeout my session after 30 minutes of inactivity. I saw a solution for this here by Gumbo. However I am unsure where to add the code to my own code... Can somebody help me ...
Here is the solution which i want to add into my code (by Gumbo) and underneath that is my own login.php page:
Conclusion / best solution (from another stackoverflow post ):
The best solution is to implement a session timeout of your own. Use a simple time stamp that denotes the time of the last activity (i.e. request) and update it with every request:
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 1800)) {
// last request was more than 30 minutes ago
session_unset(); // unset $_SESSION variable for the run-time
session_destroy(); // destroy session data in storage
}
$_SESSION['LAST_ACTIVITY'] = time(); // update last activity time stamp
Updating the session data with every request also changes the session file's modification date so that the session is not removed by the garbage collector prematurely.
You can also use an additional time stamp to regenerate the session ID periodically to avoid attacks on sessions like session fixation:
if (!isset($_SESSION['CREATED'])) {
$_SESSION['CREATED'] = time();
} else if (time() - $_SESSION['CREATED'] > 1800) {
// session started more than 30 minutes ago
session_regenerate_id(true); // change session ID for the current session and invalidate old session ID
$_SESSION['CREATED'] = time(); // update creation time
}
login.php
<?php
session_start();
header('Content-Type: text/html; charset=utf-8');
require("database.php");
require("phpfunctions.php");
if(isset($_POST["log_out"]) && ($_POST["log_out"] == '1')) {
//this means we have come from another page after pressing the log out button
//so therefore we remove session variables and destroy session
session_unset();
session_destroy();
//$log_out_message = "You have been logged out";
}
if (isset($_SESSION["username"])) {
//if the username session variable is already set then they are already logged in so send them to the index page
//we will perform further checks there on the validity of the session variables
header("Location: index.php");
exit();
}
//collect the post data if the login form has been submitted
if (isset($_POST["username"]) && isset($_POST["password"])){
$username = preg_replace('#[^A-Za-z0-9]#i', '', $_POST["username"]); // filter everything but numbers and letters
$password = preg_replace('#[^A-Za-z0-9]#i', '', $_POST["password"]); // filter everything but numbers and letters
//check if this username and password exist in our database and are therefore valid
$query = "SELECT * FROM users WHERE username=:username LIMIT 1";
$statement = $pdoConnection->prepare($query);
$statement->bindValue(':username', $username, PDO::PARAM_STR);
$statement->execute();
$statement->setFetchMode(PDO::FETCH_ASSOC);
$count = 0;
while($row = $statement->fetch()){
//username exists.
if (password_verify($password, $row["hashedPassword"])) {
//password is verified
//store the hashedPassword into a variable.
$dbHashedValue = $row["hashedPassword"];
$id = $row["userID"];
$count++;
}
}
//if count is 1 that means we found matching username in our database and also have verifed the password
if($count == 1){
//If our login credentials are matched with the database and therefore valid we store the values into session variables.
//$_SESSION['incorrectLogin'] = false;
$_SESSION["userID"] = $id;
$_SESSION["username"] = $username;
$_SESSION["password"] = $dbHashedValue;
//all login information is correct and we have stored it into SESSION variables so
//we are ready to allow the user in to our system
header("Location: index.php");
//exit the rest of the script
exit();
}else if($count == 0){
//create generic message without giving too much information away to the user in order to be more secure.
$incorrectLoginDetails = "Invalid Login! Please try again!";
}
}
?>
index.php
<?php
session_start();
header('Content-Type: text/html; charset=utf-8');
require("database.php");
require("phpfunctions.php");
//check if the username session variable exists
//this will exist only if the person has passed the login stage therefore we now know they are logged in
if(!isset($_SESSION['username'])){
header('Location: login.php');
exit();
}
//also need to check this username exists in the database and also that the session password matches up with the database.
?>
I have a file where the $_SESSION['username'] variable is first created:
<?php
$kullaniciadi = $_POST['kullaniciadi'];
$sifre = $_POST['sifre'];
if ((!$kullaniciadi =="") and (!$sifre =="")) {
include("db.php");
$sql = $sqlt->query("select * from uye where kullaniciadi='$kullaniciadi' and sifre='".md5($sifre)."'");
$kayitsayisi = mysqli_num_rows($sql);
if ($kayitsayisi == "0") {
header ("Location: login.php?hata=yes");
}
else {
$kontrol_ok = $sql -> fetch_assoc();
$k=$kontrol_ok["kullaniciadi"];
session_start();
$_SESSION['username']= $k;
header ("Location: homepage.php");
}
}
else {
header ("Location: login.php?hata=yes");
}
?>
It is called login_do.php (I send MySQL data from login.php form to here, and do the username and password check in this file).
Than in every other PHP file I have, I begin with:
<?php
session_start();
if (isset($_SESSION["username"])) {
echo 'loginok';
} else {
header ("Location: login.php");
}
?>
Than I have a logout.php, where the user is redirected to if he presses a button. logout.php file contains this:
<?php
session_start();
if(isset($_SESSION['username'])) {
unset($_SESSION['username']);
}
session_destroy();
header("Location: login.php")
?>
But it simply doesn't work. I mean if I go into my browsers cookies and delete the SESSION cookie by myself, than yes, the whole system works and I can't access any other php files than login.php unless I log in. But I need this to work with logout.php instead of me deleting the session from the browser by myself manually.
session_unset();
session_destroy() destroys all of the data associated with the current session. It does not unset any of the global variables associated with the session, or unset the session cookie. To use the session variables again, session_start() has to be called.
In order to kill the session altogether, like to log the user out, the session id must also be unset. If a cookie is used to propagate the session id (default behavior), then the session cookie must be deleted. setcookie() may be used for that.
--http://php.net/manual/en/function.session-destroy.php
I have a script which is meant to finish the current session and start a new one. There is a segment of code I use and it works fine on my development computer. However, when I posted it to the production server, the session id is constantly remaining the same.
The following is my code for restarting the session:
session_start();
$_SESSION = array();
$_POST = array();
$_GET = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(),
'',
time() - 42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]
);
}
session_destroy();
session_write_close();
session_start();
session_regenerate_id(true);
On the last line, a new session id is not generated.
Why would this be different between two servers running PHP? And what can I do to correct it?
After some experimenting I solved the problem.
The session_regenerate_id(true) line does not regenerate a new session id if any text has been written into the response. I already had a series of echo statements issuing text for debugging purposes and after I removed them new session ids were created.
session_regenerate_id() updates the current session id with a newly generated one. It does not change session variables.
echo session_id();
session_regenerate_id();
echo session_id();
You should unset session to do that:
unset($_SESSION); // or
$_SESSION = array();
How to start a new session:
session_start();
session_destroy();
session_regenerate_id();
unset($_SESSION);
session_start();
The weirdest thing is happening, when I logout of my app it redirects me to the correct page, so the script runs. However when I randomly type in a page that I should not have access to since my sessions and cookies have been destroyed I have access to it, this only happens on my hosted server, on local host it works fine, has anyone run into this before?
The start sessions script
<?php
session_start();
// If the session vars aren't set, try to set them with a cookie
if (!isset($_SESSION['user_id'])) {
if (isset($_COOKIE['user_id']) && isset($_COOKIE['user_email'])) {
$_SESSION['user_id'] = $_COOKIE['user_id'];
$_SESSION['user_email'] = $_COOKIE['user_email'];
$_SESSION['lawyer_client'] = $_COOKIE['lawyer_client'];
}
}
?>
The log out script
<?php
// If the user is logged in, delete the session vars to log them out
session_start();
if (isset($_SESSION['user_id'])) {
// Delete the session vars by clearing the $_SESSION array
$_SESSION = array();
// Delete the session cookie by setting its expiration to an hour ago (3600)
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 7600);
}
// Destroy the session
session_unset();
session_destroy();
// Delete the user ID and username cookies by setting their expirations to an hour ago (3600)
setcookie('user_id', '', time() - 7600);
setcookie('user_email', '', time() - 7600);
setcookie('lawyer_client', '', time() - 7600);
// Redirect to the home page
$home_url = 'http://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . '/index.php';
header('Location: ' . $home_url);}
?>
I am checking to see if the session is set using this script
require_once('startsession.php');
if (!isset($_SESSION['user_id'])) {
echo '<p class="login">Please log in to access this page.</p>';
exit();
}
So after looking at what I just put down my first guess would be that my logout script is not properly clearing my sessions...but why is it only not doing it on my shared host?
In some shared hosts you will have to include the sessions directory in order to work. Are you sure that the sessions are correctly initialized?
Okay when I log in as user 1 my PHP SESSIONS will stay logged in as user 1 until I leave or refresh the page and magically I'm logged in as user 2. What can be causing this problem?
Here is what I have at the top of all my pages.
ob_start(); // Start output buffering.
session_start(); // Initialize a session.
Here is some more code.
ob_start(); // Start output buffering.
session_start(); // Initialize a session.
$page = 'title';
include ('../includes/header.php');
require_once ('../includes/config.inc.php');
require_once ('../mysqli_connect.php'); // Connect to the db.
$mysqli = mysqli_connect("localhost", "aff", "adad", "adad");
if (!isset($_SESSION['user_id'])) {
$url = BASE_URL . 'index.php'; // Define the URL.
ob_end_clean(); // Delete the buffer.
header("Location: $url");
exit(); // Quit the script.
}
Are you destroying the sessions if you are logging in as user 2?