I was using $_SESSION to store users IDs but I want to change to using a regular cookie (with a KEY) paired with SQL so I can keep users authenticated when they close their browsers.
How do I create an unique key for each row in my SESSIONs table?
You're effectively looking to write your own session handler to bypass PHP's cookie expiration issue on session cookies. This is pretty straightforward, and pretty optimized if done properly.
Step 1: generating the session ID
A session ID is unique. However, if you're planning for permanent sessions, you have to bear a couple of things in mind: you want the session to carry over per browser, regardless of connection issues. So, you can cheat the system a bit by mapping the session ID with the user agent of the navigator (which doesn't change).
This allows you to reduce the chance for your session ID generator to generate the same ID for two distinct visitors. The rest is up to a random number generator and a hash algorithm, though - md5(microtime().$_SERVER['REMOTE_ADDR']) is usually a trusted friend.
Step 2: storing and retrieving the data
Storing the data is also trivial. Your aim is to create a MySQL table with a minimum of two columns: the session key (set to PRIMARY), and the data (serialized array for the simplest form, stored as TEXT).
When you create a session, just insert a new row and watch for errors. If an error comes up, the key was already used, so you'll need to re-generate another. If the row was inserted successfully - all good, you now have a session row for the user! All you need to do from there on is to read/write to this row as you see fit.
Cookie the user with the session ID, and you're done!
Caveats
Never ever use a userID as session ID. Cookies can be very easily manipulated. You want the cookie value to be random and completely separated from the user. It should be meaningless.
You'll need to write clean-up code to clean up the session table from time to time. A good way to do this is to keep track of when a session was last used - and delete accordingly.
The best way is to use some hash function, which is actually also used by PHP itself by generating session id's.
You can use something like this:
md5(microtime());
You can combine more values together, e.g. random number, user agent, server name or use stronger algorithms like SHA2.
But for uniqueness you will always need to look into table if there isn't such hash already stored (but the probability is very low of course).
But I would just go for standard session_id() which does all the job.
You can use MySQL's UUID().
If you want to keep the session alive you can also use PHP's session_set_cookie_params().
The SQL solution has one advantage though: you can scale to more webservers easily. On the other hand you can not use PHP's $_SESSION array and you have to write the session handling yourself.
Related
I would like to know if i can prevent session id cookie malformation. Let's say I have a cookie with key SESS and value of md73i54kj98ti0uf8dftps2fa3 which is a valid session id and corresponding file sess_md73i54kj98ti0uf8dftps2fa3 exists in my session storage folder. If I modify value for key SESS cookie to be for example foo it will create new file in sessions folder with name sess_foo. How can I check that provided cookie sess id value is invalid so that I can call session_regenerate_id for example to set valid id and create appropriate file.
Also, I am wondering if someone hypothetically renames cookie sess id to real session of another user will he get control? are there ways around this?
Thank you.
Update 1: First problem can be solved with session.use-strict-mode ini directive. http://php.net/manual/en/session.configuration.php#ini.session.use-strict-mode
Though it requires some extra steps when using with custom session handler. (my case).
But still what if changed session id matches real session id of other user? Should I use some kind of fingerprinting (user-agent + ip) or encrypt session data with combined key of (user-agent + ip)?
For new session files being created, you already have the solution which is session.use-strict-mode.
But what about guessing another session id? You are totally right, if you can guess another valid id, you will be using that session, effectively impersonating its owner. After login, the session id is the only secret used by the user, equivalent to the userid+password for the session.
Why is this not a problem then? Because you can't reasonably guess another valid session id. Session ids are (should be) cryptographically random, and so long that you can't just guess one (more precisely, they have a lot of entropy). Standard solutions, like the one in PHP and most other programming languages or frameworks provide a reasonable level of security, but you should not implement your own session management (id generation, verification, etc.), unless you really know what you are doing and are aware of the security aspects.
In your example, the session id seems to consist of 26 lowercase letters and numbers. There are 26 different letters and 10 numbers, so the number of possible session ids are (26+10)^26 = 2.9 * 10^40. Say you can try one billion (10^9) ids every second, and your server has one million sessions at once (neither of this is realistic in any way). It would still take around 10^25 seconds (~ 3*10^17 years) to correctly guess a valid session id. Note that this is way more then the age of the universe. :)
Of course, for the reason above (session id = username+password for the session), you must protect the session id as much as you can, for example by only storing it in an httpOnly cookie, and never sending it in the URL, or clear text (but always using https, with the secure flag for the cookie, and HSTS headers sent, etc).
i'm looking for the optimal way for safe user authentification to my website.
i'm thinking about the following solution:
first login: asking for login data -> generating hashcode consisting of ip address, password and cookie expiration date -> storing that hashcode to database but also into a cookie.
next login: check for cookie, look for hashcode in my user database.
would that be the safest way? or will there be problems using cookies?
thanks
The best and easiest way is to use php-sessions.
<?php
session_start();
$_SESSION["user"] = "foo";
$_SESSION["ip"] = "123.132.123.132";
?>
Generally, you will want to store a cookie in the browser that points to a record in your database somewhere that says that he is a known user. The one thing you'll want to ensure is that that cookie id is globally unique. So, yes, using a hash (or md5, or sha1) on a variety of unique attributes would most likely be sufficient. Putting a unique on that column in your database would be a good idea as well.
Another idea would be to have a column in your users table for cookieid, generate that when the entry is created and ensure it's uniqueness. Then just use that every time when that user logs in. You could change all the values of that column for every user with a backend script every once in a while if you want to freshen up the cookies if you're really worried about security.
will there be problems using cookies?
Except for newer html5 features that aren't compatible with all browsers, cookies are really the only way to save login information. Go to a big site you use, ebay, amazon, wellsfargo. Wipe all of your cookies and private data, then go there and login. Then view your cookies. What do they put in there? If it's good enough for those guys, it's probably good enough for you.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
PHP Session Fixation / Hijacking
I've been using $_SESSION superglobal a lot and heavily.
However the situation is like this:
Once the user is logged I want to keep track of his ID(MySQL table). I can easily
insert the id into $_SESSION['id'] = $user_id;
After all I can use that variable across the pages on my site. What's on my mind is - user can trick the ID into another. If I would see that there's a simple number then I can change it a bit and see what happens - I want to prevent this as it can cause a lot of problems as user ID would be used for adding, deleting, editing entries inside the database.
Is session_regenerate_id() just enough to keep my session safe from hijack ?
Conclusion:
Cookie only stores session identificator - all the values are on the server and never get passed to the client side.
Read about session fixation/hijacking on StackOverflow
The user has no acccess to $_SESSION['id']. He can not modify a variable that's kept on your server (see session doc).
session_regenerate_id() has a different purpose. It resets the cookie SID. That's the handle that differentiates users and sessions. It only makes sense to use if you have a secondary identifier (IP or user agent string) to verify. It's main purpose is preventing stale or intersecting sessions. Again, see the manual.
If I were you I'd have a table in your database that stored a user_id and a session_hash. Possibly a date_expires as well. Then when a user logs in you create a hash based on their id and maybe a random salt, store that in the database as well as the session variable. That way if they change that value on their side, the chances of them matching some other stored value in your database is very unlikely. Along with this if the user performs any operations on their account you just check the database table for their hash to get their real id and then follow through with the operation like you normally would.
One option would be to hash it and then use that same hash in your database.
Example:
$_SESSION['id'] = md5($user_id);
$query = "SELECT * from database_table where md5(database_table.user_id) = " . $_SESSION['id'];
I'm writing a user system where users will log in using Twitter's API, then I'll store the information in a database along with a few extra pieces that I have the user put in. I want the user to be able to come back after logging in and not have to log in again. I decided that I'd get all the relevant information about the user, save it to the database, then save the session ID and user ID to another table. Finally, I'd set a cookie on the user's computer containing the same session ID so that throughout their browsing they would stay logged in. Then if they closed the browser and revisited the site later, I would read that cookie, get the compare it with the sessions table, get the User ID, and reconstruct the session (updating the sessions table with the new session ID).
My question is, how random is the session ID? Is there a possibility that a user might get the same session ID that a user that hasn't visited the site in a week (so the cookie would still be active) had assigned to them? If this happens, then the server might mistake the new user for the old one. I really would like to avoid using the IP address because people might visit the site from a mobile browser where the IP can change at any time.
Any ideas on this? I just want to ensure that user A and user B, separated by any amount of time, won't get the same session ID.
Append current time in microsecond to the unique id...
session_id() + microtime();
So not only would the session_ids have to be the same, it would have to happen on the same microsecond... making the vanishingly unlikely just about impossible. The only way to guarantee it 100% is to check this random value against all existing session ids and re-roll it if it already exists.
Although the probability of having two active sessions with identical identifiers at the same time is vanishingly low (depending on the hash function), you could add an additional (pseudo-) unique value to that session ID to get a value with both characteristics.
You could use uniqid that fulfills the latter:
uniqid(session_id(), true)
uniqid’s value is based on microtime with an additional pseudo-random number from lcg_value and an additional source for more entropy that all together guarantees unique values.
The PHP Session ID is an MD5 hash, which makes it 128 bits in length. That's something like 340,000,000,000,000,000,000,000,000,000,000,000,000 different possibilities. The odds of two people getting the same one are pretty remote.
If you want to guarantee uniqueness, put something in their cookie based on sequential numbers.
I've been interested in how sessions work internally, but I have little knowledge of C (and am unsure where to look in the PHP source for this).
This is what I understand of sessions at the moment:
When you start a session the user gets assigned a session id which is stored in a cookie.
When session data is saved (via $_SESSION) it is stored on the filesystem, with the relevant session id and an expiry time.
Is this correct? Also what is the method in which session id are created? I assume it's based on time but what if two users send a request at the same time? What methods are in place internally to prevent them getting the same id?
Thanks,
My understanding is of the internal session handling process is the following:
When session_start is called, PHP is looking for a parameter from the client that was sent via POST, GET, or in a cookie (depending on the configuration; see session.use_cookies, session.use_only_cookies, and session.use_trans_sid) with the name of the value of session.name to use the session ID of an already started session.
If it finds a valid session ID, it tries to retrieve the session data from the storage (see session.save_handler) to load the data into $_SESSION. If it can’t find an ID or its usage is forbidden, PHP generates a new ID using a hash function (see session.hash_function) on data of a source that generates random data (see session.entropy_file).
At the end of the runtime or when session_write_close is called, the session data in $_SESSION is stored away into the designated storage.
Look at php_session_create_id in ext/session/session.c in the php source
It goes like this:
get time of day
get remote ip address
build a string with the seconds and microseconds from the current time, along with the IP address
feed that into configured session hash function (either MD5 or SHA1)
if configured, feed some additional randomness from an entropy file
generate final hash value
So getting a duplicate is pretty difficult. However, you should familiarise yourself with the concept of session fixation, which allows an attacker to potentially choose the session_id their target will adopt - see Sessions and Cookies for a good primer.
The session ID is probably just a random string of letters and numbers. Also it would be strange if PHP didn't check to see that it is unique and therefore cannot be the same for two users. As for (1) and (2), I'd say you're correct, but I haven't worked with PHP recently, so feel free not to believe me.