Open Sephi-Chan opened 14 years ago
This appears to be exactly how PusherApp works, so I bet it's possible and reliable and I agree, this would be a very nice feature, secure, private messaging is a must for a wide variety of apps.
I don't know about encryption in Javascript... But what you are suggesting indeed is something in need. I solved it in my project by using a random generated string in my Rails app. Then I output this string in a meta tag in the HTML so javascript generates the channel name (like /channelname/secret_string). It is not the best solution but is pretty ok security wise if the string length is appropriate. May affect network performance a bit though (more data to send with each message).
This solution is more obfuscation than security but i think i'll use it for my needs : it's really simple and I don't need to be really confident, it's just a webgame. :)
Yes I know it's not the proper way, just dirty fix. I hope in the future it will be possible in Juggernaut. Do you have any ideas how you would implement it in Juggernaut? Without encryption... The problem is usually you will have to connect the http web request (to rails app) with socket connection (session id). What do you think the best way would be to do that? IP is not good because a user can open more than 1 browser or have LAN computers... I think some kind of ID or token generated by Rails (like I wrote above anyway) should be a pretty ok solution. Then you can authenticate the user based on this ID. Of course this kind of way is still susceptible to sniffing attack, but I don't think that's important here.
What pusher does is pretty much what you suggest - the JS library makes an AJAX call to your server via some well known URL. Your server uses session info to authenticate you like it would authenticate you for any other request, except that it also has the channel name and the socket_id passed in as params. Then it uses your API key as a shared secret and signs some kind of token, which the JS library simply relays to the websocket server. The websocket server can decrypt the signed token and knows who you are. I think this works pretty well.
Yes, we had this feature in the previous Juggernut server (http auth callbacks) - I haven't included it yet in this one as we had performance issues - I aiming to keep Rails and the Node server totally asynchronous. At the moment I think you should just use security through obscurity - just have a unique random id as a channel name.
Alternatively - there are callbacks when clients connect/disconnect, and you can pass meta data with those callbacks. Using that, you can detect when somebody has attached to a channel they weren't meant to (hopefully a very rare event) and stop broadcasting to that channel. I know it's not ideal.
I'll look into PlasticLizard's suggestion, a signed shared secret might be a good way to do it.
What about using Rails' authenticity token?
Some sort of ability to use tokens for authentication (without the need for using Ruby) would be great. Even crucial. Obscurity isn't a good solution when you don't want a logged-out user still able to subscribe to a channel he or she has the random channel name from a previous session.
If callbacks are not performant, what about middle ground? Say, a randomized signature token (using HMAC, or SHA1 with shared secret) good for 60 seconds, set in an X-Header, that's used in the subscribe chain. If the token passes, proceed with the subscription, otherwise, don't add the subscription.
edit: ahem, sorry, I just ready the code and realized it's just a socket by that point so the x-header would be out. But a shared secret could still work. I might implement it on my own and let you know of my experience. Thanks.
@bemurphy, is my new private_pub project along the lines of what you were thinking?
Currently this only works with Faye, but I've been considering making a Juggernaut adapter. Check out this example project to see it in action.
@ryanb Funny you should ask, I shoehorned it in to a local juggernaut over the weekend and got it working, then read your project readme a little later. Yeah, pretty much what you've got with data-signature is what I was thinking.
Just curious if there's been any progress on creating true private channels in Juggernaut. I'm about to have to start looking into getting something to work and I don't want to reinvent the wheel if someone has already made some progress down this path. I'd rather help contribute to a solution that will be good for everyone.
Thanks, Jeremy
As an aside: were using jugg2 in our new project in connection with a rails3 instance. Every user gets a private "channel-token" for every session that is governed by the rails-server. This is the channel the user subscribes to and it is not distributed to others. In combination with SSL, we regard this as secure enough. All client-side initiated communication goes via ajax directly to the server, and pushes from the server to the client go through jugg2. --Frank
Frank, do you never have two users listening to the same channel then? I suppose it depends on your needs, but what do you need to broadcast to multiple users? Are you just doing multiple Juggernaut.publish for each user in rails?
Didn't consider publishing to multiple channels ala Juggernaut.publish(['user-token1','user-token2'] ... is that what you're doing Frank?
Hi snleson,
do you never have two users listening to the same channel then? yup, every user get his own "private" channel that is created new on each session start. This is a kind of abuse of the pubsub system, but it works.
publishing to multiple channels ala Juggernaut.publish(['user-token1','user-token2'] exactly - performance wise, this isn't even a problem when you push to a thousand channels at once
--Frank
I like it ... what happens when they have multiple sessions (like two browsers open)?
Aas the channel token is newly generated on every login, only the last opened window will get updates. We thought about this and decided that this is rather a feature than a flaw ;)
Although if you have two windows open, they'll have the same session, both been logged in, and the same channel token.
I use a similar method, but with the users ID - this is a GUID so it isn't guessable.
On Tue, Apr 26, 2011 at 9:45 PM, fwoeck < reply@reply.github.com>wrote:
Aas the channel token is newly generated on every login, only the last opened window will get updates. We thought about this and decided that this is rather a feature than a flaw ;)
Reply to this email directly or view it on GitHub: https://github.com/maccman/juggernaut/issues/14#comment_1062970
Alex MacCaw
(415) 513-0975 @maccman
http://alexmaccaw.co.uk | http://www.leadthinking.com | http://socialmod.com
Yes, it depends on the actual application I think. We produce a collaboration tool, were group members see other members as avatars and these users are hold in the browser as backbonejs object including their backend ids. So this information is not private anymore and not usable as token in our case.
The other problem with multiple open windows is, that you have to synchronize user triggered actions to every active window of a user, which takes probably more effort than just reacting on a user interaction locally and propagating screen updates to the related users.
Why not factor the current time into the private token which will make each one unique? It will then not be dependent upon the user's current session and allows multiple browser windows open. It also allows you to auto-expire it after a certain amount of time. This is what I do in the private_pub library (it looks like links with underscores currently aren't working here so you'll have to fix the URL).
@ryanb when I get it right, the private token in private_pub requires to be checked by the server - like e.g. the csrf-token in a rails form, right? The method of user-exclusive pubsub-channels is attractive, because it doesn't require adittional checks. If you avoid the channels beeing sniffed by others (ssl) it require only to stop sending on that channel if a server wants a specific client to be banned. The client would have to re-authorize via standard login to get a new channel token.
@fwoeck, correct, each subscribe call passes in a signature which is unique to that specific request/channel. This needs to be authorized on the server side, but is done through a Faye extension so it all happens behind the scenes and the Rails app isn't involved.
I see now both approaches have their pros and cons. The user token is simpler up front but leaves the app to manage the which users should receive which messages. The unique signature approach requires an extra check up-front, but the app then doesn't need to be concerned about authorizing which messages go to which user.
Also good point about banning clients. If a user logs-out it's still possible to listen to messages if they have the signature and it hasn't yet expired.
My node-fu is pretty weak, but I took a rough stab at this by adding subscription tokens. My commit is at https://github.com/bemurphy/juggernaut/commit/f4838d91f3075c60de7b05543d369db3b5782879 so you can see if I'm on a worthwhile track.
The client passes a token that is sha1(sharedSecret:channelName:timestamp) to the server. The server verifies the signature is proper and recent enough, as well as tracks used signatures in redis for a short time to prevent reuse. Also, I'll probably want to go back and fix up the client subscription to allow for subscriptions sans tokens.
Personally I'd prefer a token approach over unique channel names per user. Thinking about a group chatroom where everybody is on a different channel name seems unintuitive to me.
EDIT: just dawned on me btw, that limiting a signature to a single use could be dangerous. I have a hunch it would break reconnects, gotta go sift through that code...
I need to add some validation on the Rails side when a user subscribes to a channel, and disconnect the user from the channel if the auth fails. Is this possible with the juggernaut gem? Looking through the examples/code, I see that you can check when the user subscribes/unsubscribes, but can you actually do anything with it other than track it?
@pselden4 afaik it's not possible to enforce a disconnection from within ruby - you would have to stop sending to that channel.
Hello,
first time I write here :), I'm working with juggernaut and I have the same question about security, how we can control the people who subscribe to the channels.
I've read/seen 3 ways:
To sum up, I've already implemented the first way but I don't see how I can do it better and make the channels truly private, has someone figured out a better way?
Thanks in advance.
I ran into the same issue but didn't want to switch to faye and private_pub, because I already had all the juggernaut stuff up and running nicely. Therefore I made a bunch of changes to the juggernaut code to allow for an easy authentication mechanism and some additional new features. If you are interested, check out this commit: https://github.com/FewKinG/juggernaut/commit/f6efd8fa0b0e1bcdceebf6537750f05b6225f9ce
and read the corresponding readme chapter (at the very bottom): https://github.com/FewKinG/juggernaut/blob/master/README.md
Hello, I'm trying Juggernaut in order to develop a webgame (with Rails) and I need private channels.
I looked at your example about that but it's seems to not be secure : everybody can open a Javascript console on the website and subscribe to a channel, no ?
So, there is a way to have really private channels ? Using a callback (called on subscribe) to know (by asking to the Rails server, in my case) if the Juggernaut server should accept/refuse the connection ? Does this feature exists ? Would it be hard to implement ?
I have another idea : using encryption with private and public keys (both of these keys have to be injected to the client-side Javascript code to be able to decrypt datas received and crypt data for sending). Can it be reliable ?