Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveWebService WebSocket Implementation #92

Open
darth-cheney opened this issue Feb 22, 2020 · 7 comments
Open

ActiveWebService WebSocket Implementation #92

darth-cheney opened this issue Feb 22, 2020 · 7 comments

Comments

@darth-cheney
Copy link
Contributor

darth-cheney commented Feb 22, 2020

There is something fishy going on with the Websocket implementation in ActiveWebService. Though we can connect safely from Chrome and I think Firefox, you cannot connect from other programs that implement websockets. Given that it is a pretty widely understood and implemented standard, this is a sign to me that something fishy is going on.

Example

To see this in action, first create a nodeenv in some directory and source it (nodeenv --prebuilt --node=12.16.0 nodeenv && source nodeenv/bin/activate).

Now try to run the following script:

const WebSocket = require('ws');

const ws = new WebSocket('ws://127.0.0.1:8000/');
ws.on('open', event => {
    console.log(`Opened connection`);
});

ws.on('message', msg => {
    console.log('Message:');
    console.log(msg);
});

ws.on('close', (r, s) => {
    console.log('Socket closed');
    console.log(r);
    console.log(s);
});

ws.on('error', err => {
    console.error(err);
});

You will see that the Socket closes immediately, with the close code of 1005. That code means "No status code was provided even though one was expected" which is pretty cryptic (source).

@braxtonmckee
Copy link
Contributor

braxtonmckee commented Feb 22, 2020 via email

@darth-cheney
Copy link
Contributor Author

I think I know what the problem is. The overall authentication pattern requires a valid cookie inside of the event loop that is handling an open socket (see here for example).

For me, testing some debugging with external tools, I am required to "steal" a cookie from an open browser session, and to pass it in the socket open headers.

Since cookies have a TTL, it's entirely possible that your random disconnections have to do with just having the system open on your machine and running for a while. I'm not sure yet what the default TTL is, but I assume if you keep the system with a single socket connection open long enough, the cookie will expire and the socket will disconnect (without even sending over an error).

I am going to look into refactoring the websocket / auth setup in ActiveWebService. But first I might want to have a better idea of what auth you are using the production application -- do you really need cookie based authentication, and do you really need it for the socket? How could I "login" to the system using AJAX requests (in order to get a valid cookie), if at all?

@braxtonmckee
Copy link
Contributor

braxtonmckee commented Feb 22, 2020 via email

@darth-cheney
Copy link
Contributor Author

@braxtonmckee I have a couple of questions about this.

First, is there supposed to be an actual login page in the webtest frontend? What about in other frontends?

Second, where does User information really come from if one of the above is true? Are there stored password hashes somewhere in the objectdb or similar?

It would be good to have a quick rundown of the actual security requirements. It affects whether we'd stick with cookie-based stuff (which I generally try to avoid), or use some kind of JWT or stored token on the client side.

@braxtonmckee
Copy link
Contributor

Eric - we have a plugin framework for login, and our proprietary system uses an internal oauth flow. I'm not sure how that will work with the JWT. I think as long as we have a constant that tracks with the session we will be fine - we already deal with auth separately anyways, so we just need to make sure we are authenticated and then we can trust the cookie. I can imagine use cases where that's not true, but for the moment that would work for me.

@atzannes maybe you want to chime in here. I don't remember exactly how the plugin model works. Maybe there's a nice way for the auth plugin to tell the main framework what user is authenticated so it can verify that the session is valid for that user...

@darth-cheney
Copy link
Contributor Author

I'll add one thing to this convo real quick: there is no real reason, in my view, to check for authentication inside the WebSocket event loop every time it loops. A websocket connection is started with an initial HTTP request anyway, which should be authenticating based on the cookie. We already know that the socket connection is authorized in that case, and can just keep it alive without the extra work of checking each time.

In practical terms, I mean that we might want to delete this part (4 lines):
https://github.com/APrioriInvestments/object_database/blob/dev/object_database/web/ActiveWebService.py#L329

@darth-cheney
Copy link
Contributor Author

@braxtonmckee @atzannes I've been experimenting with some options here, but I still have some outstanding questions, mostly from the perspective of the prod environment:

  1. Are you using the "plugin" model from the public framework (ie the structure and classes defined in "LoginPlugin") in the production environment?
  2. (This pertains to 1 above): You say you are using oauth in production, correct? Does that flow make use of this plugin system at all?
  3. Is there anywhere at all in a real live system where Users are being taken from an object db and authenticated for use of the frontend using passwords? If so, how are these passwords stored?
  4. It seems like the current plugin structure is tracking IPs. Do we really need to include this check in basic authentication schemes?
  5. Are there any other security requirements that I should know about?

It would be good if we could simplify the LoginPlugin framework (or at least document it) if it's in active use. If not, we might consider reducing it to only the essentials, then creating a basic decorator for use in the Flask application.

We are also using a Flask plugin called flask_login that wraps a lot of functionality, and I believe that is where the cookie is being set etc. We might consider getting rid of this altogether, and again implementing our own route decorators for Flask.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants