Hi, I'm an absolute noob to php, I know a little, enough to get me by so far by learning from snippets and suchlike.

Before I dive into a new area (sessions) I'd like to know if what I am intending is possible or silly.

What I have

I dont have a forum, or a website as such to log into, but my application is of an online nature and I would like to log the times and time periods my application users are active.

At the moment my users start the app by entering their password via a client gui which is hashed and sent to a php script, which in turn checks the password hash against its counterpart in a mysql database table.

My Intentions

I want to prevent my users from sharing their passwords with friends.

The way I want to do this is by logging the IP of the user, and denying any further login attempts from a different IP while the first is active.

I cannot hardcode the IPs because as you know they can change or some even dynamic.

My questions

It this possible?
With my limited knowledge of php, am I out of my depth?
Any tips for getting started?


Any and all advice greatly appreciated.

Suze.

You can get the user's IP ($_SERVER)

If you store this in the user's DB record, you can check it on a subsequent login request for the same user.

It isn't very difficult to prevent additional logins but you need to handle the situation where the user just shuts down without logging out.

Do you have code that will detect this and log the user out automatically? If you don't, then the next time the user tries to login, you might reject it unless you have some sort of check to recognize the situation. OK, your next question is how to do that. I don't have a specific suggestion or code. You can handle it up-front when a session has been inactive for a certain period of time or you can handle it on the next login attempt. You may need to set a cookie on the user's machine in order to manage this properly. This additional error-handling will make it a bit more complicated so with limited knowledge, you might find this challenging. I don't have any code to offer but you can probably find some on Daniweb or the web. You might want to try a search on 'auto logout' for starters.

commented: helpful advice +1

This seems like it has two parts to me. To make dealing with the session data easier, implementing a custom session handler which stores the sessions in the database would be the first step.

http://www.php.net/manual/en/function.session-set-save-handler.php
http://www.nateklaiber.com/blog/2006/05/10/custom-php-session-handler
http://devzone.zend.com/article/141

Once you have that in place, you want to associate a logged in user id, the session id, and an ip address.

So say you have the following sql structure:

