I am setting up a socket.io server to handle, well, socket requests. This is running on port 1234. This is running along side a laravel 5.1 application. Laravel is using redis to handle sessions.
I have plenty of tutorials on hooking up laravel with socket.io, it's all pretty straight forward. I can connect, respond and forward messages back down the socket and to the laravel application.
However every tutorial avoids the auth part of this setup. Once the message is received within the socket:1234 space, how do I forward that message through to laravel while making sure that request is auth'ed.
Ideally I would simply share the session, and verify the XSRF token. Because the two applications are on different ports, I can't pick up the session directly.
Currently I am using an alternative approach, it involves the following:
Upon socket connection (in node), I decrypt the cookie sent up on connection using node's Crypto library and node's PHPUnserialise library.
This gives me the laravel session id (from the cookie)
I use this to access the redis laravel session
I then decrypt that session, which in turn, gives me access to the user id
It works, but I feel it could be potentially be a security hole, because I am not actually using _token to verify the origin.
I think your code is the right, and maybe the only way to do it.
A session_id is usually stored in the cookie, and at some point has to be sent to the server. Since node and php are different languages, they cannot share a session directly. You always need a intermediate storage like redis, mysql or filesystem. And of course a way to retrieve the session. The key to retrieving a session is of course the session_id.
An interesting post about securing websockets:
https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html
What he suggests is to add a random generated key to your session, that you can verify when you the websocket connection is established.
The session_id itself is already random, but these session_id's are usually long-lived, so a short-lived random id could increase security. Short-lived should be as short as possible: let php add it to the database, and once the connection is verified in node, remove it from the database, so you cannot use it again.
There are lots of additional session verification techniques, like checking the browser string, or fixating a session to one ip adress:
http://phpsec.org/projects/guide/4.html
I would not recommend these type of checks, as they don't really add much extra security, only annoyance with the end user.
Most importantly i think is that:
You use a secure way of communicating session_id etc. This means HTTPS
Sessions should expire when the user closes their browser
User should be notified if he connects from a different location, or should have access to his "login log"
I had found a good solution for this about a year ago. I decided to make it a module, its really easy to use. helps you get the cookie without hard coding it. helps you get that session Id and retrieve it from mysql and redis
https://www.npmjs.com/package/node-laravel-session
Related
I am working on an PHP API application that will work an different domains (and hosted on different servers) that need to share user session. Let's say api.a.com and api.b.com.
Back end side, for me, storing session data using a session id key that can be fetch from multiple domain is the easy part.
My main concern is sending session id to both api.a.com and api.b.com. Native PHP session uses cookies to send session id for each request. Having different top level domain, the cookies will not be sent to all domains (unless I explicitly use Javascript to extract it and send it as a request header).
I thought about storing the session id inside the the Json Web Token data sent to the server, but somehow, it feels wrong.
Any thoughts on this?
Use a memcache server. PHP supports saving sessions in memcache.
With Amazon, I have a load balancer front with Linux instances, each connected to memcache.
Then I have a completely different EB application and domain that accesses the same memcache and pulls real time stats, so it's pretty easy to setup access to the same memcache server or cluster from different applications.
Note there's a difference between "memcache" and "memcached" -- either can be used, but they're different servers!
Use a memcache server. PHP supports saving sessions in memcache or mysql.
then use sessionid by http transport.
`
$sid = $_REQUEST['sid'];// receive sessionid
session_start();
session_id($sid); //reset current sessionid by $sid
var_dump($_SESSION["A_DOMAIN_SESSION"]);
var_dump($_SESSION["B_DOMAIN_SESSION"]);
?>`
I know how to make a secure login system with PHP including basics like hashing, salting and more complex security measures.
Recently I've been using Ratchet with WebSockets a lot. And I've been wondering if it is possible to create a secure register/login system using WebSockets.
User enters his data in form.
Data is validated and sanitized.
Password is hashed.
Everything is being sent to the server via WebSocket.
PHP stores it in MySQL database and salts the password's hash.
PHP returns a callback that registration finished via WebSocket.
User is granted access to the website.
I see it might work. My doubts are here: how secure is ws:// protocol? How to make sure that after logging in the user is the user that was logged? Some sort of sessions? Tokens?
Are there any frameworks/libraries/implementations of such a thing?
Questions are more out of plain curiosity over a new (kinda) piece of tech. If it doesn't work I'll just go for AJAX POST request. :)
No, it is not possible to set up a secure authentication using any of the current WebSocket servers written in PHP.
They all lack TLS support. (Even mine, though I do have an active development branch devoted to providing TLS support.)
Here is what is required for a secure authentication system with WebSockets:
Basic PHP security first:
Always transmit via TLS or some other secure transport system.
Use bcrypt or scrypt (or a future more secure system) to hash the password for storage in the database. Even a stretched SHA256 password with a salt generated from a cryptographically random source is now considered insecure.
Authentication tokens (such as the session token created by PHP and stored in the session cookie) must be cryptographically unpredictable.
Authentication tokens must not be available to non-TLS traffic or client scripts. (Strict enforcement of the Secure flag AND strict enforcement of the HttpOnly flag.) This explicitly means that a user can not be securely authenticated unless they are in https: and wss: mode.
Session data must be stored in a secure directory or data storage on the server.
Reset the session token as part of authentication. Completely delete the data associated with the old session ID.
PHP's built in algorithm for generating session IDs uses /dev/urandom if it is available on your system. Windows users do not get cryptographically random session IDs, though because most WebSockets implementations would require a Posix environment, this isn't an issue. If rolling your own session ID generator, use openssl_random_pseudo_bytes(), and remember that when it comes to security, it's a bad idea to roll your own.
Also, by default, the PHP session cookie does not have the Secure flag or the HttpOnly flag set. Change these in your php.ini, and yes, this means that people can not authenticate anyone who is not using a secure connection -- but this is true anyways, since without TLS, you can not ensure that anyone is the same user from request to request, even if they're using the same session ID and same IP.
And, by default, the PHP session save path is your system's temp directory. This is readable and writable by anyone. Thus, any rogue process, regardless of escalated privileges, can access and alter any session data. Least access means that only the web server user and, in the case of those of us using WebSockets, the WS server's user needs to access the session save path.
WebSockets specific authentication:
The session cookie is available to the WebSocket server during the handshake. This includes when the cookie has the HttpOnly flag set. (Tested on latest publicly available Chrome on Windows.) (Secure flag not testable yet. Give me a couple months as I get TLS working on my server. If secure cookies are not available, or are available in a non-secure context, I'll be personally raising a holy stink with the browser vendors on your behalf.)
This means that someone who has an authenticated session can also be pre-authenticated on their WebSocket connection without having to pass data through the web document body (no need to embed the session ID in the HTML or in data in a script tag, which would then be re-transmitted through the open WS connection).
As for securely authenticating someone using WebSockets... There is one huge caveat.
Because WebSocket servers have multiple users connected at the same time, the traditional sense of PHP sessions is broken. Which session do we mean?
Thus, the $_SESSION superglobal MUST be strictly off limits, and the session_...() functions should be considered highly dangerous. The only safe and meaningful session functions are the read-only version of session_save_path(), which tells you where to find the session files, session_encode(), and session_decode().
To get the session data, you will need to read the session file, running its contents through session_decode(), and associating the results with the specific connection for that specific user. You will also need to do this periodically, as the session data changes without warning. To persist any data to the session, session_encode() is your friend, of course. Since PHP doesn't care about race conditions on session data, you shouldn't either. Just read the file, change the data, and write to the file, and it'll be all good.
And this brings us to the first real problem: In order to have secure authentication, you need to reset the session token when the user is authenticated. Since client side Javascript can't write to an HttpOnly cookie, you can't change the session ID on the client side.
The status of whether the user is authenticated can easily be updated within the existing session data, of course. That's fairly trivial. However, because we need a new session ID before we persist the new authentication state, and that new session ID must be valid for all of the client's connections immediately, we can't completely perform secure authentication through a WebSocket connection.
A possible work-around to use as much WebSockets as possible (once a PHP WS server supports TLS, that is):
Client sends login information to the server through the WebSocket connection.
Server responds back with a cryptographically random token.
Client responds back with a salt through the WS connection, and with a hashed and salted version of the token through an AJAX request.
Server verifies the salt matches, and using the AJAX request's script, calls session_regenerate_id(true), and returns any appropriate response (just so long as the set-cookie: header is set).
Server updates its internal mapping to the user's new session ID.
Server discards the one-time token completely, and naturally logs any attempted use of any invalid one-time tokens.
(It might be worth it to implement a true Diffie-Hellman exchange to set up a true secure shared secret. I am not a security expert; the above algorithm may not be as secure as I think it is, though because it would already be in two TLS tunnels, it should be secure. (The https: tunnel is separate and distinct from the wss: tunnel, thus there is a need to confirm information sent through one tunnel matches information sent through the other, but should not require secrecy in addition to that already afforded by TLS.))
Bottom line:
Yes, security is hard to get right. But it's worth it.
I am running two linux instances behind an ELB. My app has facebook login as a way of signing up. On the live environment, the user signs up from one instance, session is created for that but the redirection happens on the second instance a lot of times resulting in null session from facebook as that session is there on the first instance.
Help me fix this. This is very crucial to our product.
Arguably, the most correct answer is that you should not design web applications without a proper session database that is accessible to all of the web application servers.
However, ELB provides a workaround for this oversight, known as "sticky sessions" (or "session affinity").
By default, a load balancer routes each request independently to the application instance with the smallest load. However, you can use the sticky session feature (also known as session affinity) which enables the load balancer to bind a user's session to a specific application instance.
http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/US_StickySessions.html
Of course, if an application server fails, or is taken offline for maintenance, your user still has an invalid session... but for the most part, this feature should accomplish what you want.
I'm writing a JQM web app with a PHP web service. Users will be able to sign-in and register that they've made a purchase of, for instance, a soda from the club. This info will be stored in a database and eventually billed.
To illustrate what I want to do: I have already implemented this as an Android app. My "session handling" in the Android app consists of simply storing the user's credentials in Android's savedPeferences (persistent local storage) upon succesful authentication with the server. These credentials are then resent with every subsequent server request so that users only ever have to sign-in once - upon running the app for the first time.
I want to mimic this behavior in my JQM app as closely as possible. Ideally, the user should only ever have to sign in once unless they choose log out.
I'm a bit rusty when it comes to website programming, so what would be the best approach? Non-expiring cookings? Do I use a PHP session or handle everything in javascript?
This is for a hobby project; I prefer a simple solution over something overly secure and complex. Thanks!
Edit: After reading Mike's answer I stumbled across this plugin: https://github.com/carhartl/jquery-cookie
Perhaps this is the easiest way to keep users logged in..?
PHP sessions are going to be invalidated after a set amount of time (depending on your php.ini settings or any runtime modificatoins to the settings).
You can use long-time expiring cookies to persist a login (typically user is given checkbox at login to allow their login credential to be stored).
Since you are developing for a mobile device, you do also have the alternative of using HTML5 local storage since most every Android browser out there supports it. See more info at the link below.
HTML5 Local storage info
I prefer this as the login hash could be persisted even if the user clears their browser cookies and it can be handled strictly within Javascript.
So, I'm looking at architecting an application using nginx with the nginx-http-push-module and PHP-FPM, and after lots of fun configuring, I got it working to the point of handling PHP pages as it should.
What I don't get, though, is how sessions are supposed to work - all of the examples I've seen for nginx+NHPM run through the publisher-subscriber system, but it's never clear what should happen if the subscriber channel is going to be, effectively, unique to a subscriber. Think of a chat system with a public channel and a private channel for each user, for example.
Now, in a conventional PHP setup, you'd be passing the cookies to PHP, looking up the session from there, and handling the rest of the page based on whether the user was authenticated or not, but with PHP-FPM and long-polling, it doesn't seem like it should work like that.
I can understand if the request is a non authenticated user, you just dump them with an error message and terminate the long-poll from the client knowing that it's not valid, but with a valid request, you almost need to poll from the client, authenticate in PHP, then disconnect but leaving the request open - and I'm not sure how that part works.
Can anyone explain how it should be achieved, ideally with an example if possible? Please note I'm not looking for HTTP Basic authentication here, I need the authentication to be looked up against a separate data storage which is in MongoDB.
Disclaimer: I can't clearly understand your 4. paragraph.
As far as I can tell, the main problem with authentication in NHPM is that the PHP application gets absolutely zero notification of incoming connections. The Comet part of your setup is write-only for PHP.
A possible solution follows, I'll be trying this out in the next days.
nginx configuration:
push_subscriber_concurrency first: so that the channel can only be used by the intended user
push_authorized_channels_only on: not strictly necessary, but good to have in my opinion
Authorization workflow:
Client sends credentials via old-fashioned requests
Server authenticates, and generates a token (channel id). Creates the channel and responds with the token.
Client tries to open long-poll to the given channel.
If it fails (possibly because the channel was hijacked), it tells the server that channel so-and-so is invalid. Mind that we use old-fashioned requests here, so you can use any auth method. Server deletes the channel. Back to step two.
If the connection is successful (you probably won't know this, only that it hasn't failed), the channel can be considered authenticated.
Note that if your application should be accessible from multiple pages in the same browser with the same login, then you'll need to prepare for multiple channels per user.