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

How to securely integrate noVNC inside an authetication system ? #522

Closed
comsyspro opened this issue Aug 21, 2015 · 21 comments
Closed

How to securely integrate noVNC inside an authetication system ? #522

comsyspro opened this issue Aug 21, 2015 · 21 comments
Labels

Comments

@comsyspro
Copy link

Hi,

1.)
I have an authentication site (let's say "https://www.abcd.com/login.php") where a user puts in his login credentials and logs in to his account website. Now I want to put there in his account a link or an iframe which lets him auto connect to his VNC server. At the moment the only way I know is to point to the URL "https://www.abcd.com:6080/vnc_auto.html?password=1234". But here I would like to have a solution where the URL should be only reacheable inside / embedded in the authentication system that means that the URL should be something like "https://localhost/vnc_auto.html?password=1234" so that the URL is only accessible for the user which logged in his account site and not for others users from outside which could guess an URL and port with a password. Can you please give some advice how to do that? Is it also sensful to put the "?password=1234" in the URL in terms of security or is there another possibility to set the password for an auto connection?

2.)
Besides my apache server is configured for ssl with listen port 443. Is it also possible to "mix" or "redirect" the websockify webserver with the apache server on the same port 443?

3.)
I have problems using the option "--target-config".

Here is the command:
root@testserver:~/noVNC# ./utils/websockify --verbose --cert ./self.pem --web ./ 6080 --target-config ./token.list

The file "token.list" contains the line:
abcd: 192.168.124.123:5900

The URL is:
http://testserver:6080/vnc_auto.html?token=abcd

And here is the error message:
Traceback (most recent call last):
File "/root/noVNC/utils/websocket.py", line 874, in top_new_client
client = self.do_handshake(startsock, address)
File "/root/noVNC/utils/websocket.py", line 809, in do_handshake
self.RequestHandlerClass(retsock, address, self)
File "/root/noVNC/utils/websocket.py", line 112, in init
SimpleHTTPRequestHandler.init(self, req, addr, server)
File "/usr/lib/python2.7/SocketServer.py", line 655, in init
self.handle()
File "/root/noVNC/utils/websocket.py", line 540, in handle
SimpleHTTPRequestHandler.handle(self)
File "/usr/lib/python2.7/BaseHTTPServer.py", line 340, in handle
self.handle_one_request()
File "/usr/lib/python2.7/BaseHTTPServer.py", line 328, in handle_one_request
method()
File "/root/noVNC/utils/websocket.py", line 506, in do_GET
if not self.handle_websocket():
File "/root/noVNC/utils/websocket.py", line 494, in handle_websocket
self.new_websocket_client()
File "./utils/websockify", line 48, in new_websocket_client
(self.server.target_host, self.server.target_port) = self.get_target(self.server.target_cfg, self.path)
File "./utils/websockify", line 94, in get_target
raise self.EClose("Token not present")
AttributeError: ProxyRequestHandler instance has no attribute 'EClose'
Reaing zombies, active child count is 6
Ignoring interrupted syscall

What happens here?

Thanks.

I'm using this release:
https://github.com/kanaka/noVNC/releases/tag/v0.5.1

@comsyspro comsyspro changed the title How to securly integrate noVNC inside an authetication system ? How to securely integrate noVNC inside an authetication system ? Aug 21, 2015
@DirectXMan12
Copy link
Member

Can you please give some advice how to do that?

You can use websockify's authentication and token plugin mechanisms to make this work. You can look at the included plugins (see https://github.com/kanaka/websockify/blob/master/websockify/auth_plugins.py and https://github.com/kanaka/websockify/blob/master/websockify/token_plugins.py for examples). You'll have to launch websockify with the appropriate parameters (e.g. websockify/run --token-plugin mymodule.MyTokenPlugin --token-source some_arg ...). Authentication plugins have access to the host:port pair calculated using the token plugin (if present) as well as the headers for the request.

Is it also sensful to put the "?password=1234" in the URL in terms of security or is there another possibility to set the password for an auto connection?

Hmm... at the moment, you'd have to modify noVNC to send that information in a header, and then use an auth plugin. I'm going to play around a bit and see if I can get HTTP basic auth working (but it will required a bit of changing websockfiy).

Besides my apache server is configured for ssl with listen port 443. Is it also possible to "mix" or "redirect" the websockify webserver with the apache server on the same port 443?

You should be able to use mod_proxy_wstunnel to accomplish something like this.

I have problems using the option "--target-config".

Can you try either with a recently release of websockify (launch websockify manually like websockify/run --web /path/to/noVNC --target-config /path/to/config localhost:6080) or with a checkout of noVNC from master (which will automatically grab the latest websockify from master unless you have websockify installed)?

noVNC v0.5.1 bundles an older version of websockify that may have issues. There should be a new release of noVNC coming out somewhat soon.

@comsyspro
Copy link
Author

Ok, first thank you a lot.

In the meanwhile I found out that the option "--target-config" works. Here the URL must be added the "?path=" in the URL like https://testserver:6080/vnc_auto.html?path=?token=abcd. So the URL is parsed fine. Can somebody explain what function has the path argument (normally it is defined as path=websockify) and should it be set or not or doesn't it matter? So the 0.51 version works fine.

Also with the newest branch I got hangups of the VNC canvas, that means after some seconds the connection breakes down. Because of that I went back to a previous version where this problem doesn't appear.

I will have a deeper look to the token plugin now. When I try the command "./run --token-plugin /websockify/token_plugins.py 6080 192.111.111.111:5900" I get the following error:

Traceback (most recent call last):
File "./run", line 5, in
websockify.websocketproxy.websockify_init()
File "/root/noVNC_master/noVNC/utils/websockify/websockify/websocketproxy.py", line 467, in websockify_init
import(token_plugin_module)
ImportError: Import by filename is not supported.

Where do I get the token plugin an also the auth plugin? Is there somewhere an example how to do that? At the moment I can't figure out completly the differences between simpler websockify commands like "./utils/websockify --verbose --cert ./self.pem --web ./ 6080 --target-config ./token.list" and more advanced commands like " ./run --token-plugin mymodule.MyTokenPlugin --token-source some_arg ... ). Here a tutorial or a manual would help.

At the moment I create token files with a long token (bigger than 32 digits length) and start the websockyfy with "--target-config". The calling URL to the websockify webserver is done over an https:// connection. Do you think this is yet a secure solution? Because I thought that the token is like a username and with the use of a password there should be a strong security protection, am I right?

What I don't exactly know is when you sent the URL let's say "https://testserver:6080/vnc_auto.html?path=?token=abcd" can than somebody grab (man-in-the middle) the token or is the URL also encrypted when it is send and calling the URL? Can someone tell if this is secure? This question also concerns the password "&password=" argument. I'm not sure if this is also encrypted when you call that URL (it seems to me to be like a php get-submit function and this could be unencrpted and visible to sniffers).

Because of the password security I could also hardcode the password in the .js script and instead use a long token so that the security depands only on the length of the token (like a very long keyfile >100 digits). Is this a right way or must I use the token and auth plugin (which I don't understand at the moment how to use and configure it ). Also I'm aware that the URL is saved in the browser history and I heard that a sniffer can read out the browser history with javascript. So how to solve that problem (perhaps token and auth plugin prevent that but how to get it configured)? In my scenario I'm using an autentication system and inside that there is an embedded iframe with the URL defined in the "--target-config" and a very long token. Is that a secure solution yet?

@DirectXMan12
Copy link
Member

Can somebody explain what function has the path argument (normally it is defined as path=websockify) and should it be set or not or doesn't it matter?

The path argument controls at which path noVNC tries to establish the WebSocket. If you're running websockify listens on any path. Token usage requires telling noVNC to forward send the token as a URL parameter when making the WebSocket connection.
Normally, the path does not matter, since websockify listens for upgrade attempts on all paths. It becomes an issue if you're doing something like using an Apache ProxyPass to send requests only on certain URLs to websockify.

Also with the newest branch I got hangups of the VNC canvas, that means after some seconds the connection breakes down. Because of that I went back to a previous version where this problem doesn't appear.

Can you please provide which browser version this occurs on? I recently merged a major PR that changed quite a few things, so I'm trying to make sure that there weren't any major repercussions due to browser quirks.

I will have a deeper look to the token plugin now. When I try the command "./run --token-plugin /websockify/token_plugins.py 6080 192.111.111.111:5900" I get the following error: ...
Where do I get the token plugin an also the auth plugin? Is there somewhere an example how to do that? At the moment I can't figure out completly the differences between simpler websockify commands like "./utils/websockify --verbose --cert ./self.pem --web ./ 6080 --target-config ./token.list" and more advanced commands like " ./run --token-plugin mymodule.MyTokenPlugin --token-source some_arg ... ). Here a tutorial or a manual would help.

Sorry, this is a bit of a new feature. You have to pass a class name, not a file path -- if you're using one of the built-in plugins, you can just pass it's class name, like ./utils/websockify --token-plugin ReadOnlyTokenFile --token-source /path/to/your/token.list .... If you're using a plugin that you wrote yourself, pass the full import path: ./utils/websockify --token-plugin my_package.my_module.MyTokenPlugin --token-source some_argument_here ...

At the moment I create token files with a long token (bigger than 32 digits length) and start the websockyfy with "--target-config". The calling URL to the websockify webserver is done over an https:// connection. Do you think this is yet a secure solution? Because I thought that the token is like a username and with the use of a password there should be a strong security protection, am I right?

The built-in token support shouldn't be considered a username/password scheme -- they're easy to fish out of a URL, and are persistent. You could write a plugin that fetches tokens from a database of some sort (a SQL database, memcached, redis, etc), and makes them single-use -- this would be a bit better.

This question also concerns the password "&password=" argument.

The basic VNC auth isn't very secure (among other things, it's limited to a very short number of characters) -- you shouldn't count on it for security. That being said, you don't have to put password in the query string -- noVNC will prompt for a password if you don't specify one.