CREATE TABLE IF NOT EXISTS `sessions` (
  `id` varchar(32) NOT NULL,
  `access` int(11) NOT NULL COMMENT 'timestamp',
  `data` text NOT NULL COMMENT 'serialized array',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `users` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `Username` varchar(320) NOT NULL,
  `Password` varchar(64) NOT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `usersessions` (
  `UserId` int(11) NOT NULL,
  `SessionId` varchar(32) NOT NULL,
  `IpAddress` int(11) NOT NULL,
  PRIMARY KEY (`UserId`,`SessionId`,`IpAddress`),
  KEY `SessionId` (`SessionId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `usersessions`
  ADD CONSTRAINT `usersessions_ibfk_1` FOREIGN KEY (`UserId`) REFERENCES `users` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION,
  ADD CONSTRAINT `usersessions_ibfk_2` FOREIGN KEY (`SessionId`) REFERENCES `sessions` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;

The session handler would take care of managing the data in the sessions table.

When you log a user in, the process would just be a series of quick checks. First, does the user have any records in the usersessions table, if no, then just log the user in like normal and add a record that relates their user id, session id, and ip address (stored with INET_ATON).

On logout, remove any records for the user from the usersessions table.

When the user attempts to login and you find a record in the usersessions table with a matching ip, (meaning the user did not logout but maybe left the browser and came back before the session a.) expired b.) was cleaned up but the garbage collector) query the session table and check if the session the user has is expired. If it is, start a new one and update the table with the new session id. If it is still valid, set the user's session to point to the existing one. Session handler will handle keeping it alive etc from there.

When User A is logged in and if they share their password with User B, when User B logs in, the system would look for their user id in the usersessions table and find a matching row, it would then see the ip was a mismatch and it would then have to validate whether the session that was associated was valid or if the session was expired.

If the session is still valid a message could be present to the user saying something like "This account is already accessing the system. Please try back in [time till expiration]" if the session is invalid, assume the user has a dynamic ip address and create a new session for User B and log them in. User A is now prevented from logging in till User B's session is expired.

Phew that was a LOT of writing, but I hope it illustrates what you would need to do. It sounds a lot more complicated than it really is, I just wanted to present to you the obvious use cases as I see it currently.

Thank you both very much, I really appreciate your indepth answers.

Firstly I know I can do it now, and it may not be as difficult as I thought (even though the sessions post is making my head spin right now :) )

I could certainly add code to my app to have it logout when the user closes the app without doing so manually, (thanks for the suggestion) but as much as it excited me, it sounded to easy to be true, and alas it was, as I could not handle a user just killing the process :( .

While I still dont understand sessions just yet, I am drawn to the sentence "was cleaned up but the garbage collector".

Is this an mysql thing? and can I have it work on any value, or just sessions?

Thank you once again for your time and advice.

Session garbage collecting is specific to how php handles sessions. It is independent of the storage mechanism.

There are three in particular that control the garbage collection you can see them all here: http://php.net/manual/en/session.configuration.php:
session.gc_probability
session.gc_divisor
session.gc_maxlifetime

There are a few caveats that affect how sessions work.

  1. Garbage collection is started with (gc_probability / gc_divisor) (default 1%) so it only has a 1% chance of running.
  2. Garbage collection runs on requests so if your server has little to no traffic don't expect your sessions to get cleaned up when they should expire.
  3. The gc_maxlifetime affects session life, but, again if the server is not running the garbage collector because of low traffic it will not be expiring sessions.

Essentially if you're storing your sessions in mysql, you can just empty the session table and forcefully log out all users. You could also delete all the files wherever your sessions are being stored and get the same result.

commented: Helpful advice +1

Thank you kindly, I definately have enough info to be moving forward.

Seems sessions will not suit my purpose.

I understand better now, what they are after reading up, and it appears sessions are window and browser dependant.

Since my app uses neither a browser or a window, it doesnt look like a can use them.

Anyone have any suggestions as to how I can tackle my problem another way, using database perhaps?

If your app doesn't use a browser or a window, how is it connected? Some mobile device? Or a background service or daemon? Can it be programmed to periodically update its status against your database? If this is the case, you could work with a mysql trigger which records the last time of activity and assumes a user as logged out after a certain period of inactivity.

It uses a windows API call to an inetget function.

My app does periodically retrieve info from a php page on my site, but I'm having a tough time trying to figure out the logic I would need.

Heres what I pseudo see in my head.

User logs in to app, and their IP is stored in a field of a table, which is exclusive to that user and their password hash (I am okay with this)

If same user and password attempts to login or otherwise use the app from a different IP than what is stored in said field, they are denied (think I'm ok with this)

If a user does not send their periodic data within a certain amount of time, then the IP field is cleared, enabling that user to login from a different location/IP if needed (this is where I'm failing to see in my mind, what to do)

If the user ends the application normally, I can have it clear the field and there would be no problem, if the the app is unexpectedly terminated (which could be for any number of reasons) then the field would not be cleared, and if the users IP changes before they log in again they would be denied, causing them uneeded grief.

So this brings me back to my main fail of having the field cleared if the user does not send data withing a timeframe.

Im sorry if I did not explain thourily, I'm not familiar with all the lingo yet.

Any and all suggestions are warmly welcomed.

Suze.

edit, I'm unfamiliar with mysql trigger. looking it up now.

Its not too complicated.
You need two fields in your user table: accesstime and ip_address.
When a user tries to login, do as follows:
IF ip_address in database == currently used ip address
OR ip_address in database is empty
OR accesstime is back more than xxx minutes
THEN
accept login
update accesstime
update ip_address in database
ELSE
reject login

When a users logs out, clear the IP field.
With each access of a user to your application, update the access time.

If you want to exclude that your users suffer from an IP address switch during a session, you have to create your own session tokens. Use the rand() function of mysql. Then you need an additional user table field "session_token", and the procedure changes to:

IF ip_address in database == currently used ip address
OR ip_address in database is empty
OR accesstime is back more than xxx minutes
OR submitted session_token is the same as in database
THEN
accept login
create, transmit and store session token
update accesstime
update ip_address in database
ELSE
reject login

commented: Helpful information. +1

Makes sense, at least after a couple of reads.

Thanks, will try that logic.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.