Keeping users logged in without cookie - php

I was wondering if there was any other way to keep users logged in when they revisit my website even though the session has expired.
I currently use a cookie based method, but I was wondering if there were other methods people are using.

No, there are no [reliable] methods beside a cookie.

There are other methods, but you should refrain from using them because they in no way are as reliable as cookies.
You can use a IP based login system. If using this, you will see issues with multiple users from same IP.
You can generate a special link for users that is uniquely generated and make a login based on that
Instead of worrying about doing it another way, you should work on making your cookie-using system more secure.

This is a very old question, but for the sake of future visitors, I would like to supply an answer.
You SHOULD use cookies. Like the other answers have noted, they are the most reliable method out there. If you want to make sure the user isn't visiting with an expired cookie, write the time at which it expires as a cookie with a checksum.
Here's an example using PHP:
$expireTime = time() + (60*60*24*3); // 3 days ( 60 seconds * 60 minutes * 24 hours * 3 days )
$rawPepper = openssl_random_pseudo_bytes(16);
$hexPepper = bin2hex($rawPepper);
setCookie($cookieKey, $cookieValue, $expireTime);
setCookie("expiresWhen", $expireTime, $expireTime);
setCookie("rand", $hexPepper, $expireTime);
$hash_1 = hash('sha512', "Your_Super_Secret_Salt" . $cookieValue . "Another_Super_Secret_Salt!" . $expireTime);
$hash_2 = hash('sha512', "Yet_Another_Salt!" . $hash_1. $hexPepper);
setCookie("checksum", $hash_2, $expireTime);
Then in your other PHP form for validation you say:
$expires = $_COOKIE['expiresWhen'];
$pepper = $_COOKIE['rand'];
$cookieVal = $_COOKIE[$cookieKey];
$givenCheckSum = $_COOKIE['checksum'];
$hash_1 = hash('sha512', "Your_Super_Secret_Salt" . $cookieVal . "Another_Super_Secret_Salt!" . $expires);
$correctCheckSum = hash('sha512', "Yet_Another_Salt!" . $hash_1. $pepper)
if($givenCheckSum != $correctCheckSum){
/* user's cookie has expired. Handle as you please */
}else{
/* Cookie is still valid */
}
Anyone care to make corrections to this or supply suggestions?

Related

How does major website checks for user authentication?

As the title, I ask this question because the only way I know how to check if a user is logged in, is by having on top of each file a session and a query to data base to compared the sessions value.
I have something like this function, then I call this function on top of every php file, this works fine. However I dont think big website such as facebook, youtube and so on has this approach, also this means that all my files have to be .php I cant have .html as I wouldn't be able to run the function below.
public function isSessionValid()
{
session_start();
$dbConfig = new dbconfig("users");
$dbUser = $dbConfig->getDbUser();
$getUser = new GetUser($dbUser);
$result = $getUser->getUser($_SESSION['UN'],$_SESSION['PW'] );
if(!count($result) > 0){
header("Location: ../index.html");
}
}
My question is what are other efficient ways of checking for users credentials?
You don't want to have something like:
$_SESSION['PW']
Plain text unencrypted passwords are a serious concern. You could do something where upon login, you create a access token that is a random hashed string and that gets saved to the users cookies.
Here's an example:
function login()
{
$userName = 'billy';
$password = 'foobar';
// get your user data using username and password.
$userObject = new User();
$userId = $userObject->id;
// create our secure hash.
$accessToken = password_hash($password, PASSWORD_DEFAULT);
// update the user in the row.
mysqli_query($link, "UPDATE users set access_token='$accessToken' WHERE id = $userId");
// expires in 1 day, auto logout.
setcookie('userAccessToken', $accessToken, time() + 86400);
}
function getUser() {
return mysqli_query($link, "SELECT * from users WHERE access_token = ".$_COOKIE['userAccessToken']);
}
Now you only need to check for one attribute which is the access token, and its stored in the cookies in case they come back. Using the hash approach prevents anyone sniffing the cookies and getting private information.
For code maintainability you don't want duplicate code and should always try to create small reusable components that can be used across all your pages. Make a PHP file called auth.php and then just require it in the pages where you need it.
// UserPage.php
require_once __DIR__.'/lib/auth.php';
$user = getUser();
// MessagePage.php
require_once __DIR__.'/lib/auth.php';
$user = getUser();
// lib/auth.php
function getUser() {
return mysqli_query($link, "SELECT * from users WHERE access_token = ".$_COOKIE['userAccessToken']);
}
As a side note, rather than writing your application in plain php and building your own framework, I recommend you checkout laravel.com. They have video tutorials and the framework solves a lot of common problems developers run into, including this one.
One solution would be to configure the webserver to automatically route all requests through isSessionValid() first, then to their actual destination.

