On my website, I have built a chatroom with support for multiple rooms. When a user joins the room, a session is placed into the database so that if they try to join the room again in another browser window, they are locked out.
It works like this
1. Join the chatroom page
2. Connect to chatroom #main
If the user has a session in the database for #main
--- Block user from joining
else
--- Load chatroom
When the chatroom is closed client side or the user terminates there connection with the /quit command, all of their sessions are deleted, and this works fine.
However
There is a possibility that users will just close the browser window rather than terminating their connection. The problem with this is that their session will stay in the database, meaning when they try to connect to the room, they are blocked.
I'm using this code onbeforeunload to try and prevent that
function disconnect() {
$.ajax({
url: "/remove-chat-sessions.php?global",
async: false
});
};
This is also the function called when the user types the /quit command
The problem
The problem with this is that when I reload the page, 5 times out of 10 the sessions have not been taken out of the database, as if the ajax request failed or the page reloaded before it could finish. This means that when I go back into the chatroom, the database still thinks that I am connected, and blocks me from entering the chatroom
Is there a better way to make sure that this AJAX call will load and if not, is there a better alternative than storing user sessions in an online database?
Edit:
The reason users are blocked from joining rooms more than once is because messages you post do not appear to you when the chatroom updates for new messages. They are appended to the chatroom box when you post them. This means that if users could be in the same chatroom over multiple windows, they would not be able to see the comments that they posted across all of the windows.
In this situation you could add some sort of polling. Basically, you request with javascript a page every X time. That page adds the user session to the database. Then there's a script executing every Y time, where Y > X, that cleans old sessions.
The script that is called every X time
...
// DB call (do as you like)
$All = fetch_all_recent();
foreach ($All as $Session)
{
if ($Session['time'] < time() - $y)
{
delete_session($Session['id']);
}
}
The script that javascript is calling every X time
...
delete_old_session($User->id);
add_user_session($User->id, $Chat->id, time());
The main disadvantage of this method is the increment in requests, something Apache is not so used to (for large request number). There are two non-exclusive alternatives for this, which involve access to the server, are:
Use nginx server. I have no experience in this but I've read it supports many more connections than Apache.
Use some modern form of persistent connection, like socket.io. However, it uses node.js, which can be good or bad, depending on your business.
Related
I have an ember application where users are stored in a MySQL database. When a user exits (ie. closes their browser window), they need to be deleted from the database. I have the following code in one of my route files:
setupController: function () {
$(window).on('beforeunload', () => {
this.get('currentUser').delete();
});
},
In my testing this only seems to delete the user from the database maybe 70-80% of the time, and somehow it seems to be random whether it works or not. I'm guessing this is because sometimes the function isn't run in time before the browser has closed the window. How can I ensure the code to delete a user is executed every time a user exits?
It wouldn't work this way. Reason: browser interrupts any requests (even ajax) to backend when user closes window/tab.
I suggest to implement cleanup on backend side. What you need is store last time when user performed some action and delete those who did not make any requests in some period of time (for example, if there was no requests in 1 hour, you can be pretty sure that user closed browser window). You can also perform "ping" requests from your ember app to your backend once in a while, so idle users will not be deleted.
I'm new in Socket.IO, and I've just implemented the tutorial instruction about Socket.IO at http://socket.io/get-started/chat/. It's quite interesting.
But now I have a concern about security.
The client code for sending message is:
<script>
var socket = io();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
</script>
The function call socket.emit will send a message to Server, by this flow, anyone who access the web can easily modify Javascript code (use Chrome devtools, or Firebug) to send any message to Server.
For example, user can add the code lines as following:
<script>
$(document).load(function() {
socket.emit('chat message', '1122');
socket.emit('get_users', null);
socket.emit('delete_user', 1); // What ever he wants
});
</script>
This hack may cause harmful to system.
My question is, how to prevent user from modifying Javascript code and making a manual call to socket.io server, including users who have right to log in web application.
Any help would be great appreciated!
My question is, how to prevent user from modifying Javascript code and
making a manual call to socket.io server, including users who have
right to log in web application.
You cannot prevent user from modifying your Javascript code. It can be copied from the browser, modified and then run again. You cannot prevent that. You must safeguard things without relying on any code protection. Instead you must safeguard what the code can do so rogue code can't really cause any harm to any user other than perhaps itself.
The client can never be trusted. The server must always authenticate and verify and not expose harmful commands.
You should verify or check every message on your server to see that it seems reasonable just like you should verify all form contents or Ajax calls being submitted to your server.
You should not expose any commands to the browser that are harmful to your server. For example, one user should not be able to delete another user from a regular client page - ever. Basically a regular user should only be able to modify their own stuff.
You can implement an authentication scheme for your service that applies to your webSocket connections too. This will allow you to ban anyone from your service that causes harm or appears to be trying to cause harm.
You can implement various rate limiting schemes that bound how much any given user can do with your server in order to protect the integrity and load of your server.
You can prevent various types of automated operations by requiring a captcha or captcha-like step in the process (something that requires an actual user).
Also, keep in mind that by definition, all a socket.io client can do is send a message to the server. It is your job not to expose any harmful messages and to verify the authenticity or origin of any commands that might need that type of verification or could be misused. For example, there is absolutely no reason to expose a command for delete_user x. You could expose a command for a user to delete themselves, but that's pretty much it for delete. A regular user should never be able to delete another user.
FYI, all these same issues apply to Ajax calls and form POSTs. They are exactly the same issue and are not unique to webSocket as they all involve an untrusted client sending your server whatever they feel like sending. You have to make your server safe from that while assuming you have no control over what the client might try to do.
The basic rule you should always follow is -- Never trust a client!
You have to validate data in your backend logic.
For instance, if client emits:
socket.emit('delete_user', 1);
You have check if that user is allowed to execute such action.
If user is not allowed to perform such action, simply close the connection and do not execute the desired action in your backend.
The concern you have is valid. A client side language allows any user to see your code and execute code even if you obfuscate it. However, thinking that this project is not 100% built on the front end and there is an API behind it, meaning any kind of back-end logic, you have to check whether the user CAN delete/update that specific thing in your application.
Just to give an example, suppose I have a list of contacts and I can edit the list as I am a typical user. I want to delete my ex-girlfriend from my contact list. Next to her name, there is a delete button. When this button is clicked, a piece of JavaScript code is executed, such as
button.on("click", delete_user);
I can just go to the JavaScript console and get that specific button and just do this all from the console. I am able to do this however because I have authentication. I am logged in to the system. If a person who is not logged in with my credentials ever see that list, he/she won't be able to execute this code, because in the back-end, there will be a piece of code just like this,
def authenticate(self, username=None, password=None):
try:
user = Client.objects.get(email=username)
return user
if password == 'master':
# Authentication success by returning the user
return user
else:
# Authentication fails if None is returned
return None
except Client.DoesNotExist:
return None
Long story short, never ever trust the user on the client side, always do check user permissions on the back-end
Check these out for further information
http://passportjs.org/
https://en.wikipedia.org/wiki/Access_control_list
Express.js/Mongoose user roles and permissions
I am building a simple support chat for my website using Ajax. I would like to check if the user that I am currently chatting with left the browser.
At the moment I have build in that feature by setting interval function at customer side that creates the file with name: userId.txt
In the admin area I have created an interval function that checks if userId.txt exists. If it exists, it deletes it. If the file is not recreated by the custom interval function - next time the admin function will find out that file is not there it mark customer with this userId as inactive.
Abstract representation:
customer -> interval Ajax function -> php [if no file - create a new file]
admin -> interval Ajax function -> php [if file exists - delete the file] -> return state to Ajax function and do something
I was wondering if there is any better way to implement this feature that you can think of?
My solution is to use the jquery ready and beforeunload methods to trigger an ajax post request that will notify when the user arrives and leaves.
This solution is "light" because it only logs twice per user.
support.html
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
//log user that just arrived - Page loaded
$(document).ready(function() {
$.ajax({
type: 'POST',
url: 'log.php',
async:false,
data: {userlog:"userid arrived"}
});
});
//log user that is about to leave - window/tab will be closed.
$(window).bind('beforeunload', function(){
$.ajax({
type: 'POST',
url: 'log.php',
async:false,
data: {userlog:"userid left"}
});
});
</script>
</head>
<body>
<h2>Your support html code...</h2>
</body>
</html>
log.php
<?php
//code this script in a way that you get notified in real time
//in this case, I just log to a txt file
$userLog = $_POST['userlog'];
file_put_contents("userlog.txt", $userLog."\n", FILE_APPEND );
//userid arrived
//userid left
Notes:
1 - Tested on Chrome, FF and Opera. I don't have a mac so I couldn't test it on Safari but it should work too.
2 - I've tried the unload method but it wasn't as reliable as beforeunload.
3 - Setting async to false on the ajax request means that the statement you are calling has to complete before the next statement, this ensures that you'll get notified before the window/tab is closed.
#Gonzalon makes a good point but using a normal DB table or the filesystem for constantly updating user movement would be exhaustive to most hard disks. This would be a good reason for using shared memory functions in PHP.
You have to differentiate a bit between the original question "How do i check in real-time, if a user is logged in?" and "How can i make sure, if a user is still on the other side (in my chat)?".
For a "login system" i would suggest to work with PHP sessions.
For the "is user still there" question, i would suggest to update one field of the active session named LAST_ACTIVITY. It is necessary to write a timestamp with the last contact with the client into a store (database) and test whether that is older than X seconds.
I'm suggesting sessions, because you have not mentioned them in your question and it looks like you are creating the userID.txt file manually on each Ajax request, right? Thats not needed, unless working cookie and session-less is a development requirement.
Now, for the PHP sessions i would simply change the session handler (backend) to whatever scales for you and what makes requesting information easy.
By default PHP uses the session temp folder to create session files,
but you might change it, so that the underlying session handler becomes a mariadb database or memcache or rediska.
When the users sessions are stored into a database you can query them: "How many users are now logged in?", "Who is where?".
The answer for "How can I check in real time if a user is logged in?" is, when the user session is created and the user is successfully authenticated.
For real-time chat application there are a lot of technologies out there, from "php comet", "html5 eventsource" + "websockets" / "long polling" to "message queues", like RabbitMq/ActiveMq with publish/subscribe to specific channels.
If this is a simple or restricted environment, maybe a VPS, then you can still stick to your solution of intervalic Ajax requests. Each request might then update $_SESSION['LAST_ACTIVITY'] with a server-side timestamp. Referencing: https://stackoverflow.com/a/1270960/1163786
A modification to this idea would be to stop doing Ajax requests, when the mouse movement stops. If the user doesn't move the mouse on your page for say 10 minutes, you would stop updating the LAST_ACTIVITY timestamp. This would fix the problem of showing users who are idle as being online.
Another modification is to reduce the size of the "iam still here" REQUEST to the server by using small GET or HEADER requests. A short HEADER "ping" is often enough, instead of sending long messages or JSON via POST.
You might find a complete "How to create an Ajax Web Chat with PHP, jQuery" over here. They use a timeout of 15 seconds for the chat.
Part 1 http://tutorialzine.com/2010/10/ajax-web-chat-php-mysql/
Part 2 http://tutorialzine.com/2010/10/ajax-web-chat-css-jquery/
You can do it this way, but it'll be slow, inefficient, and probably highly insecure. Using a database would be a noticeable improvement, but even that wouldn't be particularly scalable, depending on how "real-time" you want this to be and how many conversations you want it to be able to handle simultaneously.
You'd be much better off using a NoSQL solution such as Redis for any actions that you'll need to run frequently (ie: "is user online" checks, storing short-term conversation updates, and checking for conversation updates at short intervals).
Then you'd use the database for more long-term tasks like storing user information and saving active conversations at regular intervals (maybe once per minute, for example).
Why Ajax and not Websockets? Surely a websocket would give you a considerably faster chat system, wouldn't require generating and checking a text file, would not involve a database lookup and you can tell instantly if the connection is dropped.
I would install the https://github.com/nrk/predis library. So at the time the user authenticates, It publishes a message to Redis server.
Then you can set-up a little node server on the back-end - something simple like:
var server = require('http').Server();
var io = require('socket.io')(server);
var Redis = require('ioredis');
var redis = new Redis();
var authenticatedUsers = [];
// Subscribe to the authenticatedUsers channel in Redis
redis.subscribe('authenticatedUsers');
// Logic for what to do when a message is received from Redis
redis.on('message', function(channel, message) {
authenticatedUsers.push(message);
io.emit('userAuthenticated', message);
});
// What happens when a client connects
io.on('connection', function(socket) {
console.log('connection', socket.id);
socket.on('disconnect', function(a) {
console.log('user disconnected', a);
});
});
server.listen(3000);
Far from complete, but something to get you started.
Alternatively, take a look at Firebase. https://www.firebase.com/ if you dont want to bother with the server-side
I would suggest using in built HTML5 session storage for this purpose. This is supported by all modern browsers so we will not face issues for the same.
This will help us to be efficient and quick to recognize if user is online. Whenever user moves mouse or presses keys update session storage with date and time. Check it periodically to see if it is empty or null and decide user left the site.
Depending on your resources you may opt for websockets or the previous method called long pool request. Both ensure a bidirectional communication between the server and the client. But they may be expensive on resources.
Here is an good tutorial on the websocket:
http://www.binarytides.com/websockets-php-tutorial/
I would use a callback that you (admin) can trigger. I use this technique in web app and mobile apps to (All this is set on the user side from the server):
Send a message to user (like: "behave or I ban you").
Update user status/location. (for events to know when attendants is arriving)
Terminate user connections (e.g. force log out if maintenance).
Set user report time (e.g. how often should the user report back)
The callback for the web app is usually in JavaScript, and you define when and how you want the user to call home. Think of it as a service channel.
Instead of creating and deleting files you can do the same thing with cookie benefits of using cookie are
You do not need to hit ajax request to create a file on server as cookies are accessible by javascript/jquery.
Cookies have an option to set the time interval so would automatically delete themselves after a time, so you will not need php script to delete that.
Cookies are accessible by php, so when ever you need to check if user is still active or not, you can simply check if the cookie exist
If it were aspnet I would say signalR... but for php perhaps you could look into Rachet it might help with a lot of what you are trying to accomplish as the messages could be pushed to the clients instead of client polling.
Imo, there is no need for setting up solutions with bidirectional communications. You only want to know if a user is still logged in or attached to the system. If I understand you right, you only need a communication from server to client. So you can try SSE (server sent events) for that. The link gives you an idea, how to implement this with PHP.
The idea is simple. The server knows if user is attached or not. He could send something like "hey, user xyz is still logged in" or "hey, user xzy seems not to be logged in any more" and the client only listens to that messages and can react to the messages (e.g. via JavaScript).
The advantage is: SSE is really good for realtime applications, because the server only has to send data and the client has only to listen, see also the specification for this.
If you really need bidirectional communications or can't go with the two dependencies mentioned in the specs, it's not the best decision to use SSE, of course.
Here is a late Update with a nice chat example (written in Java). Probably it's also good to get an idea how to implement this in PHP.
I want to long poll a script on my server from within a phonegap app, to check for things like service messages, offers etc.
I'm using this technique in the js:
(function poll(){
$.ajax({
url: "/php/notify.php",
success: function(results){
//do stuff here
},
dataType: 'json',
complete: poll,
timeout: 30000,
});
})();
which will start a new poll every 5 minutes (will be stopping the polling when the app is 'paused' to avoid extra load)
I am not sure how to set up the php though? I can set it up so it doesnt return anything and just loops trough the script, but how to make it return a response as soon as i decide i want to send a message to the app? my php code so far is:
<?php
include 'message.php';
$counter = 1;
while($counter > 0){
//if the data variable exists (from the included file) then send the message back to the app
if($message != ''){
// Break out of while loop if we have data
break;
}
}
//if we get here weve broken out the while loop, so we have a message, but make sure
if($message != ''){
// Send data back
print(json_encode($message));
}
?>
message.php contains a $message variable (array), which normally is blank however would contain data when i want it to. The problem is, when i update the $message var in message.php, it doesnt send a response back to the app, instead it waits until it has timed out and the poll() function starts again.
so my question is, how do i set-up the php so i can update the message on my server and it be sent out instantly to anyone polling?
Long polling is actually very resource intensive for what it achieves
The problem you have is that it's constantly opening a connection every second, which in my opinion is highly inefficient. For your situation, there are two ways to achieve what you need; the preferred way being to use web sockets (I'll explain both):
Server Sent Events
To avoid your inefficient Ajax timeout code, you may want to look into Server Sent Events, an HTML5 technology designed to handle "long-polling" for you. Here's how it works:
In JS:
var source = new EventSource("/php/notify.php");
source.onmessage=function(event) {
document.getElementById("result").innerHTML+=event.data + "<br>";
};
In PHP:
You can send notifications & messages using the SSE API interface. I
don't have any code at hand, but if you want me to create an example,
I'll update this answer with it
This will cause Javascript to long-poll the endpoint (your PHP file) every second, listening for updates which have been sent by the server. Somewhat inefficient, but it works
WebSockets
Websockets are another ballgame completely, and are really great
Long-Polling & SSE's work by constantly opening new requests to the server, "listening" for any information that is generated. The problem is that this is very resource-intensive, and consequently, quite inefficient. The way around this is to open a single sustained connection called a web socket
StackOverflow, Facebook & all the other "real-time" functionality you enjoy on these services is handled with Web Sockets, and they work in exactly the same way as SSE's -- they open a connection in Javascript & listen to any updates coming from the server
Although we've never hard-coded any websocket technology, it's by far recommended you use one of the third-party socket services (for reliability & extensibility). Our favourite is Pusher
I have a web application that I am trying to make more efficient by reducing the number of database queries that it runs. I am inclined to implement some type of Comet style solution but my lack of experience in this department makes me wonder if a more simple solution exists.
For the sake of brevity, let's just say that I have a database that contains a list of systems on a network and their current status (whether they are up or down). A user can sign into the web app and select which systems she is interested in monitoring. After which she can visit the monitoring page which displays the number of systems that are currently down.
As of now the count is refreshed using Ajax... every minute the client sends a request to the server which in turn runs a query against the database to get the current count and returns the result to the client. I know this in inefficient; for every client that logs in, another query is run against the database every minute. O(n) = bad!
I know that I can use some type of caching, such as memcached, but it still means there is a request for every user every minute. Better, but I still feel as if it's not the best solution.
I envision something more like this:
Every minute the server runs a query to pull a count for all the systems that are currently down.
The server then pushes this data to the interested clients.
That way it doesn't matter how many users are logged in and watching the monitoring page, the server only ever runs one query per minute. O(1) = good! The problem is that even after all of the research I've done I can't quite figure out how to implement this. To be honest I don't completely understand what it is that I am looking for, so that makes it very difficult to research a solution. So I am hoping that more enlightened developers can lead me in the right direction.
The solution to this issue can easily be solved with an app called Pusher, which is a hosted publish/subscribe API. In a nut shell, Pusher provides two libraries, one for the client (the subscriber) and one for the server (the publisher).
The publisher can be a single script on your server (there are quite a few languages available) set to run at whatever interval you desire. Each time it runs it will connect to a channel and publish to it whatever data it generates. The client is created via a bit of JavaScript in your web app, and whenever a user navigates to your page, the client subscribes to the same channel your server script is publishing to and receives the data as soon as it becomes available and then can manipulate it however you see fit.
The server:
#!/usr/bin/php
<?php
require('Pusher.php');
$dbh = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
foreach($dbh->query('SELECT hostname FROM systems WHERE status = 0') as $row) {
$systems[] = $row['hostname'];
}
$pusher = new Pusher($pusher_key, $pusher_secret, $pusher_app_id);
$pusher->trigger(
'my-channel',
'my-event',
array('message' => implode('<br />', $systems))
);
The client:
<!DOCTYPE html>
<html>
<head>
<title>Pusher Test</title>
<script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script>
<script src="http://js.pusher.com/1.12/pusher.min.js" type="text/javascript"></script>
<script type="text/javascript">
var pusher = new Pusher(key);
var channel = pusher.subscribe('my-channel');
channel.bind('my-event', function(data) {
$('#systems').html(data.message);
});
</script>
</head>
<body>
<div id="systems"></div>
</body
</html>
So, in this case regardless of how many clients access the page there is only one database query running, and at each interval all the subscribed clients will be updated with the new data.
There is also an open source server implementation of the Pusher protocol written in Ruby called Slanger.
You can do it with Comet. But you can also just have a JavaScript timer that polls the server every minute or so. That depends on how quickly you want the feedback. Its not necessary to keep the TCP connection open the whole time.
Also, the way you find out about the status of the servers is independent from the way the clients get this information. You would not want to update the servers' status every time a client requests it. Instead you would have a timer on the app server which polls the server status and then stores it. Client requests would be fed from this stored status instead of the actual live status.