DirectXMan12 added a commit to novnc/websockify that referenced this issue Aug 25, 2015
This commit reworks auth plugins slightly to enable
support for HTTP authentication.  By raising an
AuthenticationError, auth plugins can now return
HTTP responses to the upgrade request (such as 401).

Related to novnc/noVNC#522
@DirectXMan12
Copy link
Member

The PR referenced above should make it slightly easier to use HTTP authentication. Included is an example of HTTP basic auth, although it will require either modifying noVNC so that it internally adds a username and password into the connection URL (which the browser will then convert into HTTP auth) or putting the username and password in the host query parameter (which would not be secure).

DirectXMan12 added a commit to novnc/websockify that referenced this issue Aug 25, 2015
This commit reworks auth plugins slightly to enable
support for HTTP authentication.  By raising an
AuthenticationError, auth plugins can now return
HTTP responses to the upgrade request (such as 401).

Related to novnc/noVNC#522
@comsyspro
Copy link
Author

I checked it once with newest version git clone https://github.com/kanaka/noVNC.git and with all tested browsers (IE, Firefox, Chrome) the canvas is shown about 1 to 3 seconds and then stops and freezes. Then you have to connect new. There is no error message on the websockify server. My last working version was about 2 weeks back then when this problem occurs I switched back to v0.51.

Also I've tried now ./utils/websockify --token-plugin ReadOnlyTokenFile --token-source /path/to/your/token.list and it works. Can you please explain what you can achieve with the auth_plugin how to use it and what it is for? Because normally I connect over the URL including a token and authenticate with the VNC password.

Also to your last contribution Rework Auth Plugins to Support HTTP Auth #194 can you please explain how to use it, what should be changed in the noVNC to use it.

Lastly please give me your opinion to the following scenario concerning security aspects:
in this example https:// is used everywhere
I have an authentication system where a user logs in with his username and password. When he has successfully logged in then he finds inside a html-file called connect-to-vnc.html. This html-file contains an iframe something like <iframe src="https://ipvncserver:6080/vnc_auto.html?path=?token=string_with_100digits_lenghth_or_more"></iframe>. So when he calls the html file connect-to-vnc.html the iframe let him see the vnc_auto.html site where he can insert his VNC password to connect to the VNC server.
In this case the token with a lenghth over 100 digits is like a static key which let only connect the user to the VNC server. When the user logs out from the authenticate system nobody has access to the connect-to-vnc.html. So the user should hold his html-file as secret like a key file. On the websockifiy Server in a token-directory there is a tokenfile user.txt which contains the very long token string for this special user in the form token:ipvncserver:portvncserver. To revoke this user you only have to delete this file user.txt from the token-directory or change it for a new user. Normally a user has accees to his vnc server for a period from 1 to 14 days (it should be impossible to guess this big string >100 digits in 14 days). In your opinion is this a secure way or are there any weaknesses or possible optimizations?

