I'm writing up an online examination site for an educational institution. Time limits can be set for each assessment, and once a user begins an exam, a new timestamp is created on the server. The problem is that our school computers often lock up and students are forced to restart, losing exam time.
I figured out I could store the timer as a cookie, but that could easily be compromised. Any suggestions? Thanks.
You could store a timestamp in a database that is linked to their user (I would assume they have a login if they are doing exams) and perform an ajax call every few seconds to check if they are still online.
In this ajax check you can update the database with a new timestamp that indicates when they were last online, so if the computer locks up and they have to restart you can figure out how much time was lost.
You could generate unique hash to store it in the cookie like
$cookie = md5( $student_name . $test_start_timestamp . $computer_ip );
and store all the needed information in the database like:
INSERT INTO examination
SET student_hash = '$cookie',
test_start = '$test_start_timestamp',
computer_ip = '$computer_ip'
and later (if computer is restarted) you can get all the information from the database:
SELECT * FROM examination
WHERE student_hash = '$cookie'
this way the student won't be able to change the timestamp
While I realise that this can at times be difficult the only really reliable solution to your dilema is a reliable network.
But lacking that what about another communication media or pathway that confirms the client state? Can you confirm that a domain controller can see the client on the LAN? If the PC is present but the web client isn't it is more likely a user manuipulated situation. Can something (domain controller, other network location?) note the appearance or disappearance of a client from the network?
Any method is open to abuse somehow. I think the only real assurance will come from human monitoring of frequence of and user experiencing restarts.
Related
Basicaly, I have an PHP webservice which will be available from:
website
mobile phone clients - (android/iphone)
Data is retreived in JSON format. Request is sent using GET method over HTTPS protocol. Data in database is kept in mongo database.
Here comes my main questions:
I want to get the statistics for each user - how many calls he is doing per-minute/hour - how to save this data? Will it be okay to save everything into mongo database? (1 day statistics = 43 millions rows) Is there a solution maybe which will keep data only for X days and then auto-trunc everything automatically?
I also want to get statistics for each IP - how many calls were made from it per-minute/hour/day - how to save this data? Will it be okay to save everything into mongo database? Will it not become too large?
Is it always possible to see an IP of an user who is making a call to a webservice? what about IPv6?
These 3 questions are the most interesting to me currently. I am planning on letting users use basic services without the need of loggin-in! My main concern is the database/file system performance.
Next comes the description about - what measures I am planning to use. How did I come to this solution and why these 3 questions above are essential. Feel free to ignore the text below if you are not interested in details :)
I want to protect my webservice against crawling I.e. somebody can pass parameters (which are not hard to guess) to get entire data off my databse :)
Here is an usage example: https://mydomain/api.php?action=map&long=1.23&lat=2.45
As you can see - I am already using a secure https protocol, in order to prevent accidental catching entire GET request. It also protects agains 'man in the middle' attacks. However, it doesn't stop attackers from getting into website and going through JS AJAX calls to get an actual request structure. Or decompiling entire android's .APK file.
After reading a lot of questions through out the internet - I came to the conclusion that there is no way of protecting my data entirely, but I think I have found an approach of making the life of a crawlers a lot harder!
And I need your advice on either - if this whole thing is worth implementing and what technologies shall be used in my case (see next).
Next comes the security measures against non-website (mobile device) use of a service for users which are not logged-in.
<?php
/*
Case: "App first started". User: "not logged in"
0. generate UNIQUE_APP_ID for APP when first started
1. send UNIQUE_APP_ID to server (or request new UNIQUE_APP_ID from server to write it on device)
1.1. Verify how many UNIQUE_APP_IDs were created from this IP
if more than X unique values in last Y minutes ->
ban IP temporary
ban all UNIQUE_APP_IDs created by IP during Y minutes (use delay to link them together).
but exclude UNIQUE_APP_IDs from ban if these are not showing strange behaviour ( mainly - no calls to API)
force affected users to log-in to continue to use the service or ask to retry later when ban wears off
else register UNIQUE_APP_ID on server
Note: this is a 'hard' case, as the IP might belong to some public Wi-Fi AP. Precautions:
* temporary instead of permanent ban
* activity check for each UNIQUE_APP_ID belonging to IP. There could be legit users who use the service from long time thus will not be affected by this check. Created Z time ago from this IP.
* users will be not be ever banned - rather forced to log-in, where more restrictive actions will be performed individually!
Now that the application is registered and all validations are passed:
2. Case: "call to API is made". User: "not logged-in"
2.1. get IP from which call is made
2.2. get unique app ID of client
2.3. verity ID against DB on server
if not exists -> reject call
2.4. check how many calls this particular ID did in the last X minutes
if more than X calls -> ban only this unique ID
2.5 check how many Ids were banned from this IP in the last X minutes. If more than Y then ban new calls for whole IP temporary
check if all banned IDs were created from the same IP, if yes then also ban that IP, if it is different from new IP
*/
?>
As you can see - my whole solution is based on the idea that I can store the data about webservice and retrieve it for analysis easily.. for each single webservice call.. Or maybe each X'th call. I have no idea about - what kind of database shall be used. I was thinking that mongo might not be the best choice. Maybe MySQL? Keeping data safe from wrong users is one reason. Another reason is that abusal usage of database will result in a huge load on a database.(DDos?) So, i think this might be a good idea to count webservice calls.
On the other side. A bit of calculations.
If there are 1000 users working simultaniusly. Each generating 30 calls to a webservice per minute. So it's 30000 disc writes in a minute. In hour it's 60 times that i.e. 1.800.000 disc writes in an hour. If I am planning to keep statistics about daily usage then it's 24 times that i.e. in
average there will be 43.200.000 records for tracking purposes kept on a server.
Each record contains information about: time + IP + user unique ID
I was also thinking about not storing any data at all. Use redis instead. I know that there is some kind of counter exists. For for each individual IP I can create a separate key and start counting calls. In this case everything will be kept in server's RAM. There is also an expire date parameter which is possible to set for each key. And for separate users I can store their IDs instead of network IP. This solution only came to my mind after I finished writing this whole essay, so let me hear your ideas about questions above.
I am creating a web application named Online Exam using PHP+MySQL and AngularJS. Now I am getting some trouble on project like changing the user looged in status. Let us take this condition as a example:
Suppose a authorized user/student successfully logged in online exam section(After successfully logged current time will be inserted in the db in exam_start_time column as unix timestamp format and exam_status will be set as 'ACTIVE`.
1hr(60 min) countdown timer is initialize for him/her as per the inserted exam_start_time in db.
Now suppose after 15 min the system shuts down automatically, then if user logged in again(In same system or other) then the countdown timer should be set for 45 minutes only.
Previously I was updating the last_activity_time in our table in every 10 sec(using ajax calls). but now I want to change this way, Is there any way like(socket or network programming using PHP) to update the column.
Here is my table structure which is managing user logged in status
Please give me some suggestions on it.
A Php socket server programming tutorial : http://www.christophh.net/2012/07/24/php-socket-programming/
Sockets, as Pascal Le Merrer mentioned, is IMO your best option. But beware of Apache! Every WebSocket holds one Apache thread, and Apache wasn't designed to do that. when too many (and by too many I mean few dozen) clients connect to your site, it will crash. (I've been there while trying to implement long polling/comet, ended up using NodeJS. If you're using nginx, it is more likely that it will become low on resources and effective, but there are also other ways. Take a look here:
Using WebSocket on Apache server
If you find this uncomfortable/hard to learn, try also another idea:
try to add hidden iFrame to your exam page, pointing to prepared site that updates database row. Use javascript to refresh this page every 10-15 seconds. Every refresh causes update of specific row in DB, using current date and time. It should work (not tested, but give it a try).
I am confused about something. When I try to search how to count online users in PHP, all answers related with MySQL and many different ways.
In my script, any user that submits the login form creates a $_SESSION['$nickname']
So I thought, can I count login sessions with count($_SESSION['$nickname']); and show it in my page?
Or is this totally a wrong logic?
Totally wrong logic. $_SESSION is a per-user thing. One user's session is not shared with any other user's session. Think about it - an online bank written in PHP, all sharing a single $_SESSION - everyone would see everyone's account details.
Assuming you're on the standard PHP file-based sessions, you can count the session files in whatever directory they're stored, e.g.
$users = count(glob(session_save_path() . '/*'));
Note that this just counts session files - it will undoubtedly contain stale/dead sessions that haven't been garbage collected yet. If you want an actual "really is online right now", you'd have to parse each session file and examin its contents.
At first, you have to define what "to be online" means.
Should the user have clicked on a link within the last 5 minutes?
I assume that you already have a user table in your database.
So the simplest way is to add a new column, e.g. lastAction TIMESTAMP.
And when the user clicks on a link on your page, your script should update this value.
And on your statistics page or whatever, you get the number of online users with that code:
SELECT COUNT(*) FROM users WHERE lastAction > (NOW() - 60*5)
PHP is pretty flexible in terms of session storage, you can define your own session save/restore handlers.
However, the default session storage is files, where each session is individually stored to disk. Which means, that in order to find out how many users there are "online" (here, i assume "online = session exists with $_SESSION['$nickname'] set"), you would need to open all the session files stored on disk, and check how many unique nicknames exist within them. This is very heavy in both time and required resources.
Hence, most tutorials suggest counting this in the database, by maintaining a last-seen timestamp per user (and checking how many users were last seen in last X minutes).
If you wish to combine, that is doable via defining your own session save handler to store session information in the database...
Several additional notes on sessions:
Sessions are not destroyed immediately when closing a browser. In fact, the browser does not tell the server in any way that it is being closed. It means that the server should come up with some time-based algorithm to decide who's online and who is not.
Sessions are isolated from one another so $_SESSION["foo"] can not be shared by multiple different sessions - there is no equivalent to Global.asa in PHP. Not off the box.
This is the wrong logic and the session is only stored and immediately destroyed locally on the end user's PC, so it cannot be read from the server for any kind of confirmation without the user doing something. Server-side sessions are only destroyed on garbage cleanup.
Your best bet is to have a timestamp for each user which is updated whenever a user does anything. This means you will need to have this update triggered via JS/AJAX, PHP. Then you can check to see how many timestamps are within the last 5 minutes for example, and this would give you a rough idea of how many users are currently online.
You could also have a hidden iframe which continually refreshes every few seconds and updates the timestamp, or JS/AJAX which updates the timestamp every X seconds as well...
I'm developing a project of mine with scalability in mind and I've come to a crossroad. On my website I would like to detect if a user is online or not. And I can't quite think of the best way to handle this. The way I was thinking would be something along these lines(in psuedocode):
// SQL user table:
user {
"name": "blah blah",
"email": "derpy#derpyderp.com",
"online": false
}
So whenever the user logs in I could update his online column to true. However that would eventually lead to SQL queries happening every time a user logs in and if it happens that I get say, 10 logins per second, well, that's a lot of queries happening. Another way I figured I could do the same thing but in a different table:
// Activity table:
activity {
"user_id": 2,
"online": true
}
For some reason I believe that would lead to less memory consumption because of the separation from the user table. However I'm not sure if it would have any actual effect on performance.
So if you could bless me with your insight I would be more then grateful, thank you.
Generally speaking it's a common practice to add a column to the users table to store the lastActivity time. Anytime the user logs in, or accesses a page, store the current time in that field. If you want to know whether or not they are online, see if the last recorded time is within a certain window - say, five minutes. You can query all rows to see how many users are currently online as a result.
I wouldn't be too worried about running queries every few seconds - your server can handle it (assuming these are well-written and not very verbose).
you can use datetime for field type and don't forget to record user IP so you can track time o
Depending on how you want it to work you basically have two options:
Define a timeout after which you consider a user logged out
Use ajax/websockets/whatever to poll user
1: Timeout
This is the simpler use case. Every time the user requests a page, you update a timestamp in your database.
To find out how many users are online, you would do a query against this database and do a COUNT of users who have been active in the last N minutes.
This way you will get a relatively accurate idea of how many people are actively using the site at the moment.
2: Constant polling
This is a bit more complex to implement due to having to update the server with Ajax. Otherwise it works in a similar fashion to #1.
Whenever a user is on a page, you can keep a websocket open or do ajax requests every N seconds to the server.
This way you can get a pretty good idea of how many people have pages open on your site currently, but if a user leaves the page open in their browser and doesn't do anything, it would still count them as being online.
A slight modification to the idea would be to use a script on the client to monitor mouse movement. If the user doesn't move the mouse on your page for say 10 minutes, you would stop the polling or disconnect the websocket. This would fix the problem of showing users who are idle as being online.
i need to show the number of online visitors, but there is a problem with selecting algoritm to do it!
maybe i must create a table in DB, where i'll store ip addresses of visitors and time of visit! by so i can show the count of ip addresses, which's time >= NOW() - 10 minutes, for example...("now()-10 minutes" is just to show the logic, i know that this is not a function:)
is this goog way to go?
please give me an idea.
Thanks
This is good tutorial. Note that mysql (i believe youll use it) online users table should be typed as MEMORY.
I'm not really sure how you would use AJAX to store the data...
I personally use the database solution.
I store user_id, last_seen, IP and location in the site (but that's not necessary to just get the count).
When the user requests a page refresh the last_seen column and delete all the entries with NOW()-last_seen greater than x minutes.
Keeping track of "visitors" (as opposed to raw page requests, which the web server should track on its own) is a complex art.
You could store IP addresses, as you described, but what about a visitor who's using a proxy that rotates their IP as frequently as every page load? What about a set of visitors all using the same proxy that uses the same IP for all of them?
My recommendation: don't bother doing any of it yourself, and use Google's free Analytics service. It tracks visitors, browsers, traffic sources, and just about anything else you could possibly want to know about who's looking at your site.
Yes, the algorithm is o.k. in general but with some corrections
May be you want to delete all outdated records first, and then just count the rest.
10 minutes is too much. 1-3 is the average time user spend on the page.
AJAX project is funny, but it has nothing to do with storage. You need more education of terms of client-server application. AJAX is the transport, not storage.
If you expect the website will have a lot of visitors, the query could make the page for one user every 10 minutes pretty slow...
If so, I would suggest to write an CLI script that will clean the old entries and run it in a cronjob. That way the user wouldn't notice any delay, as the parsetime will be spent on the CLI.