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

Random User Logout and Cookie Anomalies #289

Open
zerofruchtshake opened this issue Jul 17, 2023 · 4 comments
Open

Random User Logout and Cookie Anomalies #289

zerofruchtshake opened this issue Jul 17, 2023 · 4 comments
Labels

Comments

@zerofruchtshake
Copy link

Hello,

I'm currently experiencing a random logout issue while using the PHP-Auth library in my application, predominantly on Windows machines. macOS users do not seem to experience this problem. The symptoms also include anomalies with the user session cookies:

Anomalies:
Users are randomly logged out, without any discernible pattern or trigger.
A specific session cookie seems to behave oddly:
Name: remember_6TpGxxxxxxx
Value: %7E (Notably shorter than typical values I observe when logged in.)
Expires: 2024-07-16T20:43:45.520Z (This is definitely incorrect as the expiry date gets updated to a correct value when the user logs in again.)
These issues occur when running a Tampermonkey script with Chrome on an external domain (example1.com) to interact with the login-protected domain (example2.com).

Despite preliminary debugging efforts using browser developer tools and server-side logging, the root cause of this issue remains elusive...

If anyone could provide some guidance or hints on where to start further debugging, I would greatly appreciate it. Thank you in advance for your assistance.

@ocram ocram added the question label Jul 21, 2023
@ocram
Copy link
Contributor

ocram commented Jul 21, 2023

Thank you for bringing this up and for sharing all the details!

  1. The value ~ (%7E) in a remember_* cookie is definitely not an expected value, as that cookie is supposed to contain a long random string.
  2. Why do you think 2024-07-16 is not a valid expiry date, and what kind of date do you usually see there after logging in?
  3. Does all the weird behavior just occur when running the Tampermonkey script with Chrome, and you don’t notice it otherwise?

@zerofruchtshake
Copy link
Author

  1. because remember-me duration should be either 12 or 48h
    if ($_POST['remember'] == 1) { $rememberDuration = (int) (172800); } else { $rememberDuration = (int) (43200); }
    when logging in, it is set correctly: 2023-07-24T10:07:46.565Z

  2. i'll test some more. it definitely does not happen on MacOS (including using the same Tampermonkey script in Chrome)

@zerofruchtshake
Copy link
Author

zerofruchtshake commented Jul 22, 2023

I tried adding some log statements to find the cause:

Auth.php

private function setRememberCookie($selector, $token, $expires) {
		writeToLog("setRememberCookie() called. Selector: " . $selector . ", Token: " . $token . ", Expires: " . $expires);
		$e = new \Exception();
		writeToLog("Stack trace: " . $e->getTraceAsString());
		$params = \session_get_cookie_params();
		if (isset($selector) && isset($token)) {
			$content = $selector . self::COOKIE_CONTENT_SEPARATOR . $token;
		} else {
			$content ='';
		}

		// save the cookie with the selector and token (requests a cookie to be written on the client)
		writeToLog("Creating new Cookie object with name: " . $this->rememberCookieName);
		$cookie = new Cookie($this->rememberCookieName);
		$cookie->setValue($content);
		$cookie->setExpiryTime($expires);
		$cookie->setPath($params['path']);
		$cookie->setDomain($params['domain']);
		$cookie->setHttpOnly($params['httponly']);
		$cookie->setSecureOnly($params['secure']);
		$result = $cookie->save();
		writeToLog("Cookie saved. Value: " . $cookie->getValue() . ", Expiry time: " . $cookie->getExpiryTime() . ", Path: " . $cookie->getPath() . ", Domain: " . $cookie->getDomain());

		if ($result === false) {
			throw new HeadersAlreadySentError();
		}

		// if we've been deleting the cookie above
		if (!isset($selector) || !isset($token)) {
			// attempt to delete a potential old cookie from versions v1.x.x to v6.x.x as well (requests a cookie to be written on the client)
			writeToLog("Deleting old cookie with name: 'auth_remember'");
			$cookie = new Cookie('auth_remember');
			$cookie->setPath((!empty($params['path'])) ? $params['path'] : '/');
			$cookie->setDomain($params['domain']);
			$cookie->setHttpOnly($params['httponly']);
			$cookie->setSecureOnly($params['secure']);
			$cookie->delete();
			writeToLog("Old cookie 'auth_remember' deleted");
		}
	}

LOGS:

2023-07-22 13:14:43 setRememberCookie() called. Selector: , Token: , Expires: 1721582083
2023-07-22 13:14:43 Stack trace: #0 /var/www/html/vendor/delight-im/auth/src/Auth.php(157): Delight\Auth\Auth->setRememberCookie()
#1 /var/www/html/vendor/delight-im/auth/src/Auth.php(69): Delight\Auth\Auth->processRememberDirective()
#2 /var/www/html/checkkdnr.php(18): Delight\Auth\Auth->__construct()
#3 {main}
2023-07-22 13:14:43 Creating new Cookie object with name: remember_6TpGq1xR_F05q3tke-JkBw
2023-07-22 13:14:43 Cookie saved. Value: ~, Expiry time: 1721582083, Path: /, Domain: 
2023-07-22 13:14:43 setRememberCookie() called. Selector: , Token: , Expires: 1721582083
2023-07-22 13:14:43 Stack trace: #0 /var/www/html/vendor/delight-im/auth/src/Auth.php(157): Delight\Auth\Auth->setRememberCookie()
#1 /var/www/html/vendor/delight-im/auth/src/Auth.php(69): Delight\Auth\Auth->processRememberDirective()
#2 /var/www/html/login.php(16): Delight\Auth\Auth->__construct()
#3 {main}
2023-07-22 13:14:43 Creating new Cookie object with name: remember_6TpGq1xR_F05q3tke-JkBw
2023-07-22 13:14:43 Cookie saved. Value: ~, Expiry time: 1721582083, Path: /, Domain: 

checkkdnr.php

<?php

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: https://abc.example.net'); //the website where the tampermonkey script should add data from my application
header("Access-Control-Allow-Headers: Content-Type");
header("Access-Control-Allow-Credentials: true");

use Delight\Auth\Auth;

require_once 'vendor/autoload.php';
require_once '/var/www/include/php_db.inc.php';

$error = '';
$db = php_db("users");


// Create a new instance of the Auth class
$auth = new \Delight\Auth\Auth($db);
if (!$auth->isLoggedIn()) {
    // User is not logged in, redirect to the login page
    header('Location: login.php');
    exit;
}

@ocram
Copy link
Contributor

ocram commented Jul 27, 2023

Thanks a lot!

The behavior you showed happens only when an existing but invalid “remember me” cookie has been found, i.e. sent by the client and checked against the database on the server. So in your case there must have been a “remember me” cookie but it had invalid data. So then it is replaced with empty values (two empty strings separated by a ~) and written to the cookie with a duration of one year, to avoid unnecessary database requests on the subsequent page requests.

Do you have any idea where that invalid cookie initially comes from?

The file Auth.php with its processRememberDirective method is where you should add logging next, otherwise.

A possible solution would perhaps be replacing

$this->setRememberCookie('', '', \time() + 60 * 60 * 24 * 365.25);

with

$this->setRememberCookie(null, null, \time() - 3600);

and seeing if that changes anything. But that would only remove the invalid cookie instead of replacing it with a %7E cookie. The question is, why is the %7E cookie a problem in the first place? Why do logouts happen?

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

No branches or pull requests

2 participants