Implementing a 'Remember me' function

I was hoping somebody could help me out with how I would implement a 'Remember me' function in Parse.
When the ParseUser is first created, a ParseSession is also created with them, however this has an expiry date of 1 year. The problem is, the expiryDate is read-only, so I'm not able to change this. Even then, I am confused as to how these ParseSession's work, as they don't actually store any cookies.
Should I just delete the ParseSession's that are automatically created on registration, and make my own cookie and session to deal with this, or does Parse provide an easier way?
Edit with code I have tried so far:
So far I have tried to do this without Parse, using PHP's own cookie functions:
$user = ParseUser::logIn($email, $password);
$_SESSION['mySession'] = serialize($user);
$_SESSION['start'] = time(); // Taking now logged in time.
if ($rememberMe) {
// End session in 1 month.
$_SESSION['expire'] = $_SESSION['start'] + (31 * 24 * 60 * 60);
} else {
// End session in 1 hour.
$_SESSION['expire'] = $_SESSION['start'] + (5);
}
$parseSession = $user->getSessionToken();
// Note I got stuck here as I realised the expiryDate is readOnly in the docs.
Note that this code right now isn't working as expected since the session I am creating using
session_start();
ParseClient::setStorage(new ParseSessionStorage());
seems to be creating a session that lasts only the browser session - but I will look into this and I believe/hope it's something to do with PHP storing the sessions (I checked the .ini and it said on Windows I need to configure it manually which I have not done yet).

How to expire the activation link in PHP?

I have a php script that sends an activation link via email to the users so they can activate their account. The link is like this: mysite.com/activation.phpid?id=20
How can I create the link to expire after 24 hours?
I haven't tried anything as I couldn't find anything to teach me how to do it. all I know is that I might be able to do this by storing something in mysql but how?
it will be great if someone could tell me the step by step instruction please.
Thanks
Make the link like this:
$time = time();
$hash = md5($id . $time . "somerandomsalt"); // check this again in activation.php
$link = "activation.php?id=" . $id . "&hash=" . $hash . "&time=" . $time;
Then in activation.php you check if the hash matches. Oh, and check the time of course :P
You could obfuscate it a bit to hide the id, hash and time query parameters, but this is the basics.
Just add an extra field in your database with the expiration date of the link. When the link is clicked you can then check the date to make sure it isn't expired.
edit
I'm guessing at your column and table names.
SELECT IF (DATEDIFF(date_registered, CURRENT_TIMESTAMP) <= 0, 1, 0) AS expired
FROM users
WHERE id = 20
If expired is 1 then the link is expired. If it is 0 then it is valid.

Nonce and timestamp, best to secure POST values?

I have a script on my webserver that initiates a HTTPS connection with a secured payment solution. The payment solution allows the user to store its credit cards credentials, so the script's integrity is mandatory.
The script is called from web and from a web browser launch by an iPhone application.
It takes several POST values in entry:
user ID
value
currency
... etc
to generate the initial request with the payment solution.
My goal is to secure, as much as possible, the POST values sent to the script to avoid attacks, essentialy because anyone could see the POST variables it takes in entry with a simple Firebug.
The script is reached via HTTPS protocol, and this is what I came up with in order to secure the content data :
if (!empty($_POST)) {
$uid = $_POST['uid'];
$nonce = $_POST['nonce'];
$request_timestamp = $_POST['time'];
//other useful values ...
}
/*
* Test 1 : is the nonce correct ? (Example of possible hash)
*/
if (strcmp(md5($uid . $request_timestamp . 'mygrE4TpassPhraZ'), $nonce) !== 0) {
echo 'Bad nonce';
exit;
}
/*
* Test 2 : is the timestamp value acceptable ? 10 seconds maximum here.
*/
if (time() - $request_timestamp > 10) {
echo 'Request too old';
exit;
}
/*
* Test 3 : is this request is the first valid one that I receive with those credentials for this user ?
*/
if (strcmp(User::getOnGoingNonce($uid), $nonce) === 0) {
echo 'Request already registered';
exit;
}
//direct database access
User::setOnGoingNonce($uid,$nonce);
/*
* Finally, chat granted with the payment solution ...
*/
Do you think this is secure enough ? Do you have cleaner solutions ?
Inputs would be greatly appreciated, thank you in advance.
It is good developer practice to sanitize and filter every user input. There is a special PHP function for that purpose, available to make a programmer's life easier. It is called filter_var().
If you work with arrays you can use the filter_var_array().
See here for details.
So, the practical solution to your code should be something like this:
$uid = filter_var($_POST['uid'], FILTER_SANITIZE_NUMBER_INT);
$nonce = filter_var($_POST['nonce'], FILTER_SANITIZE_STRING);
$request_timestamp = filter_var($_POST['time'], FILTER_SANITIZE_STRING);
I assume, that 'uid' is a integer, other variables are strings. But you can choose whatever filter you need. See here for type of filters.
When sanitizing the user input you ensure, that your script will not allow SQL injection attacks, for example, or XSS attacks.