@DirectXMan12
Copy link
Member

with all tested browsers (IE, Firefox, Chrome)

Can you indicate which versions of those browsers? That's important. Also, can you set the log level to debug and show the contents of the browser javascript console (in the developer tools for Chrome, Firefox, or newer versions of IE).

Finally, if you feel comfortable (if I can't identify anything immediately wrong from the logs or browser version), a recording of the broken session would be useful so I can debug from my end (you can do this by running websockify with --record=somefile.js, and then uploading the resulting file somewhere).

Also I've tried now ./utils/websockify --token-plugin ReadOnlyTokenFile --token-source /path/to/your/token.list and it works. Can you please explain what you can achieve with the auth_plugin how to use it and what it is for? Because normally I connect over the URL including a token and authenticate with the VNC password.

The VNC password auth isn't very secure. Additionally, people might want to do authentication of the connection based on things like the HTTP headers, instead of through the VNC protocol. The auth plugins allow you to reject connections based on the header values.

Also to your last contribution Rework Auth Plugins to Support HTTP Auth #194 can you please explain how to use it, what should be changed in the noVNC to use it.

The PR that I created above reworks the way the auth plugins are used internally to noVNC. The upshot of this is that the auth plugins get called before the WebSocket handshake happens, so that they can return different error codes to instruct the browser to attempt to use various forms of HTTP auth, such as HTTP basic auth, SPNEGO, etc. Additionally, you could use something like a cookie to ensure that a user was logged in. You can look at the example plugins to see the structure (they're in auth_plugins.py), but they're basically just classes which have an authenticate(self, headers, target_host, target_port) method which raises an AuthenticationError if the authentication is rejected.

@comsyspro
Copy link
Author

Hi,

here are the versions:
Google Chrome 44.0.2403.157 m
Firefox 40.0.2
IE 11.0.9600.17914

I've created a recording. I would give you an account where you can download it. Please send me your email. In the browser I can't find the JavaScript console where is it?

@DirectXMan12
Copy link
Member

I've created a recording. I would give you an account where you can download it. Please send me your email.

Great. Those browsers should work fine, so this is definitely an issue. The email on my GitHub account page is fine (http://github.com/directxman12) to send to. Thanks!

@DirectXMan12
Copy link
Member

In the browser I can't find the JavaScript console where is it?

In Firefox and Chrome, press F12 and select "Console", (or Firefox Menu->Developer->Web Console, Chrome Menu->Developer Tools, select "Console")

Also, which VNC server are you using?

@kanaka
Copy link
Member

kanaka commented Aug 26, 2015

@comsyspro thanks for helping out with the debug of this.

Regarding your security questions, my suggestion would be to use a short lived token that only lasts for a minute or two, and just redirect the browser to a path that includes the token in the URL. This is similar to how openstack (and other systems) integrate noVNC/websockify. Also, I wouldn't necessarily use an iframe. The iframe can cause user interaction issues with noVNC keyboard/mouse and I don't think it adds much to the security if you have short lived tokens.

A simple way of easily accomplishing the above without writing a new plugin would be to use the TokenFile plugin. One thing that may not be immediately obvious from the *TokenFile plugins is that rather than storing all your token mappings in a single file, you can store them one token per file in a configuration directory. And if you use TokenFile (rather than ReadOnlyTokenFile) then every time a connection is made, all the config files are re-read. This means that you can easily add and remove tokens just by creating or deleting a config file. You can then have a timer that automatically removes the token after 60 seconds (established connections are unaffected), or a process that sweeps token files older than 60 seconds, or both. That allows you to keep you web framework for your main app and auth separate from the websockify process and use the file-system to communicate the auth information. And it should be reasonably efficient as long as you don't expect to have thousands of simultaneous users. Of course, another option would be to create auth tokens on demand in a DB (rather than a file-systems) and write an auth token that reads from the DB (e.g. and your select could ignore anything older than 60 seconds).

As long as your tokens are secure (long and using a good random generator), then that part of your system should be reasonably secure. Of course, if your web framework, it's authentication and the systems it is running on are weak, then all bets are off of course. Also, websockify itself has not been security audited that I'm aware of, so the general model should be okay in theory, but the implementation might have issues. Scratch that ... it definitely has holes. Whether it is worse than your average piece of software is the question.

DirectXMan12 added a commit that referenced this issue Aug 26, 2015
For performance reasons, the `handle_tight` function skips the
use of the receive queue API and uses the raw receive queue directly.
Because of the way that typed array receive queue gets reused, this
introduced the potential for buffer over-reads.

To address this, a new function, `rQwhole`, was introduced.
`rQwhole` simply returns a new view into the receive queue that
starts at 0 and ends at the current recorded end of the queue.

`handle_tight` now makes use of this function.

Fixes #522
@DirectXMan12
Copy link
Member

@comsyspro I think I found the bug in noVNC. To make a long story short, it looks like it was a buffer over-read issue. Can you try the PR above (#524) and see if it fixes your issue? I probably wouldn't have noticed that without your recording. Thanks a bunch!

@comsyspro
Copy link
Author

@DirectXMan12
As VNC Server I use TightVNC 2.7.10 64bit on Windows 7 Pro 64bit

In the output of the console there are no error messages visible but you can see that the canvas stopped here very fast on entering (only few buckets) and sometimes it stops after some seconds.
screenshot_01
tested with newest master on Ubuntu 15.04 / Python 2.7.9

@kanaka
Thanks for the detailed description and ideas. So now I know my system is yet quite secure but I will try to make it even more secure. I could try to generate a token with php for a certain user which than is stored in the specific token-directory, than calling the URL and after that deleting the token form the token-directory. For me it's easier to do it like that rather than developing a plugin. But perhaps you could also add this functionality to noVNC.

@DirectXMan12
Copy link
Member

@comsyspro is that with or without the patch above?

@comsyspro
Copy link
Author

The above comment was the error case without any changes.

@DirectXMan12
I changed the two files and now it works. But I've the impression that the old version performs better more fluid when using the tight encoding (disabling copyrect). When I watch a YouTube video over the noVNC canvas than I get very short stops (juddering) whereas in the version 0.51 I get a very fluid view of the video.

@DirectXMan12
Copy link
Member

Of course, another option would be to create auth tokens on demand in a DB (rather than a file-systems) and write an auth token that reads from the DB (e.g. and your select could ignore anything older than 60 seconds).

If you have a redis setup, you could set the TTL when you create the token to 60s, and then the plugin should work something like this (something similar would work with memcached):

class SingleUseToken(websockify.token_plugins.BasePlugin):
    def __init__(self, src=None):
        import redis
        self.client = redis.Redis(host=src)

    def lookup(self, token):
        p = self.client.pipeline()
        pair, _ = p.get(token).delete(token).execute()
        return pair.split(':')

@DirectXMan12
Copy link
Member

But I've the impression that the old version performs better more fluid when using the tight encoding (disabling copyrect)

😮 I'll have to look into that. Can you possibly send me a recording of that as well (I tried with my own VNC server, and I'm not seeing what you're seeing, I suspect due to TigerVNC having some newer features)?. Testing performance against lots of different VNC servers is quite useful.

@comsyspro
Copy link
Author

I did a second test with both versions v0.51 and master and now I see no differences in streaming quality - seems to be equal now.

@kanaka
Copy link
Member

kanaka commented Aug 26, 2015

But perhaps you could also add this functionality to noVNC.

Authentication is quite specific to the application/system into which websockify/noVNC is being integrated. We've tried to make websockify fairly extensible and pluggable to make this easy, but also generic. There really isn't one right way to do it for all situations.

@DirectXMan12
Copy link
Member

Feel free to reopen this if you have further questions on how to use the plugin system. In the mean time, I'm going to close this thread.

@onny
Copy link

onny commented Mar 11, 2017

Does someone already have a basic auth example for noVNC and websockify?

@DirectXMan12
Copy link
Member

auth plugins work very similarly to token plugins (https://github.com/novnc/websockify/wiki/Token-based-target-selection), so you'll need to use the basic auth plugin in websockify. I don't believe we have a concrete example, but I recall being able to throw one together. There's a trick to get basic auth properly forwarded on the websocket upgrade connection, but I don't recall what I did when testing things out.

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

No branches or pull requests

5 participants