I've been trying to get my sessions running across my subdomains, which I'm pretty sure I got working on Monday but after adding some code Tuesday its not working Wednesday! I've used the code ini_set("session.cookie_domain", $domain); where $domain = .example.com.
My site's main page is currently located on test.example.com and I access the login page through test.example.com/login. When i enter this address, the url in the address bar is automatically changed to http://www.test.example.com/login, and this is where the problem lies. The session is created for www.test.example.com but most links on the site direct to test.example.com/<sub folder>.
The only thing I can think of that might be throwing it off is the way I handle sessions. In every page a session is started. First the ini_set("session.cookie_domain", $domain); is set, then the session is started. Next I check to see if the session has expired. If the session has expired the current session is destroyed and unset then a new session is created. The rest is just setting up user information.
The only thing I've added recently is the session expiry checker. I've tried bypassing it but it hasn't changed anything.
Any help is greatly appreciated. I can post code if it makes it easier.
Mike
Please add some code :).
I can only tell you how we achieved the same functionality. Try adding
<directory "/path/to/your/docroot">
php_value session.cookie_domain ".example.com"
</directory>
to your virtual host configs. This was the only thing we had to do to make this functionality work. Now we can access all subdomains with the same cookies without adding all the extra code. I don't say this is a solutions, but this approach makes testing a lot less complicated.
Edit
You can set virtual hosts in the configuration of your webserver. Assuming you use apache they will be either in httpd.conf or are present in other files on the filesystem which are included in your httpd.conf. Where httpd.conf is located on your system depends on your configuration, but if you use Linux it will probably be somewhere in /etc/apache, /etc/httpd, /usr/local/apache, /usr/local/httpd
Once you have located this file it will have one or more entries like this:
<VirtualHost *:80>
ServerAdmin webmaster#yourdomain.org
DocumentRoot /var/www/yourdomain/www
ServerName yourdomain.org
<directory "/var/www/yourdomain/www">
Options FollowSymLinks Includes
AllowOverride All
Order allow,deny
Allow from all
</directory>
</VirtualHost>
And modify the code that it looks like this:
<VirtualHost *:80>
ServerAdmin webmaster#yourdomain.org
DocumentRoot /var/www/yourdomain/www
ServerName yourdomain.org
<directory "/var/www/yourdomain/www">
Options FollowSymLinks Includes
AllowOverride All
Order allow,deny
Allow from all
php_value session.cookie_domain ".yourdomain.org"
</directory>
</VirtualHost>
Notice the php_value session.cookie_domain ".yourdomain.org" line.
Add this line to all server configuration for this domain and your cookies will be shared.
This is impossible to debug without knowing more details.
You might want to first check if the cookies are being set properly, and if they are actually being returned to the server.
Use a tool which lets you see headers on your browser (webdeveloper toolbar / liveheaders / firebug for Firefox) and see if the server is actually requesting that the browser accept a cookie - and for what.
Forgive me for not knowing but what 'virtual host configs' is. My code runs something like this:
The main page will include session.php
function Session()
{
$this->time = time();
$this->startSession();
}
function startSession()
{
global $serverFunctions;
$serverFunctions->setSubdomainSharing();
session_start();
$this->checkSessionLife();
//check if user is logged in
$this->logged_in = $this->checkLogin();
//if user is not logged in then it is given guest credintials
if (!$this->logged_in)
{
$this->user_name = $_SESSION['user_name'] = GUEST_NAME;
$this->user_level = $_SESSION['user_level'] = GUEST_LEVEL;
}
if (!isset($_SESSION['language']))
{
$this->setLanguage("translation_english");
}
else
{
$this->user_language = $_SESSION['language'];
}
}
function checkSessionLife()
{
global $serverFunctions;
if (isset($_SESSION['start_time']))
{
$session_life = time() - $_SESSION['start_time'];
if ($session_life > 15)
{
$this->logout();
$serverFunctions->setSubdomainSharing();
session_start();
}
}
else if (!isset($_SESSION['start_time']))
{
//logout any session that was created
//before expiry was implemented
$this->logout();
$serverFunctions->setSubdomainSharing();
session_start();
}
$_SESSION['start_time'] = time();
}
function logout()
{
global $database;
// Unset session variables
session_destroy();
session_unset();
//session_regenerate_id(true);
$this->logged_in = false;
// Set user level to guest
$this->user_name = GUEST_NAME;
$this->user_level = GUEST_LEVEL;
}
The session file includes another PHP file called serverFunctions. This is just a class that allows me to format URL and such.
function getAddressPrefix()
{
$address_prefix = "";
if ($_SERVER['SERVER_ADDR'] == '127.0.0.1')
{
$address_prefix = "http://localhost/myproject";
}
else
{
$address_prefix = $this->getServerName();
}
return $address_prefix;
}
function getServerName()
{
return "http://" . str_replace("www.", "", $_SERVER['SERVER_NAME']);
}
function formatRequestingPage()
{
return $this->getServerName() . $_SERVER['SCRIPT_NAME'];
}
function setSubdomainSharing()
{
if ($_SERVER['SERVER_ADDR'] != '127.0.0.1')
{
$domain = $this->getServerName();
do
{
$domain = substr($domain, strpos($domain, ".", 0) + 1);
}
while (substr_count($domain, ".") > 1);
$domain = ".".$domain;
ini_set("session.cookie_domain", $domain);
}
}
When the user logs in, the login request is handled by process_request.php
function LoginReq()
{
global $session;
global $variables;
global $serverFunctions;
$retval = $session->login($_POST['user_name'], $_POST['password']);
if ($retval)
{
header("Location: " . $serverFunctions->getAddressPrefix());
exit();
}
else
{
$_SESSION['variables_array'] = $_POST;
$_SESSION['error_array'] = $variables->getErrorArray();
header("Location: " . $serverFunctions->getAddressPrefix() . "/login/");
exit();
}
}
If I'm missing anything or need to explain what happens a bit more let me know.
Related
I'm trying to login to the main page of SuiteCRM, an open source software that provides a CRM model for large companies, and I'm having some problems logging in.
index.php?action=Login&module=Users&login_module=Home&login_action=index (url)
On the local server I am using Wampserver 3.2.6 (64 bits), PHP 7.4.26 and MariaDB 10.3.28 database. In the line that the error indicates, only the command session_start() appears and nothing else, so when I try to log in to the system, it remains on the same screen after putting the credentials, because there is some configuration inside the SuiteCRM folders that prevents the software or the local server itself to store sessions.
index.php?action=Login&module=Users&login_module=Home&login_action=index (url)
include\MVC\SugarApplication.php (file path)
public function startSession()
{
$sessionIdCookie = isset($_COOKIE['PHPSESSID']) ? $_COOKIE['PHPSESSID'] : null;
if (isset($_REQUEST['MSID'])) {
session_id($_REQUEST['MSID']);
session_start();
if (!isset($_SESSION['user_id'])) {
if (isset($_COOKIE['PHPSESSID'])) {
self::setCookie('PHPSESSID', '', time() - 42000, '/');
}
sugar_cleanup(false);
session_destroy();
exit('Not a valid entry method');
}
} else {
if (can_start_session()) {
session_start(); // <= here is the error line #609
}
}
//set the default module to either Home or specified default
$default_module = !empty($GLOBALS['sugar_config']['default_module']) ? $GLOBALS['sugar_config']['default_module'] : 'Home';
//set session expired message if login module and action are set to a non login default
//AND session id in cookie is set but super global session array is empty
if (isset($_REQUEST['login_module']) && isset($_REQUEST['login_action']) && !($_REQUEST['login_module'] == $default_module && $_REQUEST['login_action'] == 'index')) {
if (!is_null($sessionIdCookie) && empty($_SESSION)) {
self::setCookie('loginErrorMessage', 'LBL_SESSION_EXPIRED', time() + 30, '/');
}
}
LogicHook::initialize()->call_custom_logic('', 'after_session_start');
}
What is the best alternative to solve this problem?
I am facing some weird issue of session variable getting reset on action redirect.
I am using Codeigniter and redirecting to dashboard action after login, I am getting data in login action after verifying credentials with DB, but when I use redirect() to redirect to dashboard, session variables gets vanished.
Admin.php
<?php class admin extends CI_Controller
{
function login()
{
$login = $this->Admin_model->login($this->input->post()); // <-- verify data and set to session
if($login)
{
$this->session->set_flashdata("success","Logged in Successfully");
var_dump($_SESSION); // <-- able to fetch data from session
// exit();
redirect("admin/dashboard");
}
else
{
$this->session->set_flashdata("error","Invalid Credentials!! Please Try Again!!");
redirect("admin");
}
}
function dashboard()
{
var_dump($_SESSION); // <-- session data is vanished and not able to get userdata('id')
exit();
if($this->session->userdata('id') != '')
{
$data['active_tab'] = "dashboard";
}
else
{
redirect("admin");
}
}
?>
Admin_model.php
<?php Class Admin_Model extends CI_Model
{
function login($data)
{
$user = $this->db->get_where("users",array("username" => $data['username'],
"password" => md5($data['password']),
"is_active" => "1")
)->row_array();
if(!empty($user))
{
$this->set_user_session($user);
return true;
}
else
{
return false;
}
}
function set_user_session($login)
{
$arr = array();
$arr["id"] = $login["id"];
$arr["username"] = $login["username"];
$this->session->set_userdata($arr);
}
?>
Tried this in xampp and wamp, all browsers but still the issue remains the same, any help would be grateful.
Which version of CodeIgniter are you working with? You can try the following steps.
Go to system/libraries/Session/Session.php
Comment session_start() by adding //. We want to relocate the sessionn_start().
Find (using ctrl + f) a comment that says Security is king. Comment out all the line under that comment until the end of the function. In my case I commented out the line number 315 - 320.
on line number 282 change this line ini_set('session.name', $params['cookie_name']); to ini_set('session.id', $params['cookie_name']);
comment out following lines
line 108 //session_set_save_handler($class, TRUE);
line 290-296
// session_set_cookie_params(
// $params['cookie_lifetime'],
// $params['cookie_path'],
// $params['cookie_domain'],
// $params['cookie_secure'],
// TRUE // HttpOnly; Yes, this is intentional and not configurable for security reasons
// );
line 305 //ini_set('session.gc_maxlifetime', $expiration);
Go to the main index.php, it is the first index.php and located in the same directory with the sub-directories 'application', 'system', 'user_guide', etc.
Put session_start() right after < ?php
Hope this can help you....
You have to use this->session->set_userdata() for setting the session. this->session-> set_ flashdata() is used for setting flash messages which are removed after next action.
The new versions of the browsers might be destroying the session because of the new cookie policy.
References
https://developers.google.com/search/blog/2020/01/get-ready-for-new-samesitenone-secure
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
Whenever the cookie is required to be sent to server, the browser sees the SameSite attribute to decide if the cookie to be sent to server or blocked. For user actions, it is sent to the server but for auto-redirects, it doesn't if SameSite is set to 'Strict' or 'Lax' (Lax is going to be the default value now).
Solution:
The cookie attribute SameSite can be set to 'None' along with specifying the 'Secure' attribute to 'true'. Setting 'Secure' attribute to 'true' would require your site to run on https. Sites running with http:// protocol will not be able to set 'Secure' cookie.
Please set the 'HttpOnly' attribute to 'true' for making it accessible for http requests to the server only.
In PHP, it can be achieved as below
session_set_cookie_params(0, '/PATH/; SameSite=None', <COOKIE_DOMAIN>, true, true);
I have a problem with the Mobile Detection Script.
There are two scenarios:
First the script should detect if it's a mobile or not. If mobile, than redirect to another page (this works fine).
The second query should determine, if the person is on the root page or not. If it's not the root page, the layout should be the classic one. (no redirection)
But when I add this line there won't be anymore redirection, even if I open the root page on a mobile.
I also tried to destroy the session on the google_mobile.php (redirected page) and set the $_SESSION['layoutType'] = 'mobile', but anyway the session is set to classic when I open the root page.
Thanks for your help!
Here is the script:
session_start();
require_once 'Mobile_Detect.php';
function layoutTypes() {
return array('classic', 'mobile');
}
function initLayoutType() {
// Safety check.
if (!class_exists('Mobile_Detect'))
return 'classic';
$detect = new Mobile_Detect;
$isMobile = $detect->isMobile();
$layoutTypes = layoutTypes();
// Set the layout type.
if (isset($_GET['layoutType'])) {
$layoutType = $_GET['layoutType'];
} else {
if (empty($_SESSION['layoutType'])) {
$layoutType = ($isMobile ? 'mobile' : 'classic');
} else {
$layoutType = $_SESSION['layoutType'];
}
//check if it's the root page
if ($_SERVER['REQUEST_URI'] != "/")
$layoutType = 'classic';
}
// Fallback. If everything fails choose classic layout.
if (!in_array($layoutType, $layoutTypes))
$layoutType = 'classic';
// Store the layout type for future use.
$_SESSION['layoutType'] = $layoutType;
return $layoutType;
}
$layoutType = initLayoutType();
if ($_SESSION['layoutType'] == 'mobile') {
header("Location: www.example.com/google_mobile.php");
exit;
}
I've tested your code, it seems to work as you described. I'd guess it is a session issue.
session_destroy() does not clear your previous session state in the immediate session. That means your $_SESSION would still be "dirty" in a script even if session_destroy() is the first line in it. It's safer to clear cookies from your browser instead.
One other possible problem would be query string. You're checking the REQUEST_URI and it includes any query string on URI. "/?foo=bar" is certainly not "/". You may want to check SCRIPT_NAME (i.e. $_SERVER['SCRIPT_NAME'] == 'index.php) instead.
I'm trying to create a basic REST style API with PHP and I'm having a strange issue. When I visit one of my pages (viewinput.php) through the URL /rest/viewinput.php the page loads fine. When I try through /rest/viewinput I get a "page not found" error.
So, here's the code that determines the type of request and where to send it. This is found on my server.php page
//in server.php
public function serve() {
$uri = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];
$paths = explode('/', $this->paths($uri));
array_shift($paths); //
$resource = array_shift($paths);
if ($resource == 'rest') {
$page = array_shift($paths);
if (empty($page)) {
$this->handle_base($method);
} else {
$this->handle_page($method, $page);
}
}
else {
// We only handle resources under 'clients'
header('HTTP/1.1 404 Not Found');
}
}
Since it is a GET method with a determined page name, it will be passed to this function
//in server.php
private function handle_page($method, $page) {
switch($method) {
case 'GET':
if($page == "viewinput"){ //I have both viewinput.php and viewinput just to check both. Only viewinput.php works
$this->display_info();
}
if($page == "viewinput.php"){
$this->display_info();
}
default:
header('HTTP/1.1 405 Method Not Allowed');
header('Allow: GET, PUT, DELETE');
break;
}
}
From here it is then sent to
//in server.php
function display_info(){
$view = new ViewInputs();
$view->view(); //this function is on viewinput.php
}
So, when I visit /rest/viewinput.php the view function displays properly. When I visit /rest/viewinput I get a "broken link" error.
I followed a tutorial online for a REST server Found Here and it works just fine.
The following is in my httpd.conf file
Options +FollowSymlinks
RewriteEngine on
RewriteRule ^/.* rest/server.php
This is my viewinput.php file. I believe that it's working correctly (when page loads, the serve function on server.php should run.
<?
include_once 'server.php';
class ViewInputs{
function view(){
$sql = mysql_query("select * from entry");
?>
<table>
<th>ID</th>
<th>Text</th>
<col width="200">
<col width="150">
<?
while ($result = mysql_fetch_array($sql)){
?>
<tr><td><? echo $result['id']." "; ?></td><td><? echo $result['text']; ?></td></tr>
<?
}
?> </table> <?
}
}
$server = new server();
$server->serve();
?>
From httpd.conf. I may be wrong, but I believe this is how to allow a .htaccess file
DocumentRoot "C:/xampp/htdocs/rest"
<Directory />
Options FollowSymLinks
AllowOverride ALL
Order deny, allow
Deny from none
</Directory>
<Files ".ht*">
Require all ALLOW
</Files>
Your rewrite rule is not written correctly. What is happening is that when you go to rest/viewinput.php that file actually exists so it is able to run. When you go to rest/viewinput that file doesn't exist. You can check your rewrite rule by going to http://martinmelin.se/rewrite-rule-tester/ you will see your rewrite rule is no good.
The point of doing this is so that you don't have a veiwinput.php file, everything should be sent directly to the server.php file. Most likely the rewrite rule you want is something like this:
RewriteRule rest/* rest/server.php
If you want to actually go to viewinput.php there is no point in having a rewrite rule at all, just get rid of it.
If you want rest/viewinput to be treated as rest/viewinput.php then use this:
RewriteRule ^rest/([a-z]+) rest/$1.php
I need to use main domain cookies for my sub domains as with higher priority when both sub and main domain cookies exists.
The problem is when I'm on sub.domain.com and there exist cookies for
sub.domain.com
.domain.com
The PHP global $_COOKIE contains $_COOKIE['data'] == 'sub.domain.com'.
I would like to check if there is also a .domain.com cookie and use it.
How do I read the main cookie when I'm on a sub domain with an existing sub domain cookie?
It looks like the gist of your issue is reading a cookie set in domain.com from sub.domain.com.
Add
session.cookie_domain = .domain.com
to your php.ini to make this happen. If you're on a shared hosting enviroment and can't modify your ini file, try having this somewhere in your code:
ini_set("session.cookie_domain", ".domain.com");
You should now be able to access cookies set by domain.com on subdomain.domain.com.
There is a $_SERVER ['HTTP_COOKIE'] variable that contains both sub domain and main domain cookie variables with the same name as one large string. In the following simple piece of code the $cookie_variable array will contain both values of specific variables:
if( 'sub.domain.com' == $_SERVER['HTTP_HOST']) {
$var_name = 'somedata';
$domains_counter = 0;
foreach(explode(';', $_SERVER['HTTP_COOKIE']) as $cookie_variable_string) {
if( false !== strpos($cookie_variable_string, $var_name.'=') ) {
$cookie_variable[$domains_counter] = urldecode(
trim(
substr(
$cookie_variable_string,
strpos($cookie_variable_string, $var_name) + strlen($var_name.'=')
)
)
);
$domains_counter++;
}
}
var_dump($cookie_variable);
}
Here's a function that gets all variables:
public static function get_http_cookie_variables() {
$domains_counter = [];
foreach(explode(';', $_SERVER['HTTP_COOKIE']) as $cookie_variable_string) {
$key_value = explode('=', $cookie_variable_string);
$cookie_var_name = trim($key_value[0]);
if(is_null($domains_counter[$cookie_var_name])) {
$domains_counter[$cookie_var_name] = 0;
}
$http_cookie_variables[$cookie_var_name][$domains_counter[$cookie_var_name]] = urldecode(trim($key_value[1]));
$domains_counter[$cookie_var_name]++;
}
return $http_cookie_variables;
}