How to create and use nonces

I am running a website, and there is a scoring system that gives you points for the number of times you play a game.
It uses hashing to prove the integrity of http request for scoring so users cannot change anything, however as I feared might happen, someone figured out that they didn't need to change it, they just needed to get a high score, and duplicate the http request, headers and all.
Previously I'd been prohibited from protecting against this attack because it was considered unlikely. However, now that it has happened, I can. The http request originates from a flash game, and then is validated by php and php enters it into the database.
I'm pretty sure nonces will solve the issue, but I'm not exactly sure how to implement them. What is a common, and secure way of setting up a nonce system?
It's actually quite easy to do... There are some libraries out there to do it for you:
PHP Nonce Library
OpenID Nonce Library
Or if you want to write your own, it's pretty simple. Using the WikiPedia page as a jumping off point, In pseudo-code:
On the server side, you need two client callable functions
getNonce() {
$id = Identify Request //(either by username, session, or something)
$nonce = hash('sha512', makeRandomString());
storeNonce($id, $nonce);
return $nonce to client;
}
verifyNonce($data, $cnonce, $hash) {
$id = Identify Request
$nonce = getNonce($id); // Fetch the nonce from the last request
removeNonce($id, $nonce); //Remove the nonce from being used again!
$testHash = hash('sha512',$nonce . $cnonce . $data);
return $testHash == $hash;
}
And on the client side:
sendData($data) {
$nonce = getNonceFromServer();
$cnonce = hash('sha512', makeRandomString());
$hash = hash('sha512', $nonce . $cnonce . $data);
$args = array('data' => $data, 'cnonce' => $cnonce, 'hash' => $hash);
sendDataToClient($args);
}
The function makeRandomString really just needs to return a random number or string. The better the randomness, the better the security... Also note that since it's fed right into a hash function, the implementation details don't matter from request to request. The client's version and the server's version don't need to match. In fact, the only bit that needs to match 100% is the hash function used in hash('sha512', $nonce . $cnonce . $data);... Here's an example of a reasonably secure makeRandomString function...
function makeRandomString($bits = 256) {
$bytes = ceil($bits / 8);
$return = '';
for ($i = 0; $i < $bytes; $i++) {
$return .= chr(mt_rand(0, 255));
}
return $return;
}
Nonces are a can of worms.
No, really, one of the motivations for several CAESAR entries was to design an authenticated encryption scheme, preferably based on a stream cipher, that is resistant to nonce reuse. (Reusing a nonce with AES-CTR, for example, destroys the confidentiality of your message to the degree a first year programming student could decrypt it.)
There are three main schools of thought with nonces:
In symmetric-key cryptography: Use an increasing counter, while taking care to never reuse it. (This also means using a separate counter for the sender and receiver.) This requires stateful programming (i.e. storing the nonce somewhere so each request doesn't start at 1).
Stateful random nonces. Generating a random nonce and then remembering it to validate later. This is the strategy used to defeat CSRF attacks, which sounds closer to what is being asked for here.
Large stateless random nonces. Given a secure random number generator, you can almost guarantee to never repeat a nonce twice in your lifetime. This is the strategy used by NaCl for encryption.
So with that in mind, the main questions to ask are:
Which of the above schools of thought are most relevant to the problem you are trying to solve?
How are you generating the nonce?
How are you validating the nonce?
Generating a Nonce
The answer to question 2 for any random nonce is to use a CSPRNG. For PHP projects, this means one of:
random_bytes() for PHP 7+ projects
paragonie/random_compat, a PHP 5 polyfill for random_bytes()
ircmaxell/RandomLib, which is a swiss army knife of randomness utilities that most projects that deal with randomness (e.g. fir password resets) should consider using instead of rolling their own
These two are morally equivalent:
$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);
and
$_SESSION['nonce'] []= random_bytes(32);
Validating a Nonce
Stateful
Stateful nonces are easy and recommended:
$found = array_search($nonce, $_SESSION['nonces']);
if (!$found) {
throw new Exception("Nonce not found! Handle this or the app crashes");
}
// Yay, now delete it.
unset($_SESSION['nonce'][$found]);
Feel free to substitute the array_search() with a database or memcached lookup, etc.
Stateless (here be dragons)
This is a hard problem to solve: You need some way to prevent replay attacks, but your server has total amnesia after each HTTP request.
The only sane solution would be to authenticate an expiration date/time to minimize the usefulness of replay attacks. For example:
// Generating a message bearing a nonce
$nonce = random_bytes(32);
$expires = new DateTime('now')
->add(new DateInterval('PT01H'));
$message = json_encode([
'nonce' => base64_encode($nonce),
'expires' => $expires->format('Y-m-d\TH:i:s')
]);
$publishThis = base64_encode(
hash_hmac('sha256', $message, $authenticationKey, true) . $message
);
// Validating a message and retrieving the nonce
$decoded = base64_decode($input);
if ($decoded === false) {
throw new Exception("Encoding error");
}
$mac = mb_substr($decoded, 0, 32, '8bit'); // stored
$message = mb_substr($decoded, 32, null, '8bit');
$calc = hash_hmac('sha256', $message, $authenticationKey, true); // calcuated
if (!hash_equals($calc, $mac)) {
throw new Exception("Invalid MAC");
}
$message = json_decode($message);
$currTime = new DateTime('NOW');
$expireTime = new DateTime($message->expires);
if ($currTime > $expireTime) {
throw new Exception("Expired token");
}
$nonce = $message->nonce; // Valid (for one hour)
A careful observer will note that this is basically a non-standards-compliant variant of JSON Web Tokens.
One option (which I mentioned in comment) is recording gameplay and replay it in secure environment.
The other thing is to randomly, or at some specified times, record some seemingly innocent data, which later can be used to validate it on server (like suddenly live goes from 1% to 100%, or score from 1 to 1000 which indicate cheat). With enough data it might just not be feasible for cheater to try to fake it. And then of course implement heavy banning :).
This very simple nonce changes every 1000 seconds (16 minutes)
and can be used for avoiding XSS where you are posting data to and from the same application. (For example if you are in a single page application where you are posting data via javascript. Note that you must have access to the same seed and nonce generator from the post and the receiving side)
function makeNonce($seed,$i=0){
$timestamp = time();
$q=-3;
//The epoch time stamp is truncated by $q chars,
//making the algorthim to change evry 1000 seconds
//using q=-4; will give 10000 seconds= 2 hours 46 minutes usable time
$TimeReduced=substr($timestamp,0,$q)-$i;
//the $seed is a constant string added to the string before hashing.
$string=$seed.$TimeReduced;
$hash=hash('sha1', $string, false);
return $hash;
}
But by checking for the previous nonce, the user will only be bothered if he waited more than 16.6 minutes in worst case and 33 minutes in best case. Setting $q=-4 will give the user at least 2.7 hours
function checkNonce($nonce,$seed){
//Note that the previous nonce is also checked giving between
// useful interval $t: 1*$qInterval < $t < 2* $qInterval where qInterval is the time deterimined by $q:
//$q=-2: 100 seconds, $q=-3 1000 seconds, $q=-4 10000 seconds, etc.
if($nonce==$this->makeNonce($seed,0)||$nonce==$this->makeNonce($seed,1)) {
//handle data here
return true;
} else {
//reject nonce code
return false;
}
}
The $seed, could be the any function call or user name, etc. used in the process.
It is not possible to prevent cheating. You can only make it more difficult.
If someone came here looking for a PHP Nonce Library: I recommend not using the first one given by ircmaxwell.
The first comment on the website describes a design flaw:
The nonce is good for one certain time window, i.e. the nearer the
user gets to the end of that windows the less time he or she has to
submit the form, possibly less than one second
If you are looking for a way to generate Nonces with a well-defined lifetime, have a look at NonceUtil-PHP.
Disclaimer: I am the author of NonceUtil-PHP

Categories