Xpra: Ticket #2793: dangerous HTML5 client example

On the HTML5 client page (https://xpra.org/trac/wiki/Clients/HTML5) there's an example of starting an xpra server with AES and password access. A URL is generated for a Web browser to access the server. One might conclude from this example that the password and AES key are required by the server. They're not. They're completely ignored on the browser's connection. The server has just provided world-accessible (0.0.0.0), unencrypted, and unauthenticated access to an xterm.

This is clearly demonstrated using the command line xpra client with the server as started from the example. Note that I do not provide the AES key, user, or password on either attempt to connect.

$ xpra attach tcp://localhost:10000 || echo -e "\n\nTCP connection failed.\nTry again with Websocket."; xpra attach ws://localhost:10000
Warning: vendor 'Intel Open Source Technology Center' is greylisted,
 you may want to turn off OpenGL if you encounter bugs
2020-06-01 10:14:26,820 Xpra GTK3 X11 client version 4.0.1-r26379 64-bit
2020-06-01 10:14:26,978  running on Linux Ubuntu 18.04 bionic
2020-06-01 10:14:26,979  window manager is 'GNOME Shell'
2020-06-01 10:14:27,001 Warning: failed to import opencv:
2020-06-01 10:14:27,001  No module named 'cv2'
2020-06-01 10:14:27,002  webcam forwarding is disabled
2020-06-01 10:14:27,503 GStreamer version 1.14.5 for Python 3.6.9 64-bit
2020-06-01 10:14:27,710 No OpenGL_accelerate module loaded: No module named 'OpenGL_accelerate'
2020-06-01 10:14:27,923 Warning: vendor 'Intel Open Source Technology Center' is greylisted,
2020-06-01 10:14:27,923  you may want to turn off OpenGL if you encounter bugs
2020-06-01 10:14:27,981 OpenGL enabled with Mesa DRI Intel(R) HD Graphics 5500 (Broadwell GT2)
2020-06-01 10:14:28,101  keyboard settings: rules=evdev, model=pc105, layout=us,us
2020-06-01 10:14:28,103  desktop size is 2880x1620 with 1 screen:
2020-06-01 10:14:28,103   :0.0 (762x428 mm - DPI: 96x96) workarea: 2880x1565 at 0x55
2020-06-01 10:14:28,103     eDP-1 (340x190 mm - DPI: 215x216)
2020-06-01 10:14:31,179 Warning: server connection failure:
2020-06-01 10:14:31,179  disconnected before the session could be established
2020-06-01 10:14:31,180  missing encryption tokens
TCP connection failed.
Try again with Websocket.
Warning: vendor 'Intel Open Source Technology Center' is greylisted,
 you may want to turn off OpenGL if you encounter bugs
2020-06-01 10:14:32,932 Xpra GTK3 X11 client version 4.0.1-r26379 64-bit
2020-06-01 10:14:33,067  running on Linux Ubuntu 18.04 bionic
2020-06-01 10:14:33,069  window manager is 'GNOME Shell'
2020-06-01 10:14:33,095 Warning: failed to import opencv:
2020-06-01 10:14:33,095  No module named 'cv2'
2020-06-01 10:14:33,095  webcam forwarding is disabled
2020-06-01 10:14:33,479 GStreamer version 1.14.5 for Python 3.6.9 64-bit
2020-06-01 10:14:33,650 No OpenGL_accelerate module loaded: No module named 'OpenGL_accelerate'
2020-06-01 10:14:33,838 Warning: vendor 'Intel Open Source Technology Center' is greylisted,
2020-06-01 10:14:33,838  you may want to turn off OpenGL if you encounter bugs
2020-06-01 10:14:33,892 OpenGL enabled with Mesa DRI Intel(R) HD Graphics 5500 (Broadwell GT2)
2020-06-01 10:14:33,995  keyboard settings: rules=evdev, model=pc105, layout=us,us
2020-06-01 10:14:33,997  desktop size is 2880x1620 with 1 screen:
2020-06-01 10:14:33,998   :0.0 (762x428 mm - DPI: 96x96) workarea: 2880x1565 at 0x55
2020-06-01 10:14:33,998     eDP-1 (340x190 mm - DPI: 215x216)
2020-06-01 10:14:34,326 enabled fast mmap transfers using 256MB shared memory area
2020-06-01 10:14:34,327 enabled remote logging
2020-06-01 10:14:34,328 Xpra GTK3 X11 server version 4.0.1-r26379 64-bit
2020-06-01 10:14:34,328  running on Linux Ubuntu 18.04 bionic
2020-06-01 10:14:34,340 Attached to ws://localhost:10000/
2020-06-01 10:14:34,341  (press Control-C to detach)
2020-06-01 10:14:34,451 server does not support xi input devices
2020-06-01 10:14:34,452  server uses: xtest
2020-06-01 10:14:34,850 sound output found 2 audio output devices:
2020-06-01 10:14:34,851 sound output  * Built-in Audio Analog Stereo
2020-06-01 10:14:34,851 sound output    alsa_output.pci-0000_00_1b.0.analog-stereo
2020-06-01 10:14:34,851 sound output  * Built-in Audio Analog Stereo
2020-06-01 10:14:34,851 sound output    bell.ogg
2020-06-01 10:14:34,851 sound output  to select a specific one,
2020-06-01 10:14:34,851 sound output  use the environment variable 'XPRA_PULSE_SINK_DEVICE_NAME'
2020-06-01 10:14:34,851 sound output using default pulseaudio device
2020-06-01 10:14:35,371 sound output using 'opus' audio codec


Mon, 01 Jun 2020 15:54:48 GMT - Antoine Martin: status changed

Thanks, the wiki page was out of date - this syntax used to work as a fallback but it no longer does. It has now been updated: wiki/Clients/HTML5 - this fixes the tcp-auth setting. FYI, in v4, the newer syntax (#1160) aims to prevent this sort of confusion, use:

--bind-tcp=0.0.0.0:10000,auth=file:filename=password.txt

As for AES, I will look into it.


Mon, 01 Jun 2020 17:44:42 GMT - Kyler Laird:

Thank you for the quick update! #1160 syntax looks *much* better.

I tried the option --bind-tcp=0.0.0.0:10000,encryption=AES,encryption-keyfile=./key.txt which I found in #1160 but it seems to be completely ignored.


Tue, 02 Jun 2020 04:15:32 GMT - Antoine Martin:

I tried the option .. but it seems to be completely ignored.

The html5 client is ignoring the encryption options because some bugs were introduced in a recent version (r26569 and others) - that's what I need to fix for this ticket.

Connecting with a python client should work, ie:

xpra attach tcp://192.168.0.10:10000 --encryption=AES --encryption-keyfile=./aes.txt

(the client syntax for encryption will be improved soon too: #2794)

The server should reject your html5 client connection - it does for me - with this message:

Warning: authentication failed
 missing encryption tokens

Tue, 02 Jun 2020 15:28:55 GMT - Antoine Martin: status changed; resolution set

The fix for websockets not honouring encryption options is in r26591.

(one could conceivably use SSL and AES at the same time by overriding XPRA_ENCRYPTED_SOCKET_TYPES and enabling it for ssl and wss sockets - though I don't really see the point of doing that.

This will be included in the next stable update.

I think we can close this ticket, feel free to re-open if you still encounter issues, and thanks for reporting it!


Tue, 02 Jun 2020 16:09:27 GMT - Antoine Martin: status changed; resolution deleted

Hit some more issues during testing.


Thu, 04 Jun 2020 14:01:49 GMT - Kyler Laird:

I understand that you're still working on this. I appreciate that. I'm just adding my experience.

I made the changes in r26591 on the server. I started it with --bind-ws=0.0.0.0:4430,encryption=AES,encryption=aes.txt. Using the Python client with --encryption=AES --encryption-keyfile=./aes.txt I get an authenticated connection but then it hangs.

server:

2020-06-04 13:45:54,855 sending data using AES encryption
2020-06-04 13:45:54,857 receiving data using AES encryption
2020-06-04 13:45:54,857 Authentication required by password authenticator module 1
2020-06-04 13:45:54,857  sending challenge for username 'kyler' using hmac+sha512 digest
2020-06-04 13:47:54,862 Error: connection timed out: ws socket: 10.0.0.164:4430 <- xxx.xxx.xxx.xxx:10976
2020-06-04 13:47:54,864 Warning: failed to query SO_RCVLOWAT, SO_REUSEPORT
2020-06-04 13:47:54,864 Warning: failed to query TCP_MAXSEG

client:

2020-06-04 08:46:45,931 Xpra GTK3 X11 client version 4.0.1-r26379 64-bit
2020-06-04 08:46:46,091  running on Linux Ubuntu 18.04 bionic
2020-06-04 08:46:46,092  window manager is 'GNOME Shell'
2020-06-04 08:46:46,126 Warning: failed to import opencv:
2020-06-04 08:46:46,126  No module named 'cv2'
2020-06-04 08:46:46,127  webcam forwarding is disabled
2020-06-04 08:46:46,545 GStreamer version 1.14.5 for Python 3.6.9 64-bit
2020-06-04 08:46:46,720 No OpenGL_accelerate module loaded: No module named 'OpenGL_accelerate'
2020-06-04 08:46:46,914 Warning: vendor 'Intel Open Source Technology Center' is greylisted,
2020-06-04 08:46:46,915  you may want to turn off OpenGL if you encounter bugs
2020-06-04 08:46:46,966 OpenGL enabled with Mesa DRI Intel(R) HD Graphics 5500 (Broadwell GT2)
2020-06-04 08:46:47,316 receiving data using AES encryption
2020-06-04 08:48:47,445 Connection lost

AES certainly seems to be working. If I don't use it on the client, I get "missing encryption tokens". If I use it with a different key, I get an Exception in parse_ws_frame().


Thu, 04 Jun 2020 14:03:35 GMT - Antoine Martin:

I understand that you're still working on this.

I believe the fix is in r26614. I am just testing now.


Thu, 04 Jun 2020 14:28:30 GMT - Antoine Martin: owner, status changed

Almost identical to #2431.

AES packets are padded to the next 32-byte boundary (that's just how CBC mode operates) and when authentication is enabled, this will be used on the challenge packets, before we get a chance to parse the hello packets fully, before we can disable legacy compatibility mode. And legacy mode was buggy with padded packets (must have been broken since #2121): the xpra header uses the non-padded packet size, but websocket framing requires the full packet size. (this explains why the browsers were failing to parse websocket frames)

For trunk: r26614 just disabled legacy mode, and then r26615 removed it entirely.

For v4 and v3 backports, I used a less disruptive solution: r26616 fixes the headers without removing the legacy mode.

@kylerlaird: please close if that works for you. I've tested the wiki examples with browsers, python client with ws://localhost:10000/ connection URIs, all with and without AES, with and without authentication.


Thu, 04 Jun 2020 15:05:33 GMT - Antoine Martin:

r26617 also helps?


Thu, 04 Jun 2020 15:18:31 GMT - Antoine Martin:

r26617 also helps?

No, hits another bug later, workaround applied as per #2799.


Thu, 04 Jun 2020 18:26:16 GMT - Kyler Laird:

I checked out the code from svn and made that. I copied it to the server and ran my AES test. When I attempt to connect, the server flags "unsupported cipher".

I'd be happy to try a package.


Thu, 04 Jun 2020 19:37:50 GMT - Kyler Laird:

My mistake; previous libraries were used in my earlier testing. I removed them all to ensure that the fresh ones were used.

It works from Python client. Using the HTML5 client, I briefly see the desktop (with an xterm) but then it dies.

I see this on the server.

2020-06-04 14:32:26,696 Warning: AES decryption failed: invalid padding
2020-06-04 14:32:26,696 Error: AES encryption padding error - wrong key?
2020-06-04 14:32:26,698 xpra client 3 disconnected.
2020-06-04 14:32:27,985 Warning: authentication failed
2020-06-04 14:32:27,986  missing encryption tokens
2020-06-04 14:32:28,988 Disconnecting client 127.0.0.1:57884:
2020-06-04 14:32:28,988  missing encryption tokens

On the browser, it says "You were disconnected for the following reason: missing encryption tokens"

Very promising!


Thu, 04 Jun 2020 20:04:27 GMT - Kyler Laird:

I made my own version of index.html, with encryption and key hardcoded.

Again, the xterm displays beautifully...for a moment.

hello capabilities
Object { version: "4.1", "build.revision": "26619", "build.local_modifications": "0", platform: "Posix", "platform.name": "Posix", "platform.processor": "Linux x86_64", "platform.platform": "5.0 (X11)", "session-type": "Firefox", "session-type.full": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:76.0) Gecko/20100101 Firefox/76.0", namespace: true, … }
Utilities.js:1:557
connection-lost Utilities.js:1:557
audio-state: stopped Utilities.js:1:557
connection closed: missing encryption tokens 2 Utilities.js:1:557
The connection to ws://localhost:4430/kyler.html was interrupted while the page was loading. Protocol.js:1:1643

Thu, 04 Jun 2020 20:09:32 GMT - Kyler Laird:

I just now noticed this on the server. It seems more relevant:

2020-06-04 14:55:00,497 Warning: AES decryption failed: invalid padding
2020-06-04 14:55:00,498 Error: AES encryption padding error - wrong key?
2020-06-04 14:55:00,499 xpra client 9 disconnected.
2020-06-04 14:55:01,850 Warning: authentication failed
2020-06-04 14:55:01,851  missing encryption tokens
2020-06-04 14:55:02,852 Disconnecting client 127.0.0.1:59224:
2020-06-04 14:55:02,853  missing encryption tokens

Fri, 05 Jun 2020 02:37:48 GMT - Antoine Martin:

It works from Python client. Using the HTML5 client, I briefly see the desktop (with an xterm) but then it dies.

I have just re-tested from a fresh checkout, using trunk, 3.0.x and 4.0.x branches and then following the exact steps from the wiki wiki/Clients/HTML5:

PASSWORD=YOURPASSWORD
AES_KEY=0123456789ABCDEF
echo -n $PASSWORD > password.txt
echo -n $AES_KEY > aes.txt
xpra start :10 --bind-tcp=0.0.0.0:10000 --html=on --start=xterm \
    --tcp-auth=file:filename=./password.txt \
    --tcp-encryption=AES --tcp-encryption-keyfile=`pwd`/aes.txt
sleep 5
xdg-open "http://localhost:10000/index.html?username=$USER&password=$PASSWORD&encryption=AES&key=$AES_KEY"

Works for me every time.

Please provide more details on your steps so that I can reproduce. Maybe you forgot to clean the html5 installation before installing from source, then the code actually used might be a mix of your old and new build.


Fri, 05 Jun 2020 12:02:40 GMT - Kyler Laird:

Thanks for the new package! I was having a heck of a time building and installing xpra from source.

Using 4.0.2 and the example in the AES section of https://xpra.org/trac/wiki/Clients/HTML5, I get this from the server:

2020-06-05 06:57:54,782 sending data using AES encryption
2020-06-05 06:57:54,785 receiving data using AES encryption
2020-06-05 06:57:54,785 Authentication required by password file authenticator module 1
2020-06-05 06:57:54,785  sending challenge for username 'kyler' using hmac+sha512 digest
2020-06-05 06:57:54,858 Error: error in network packet reading/parsing
2020-06-05 06:57:54,858  initializer for ctype 'unsigned char *' must be a cdata pointer, not memoryview
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/xpra/net/protocol.py", line 801, in _read_parse_thread_loop
    self.do_read_parse_thread_loop()
  File "/usr/lib/python3/dist-packages/xpra/net/protocol.py", line 929, in do_read_parse_thread_loop
    data = self.cipher_in.decrypt(data)
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/primitives/ciphers/base.py", line 149, in update
    return self._ctx.update(data)
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 120, in update
    n = self.update_into(data, buf)
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 135, in update_into
    data, len(data))
TypeError: initializer for ctype 'unsigned char *' must be a cdata pointer, not memoryview
2020-06-05 06:58:01,022 Warning: authentication failed
2020-06-05 06:58:01,023  missing encryption tokens
2020-06-05 06:58:02,025 Disconnecting client 127.0.0.1:47836:
2020-06-05 06:58:02,026  missing encryption tokens

If I remove the password requirement, I get this:

2020-06-05 07:01:09,114 sending data using AES encryption
2020-06-05 07:01:09,119 receiving data using AES encryption
2020-06-05 07:01:09,119 Handshake complete; enabling connection
2020-06-05 07:01:09,338 HTML5 Posix Firefox client version 4.0.2-r26625
2020-06-05 07:01:09,339   as 'kyler'
2020-06-05 07:01:09,345 setting keyboard layout to 'us'
2020-06-05 07:01:09,464  client root window size is 1136x648 with 1 display:
2020-06-05 07:01:09,464   HTML (301x171 mm - DPI: 95x96)
2020-06-05 07:01:09,464     Canvas
2020-06-05 07:01:09,504 server virtual display now set to 1088x640 (best match for 1136x648)
2020-06-05 07:01:09,662 Error: error in network packet reading/parsing
2020-06-05 07:01:09,663  initializer for ctype 'unsigned char *' must be a cdata pointer, not memoryview
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/xpra/net/protocol.py", line 801, in _read_parse_thread_loop
    self.do_read_parse_thread_loop()
  File "/usr/lib/python3/dist-packages/xpra/net/protocol.py", line 929, in do_read_parse_thread_loop
    data = self.cipher_in.decrypt(data)
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/primitives/ciphers/base.py", line 149, in update
    return self._ctx.update(data)
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 120, in update
    n = self.update_into(data, buf)
  File "/usr/lib/python3/dist-packages/cryptography/hazmat/backends/openssl/ciphers.py", line 135, in update_into
    data, len(data))
TypeError: initializer for ctype 'unsigned char *' must be a cdata pointer, not memoryview

Fri, 05 Jun 2020 12:15:33 GMT - Antoine Martin: owner, status changed

TypeError: initializer for ctype 'unsigned char *' must be a cdata pointer, not memoryview

The version of python3-cryptography to support memoryviews.

As per this comment: As of cryptography 2.5 we support the buffer protocol for many key inputs (and we have update_into in symmetric cryptography to write into a rw buffer).

The quick fix would be to call our helper function memoryview_to_bytes, but this would penalize (adding an extra memory copy every time) all the other distributions that ship with a more recent version of the library. So I will make a patch and apply it selectively only on the distros that need it.

In the meantime, you can try replacing:

data = self.cipher_in.decrypt(data)

with:

data = self.cipher_in.decrypt(memoryview_to_bytes(data))

Fri, 05 Jun 2020 12:55:55 GMT - Kyler Laird: status changed; resolution set

It works!!! I appreciate that you not only explained the dependency but also gave a quick workaround.

I am integrating this into a big project. Being able to use shared keys instead of certificates will simplify it immensely.

Xpra feels like a niche project, not getting nearly the coverage of VNC. My every interaction with it, however, screams of high quality. I am delighted that I chose to depend on it for this project.

Thank you, Antoine. I see only a sliver of your workload but the grace and competence you bring to it are marvelous.


Fri, 05 Jun 2020 14:53:46 GMT - Antoine Martin:

I see only a sliver of your workload but the grace and competence you bring to it are marvelous.

Thank you very much!

A nicer solution has been merged in r26631 and will be in the next stable update.


Fri, 05 Jun 2020 21:29:18 GMT - Kyler Laird:

So...it turns out that I *do* need to use AES over SSL, as mentioned in comment:4. I can set ENCRYPTED_SOCKET_TYPES=wss and use --bind-wss=0.0.0.0:1234,encryption=AES,encryption-keyfile=/tmp/aes.txt but the AES encryption appears to be ignored.


Sat, 06 Jun 2020 15:09:03 GMT - Antoine Martin:

it turns out that I *do* need to use AES over SSL ... --bind-wss=0.0.0.0:1234,encryption=AES,encryption-keyfile=/tmp/aes.txt

This works for me, I've tested both with 4.1 beta and 4.0.

Maybe try the legacy syntax form:

XPRA_ENCRYPTED_SOCKET_TYPES=wss xpra start --start=xterm --no-daemon -d auth,crypto,http \
    --bind-wss=0.0.0.0:10000 --wss-auth=file:filename=./password.txt \
    --tcp-encryption=AES --tcp-encryption-keyfile=`pwd`/aes.txt \
    --ssl-cert=./ssl-cert.pem

I can connect with:

xpra attach wss://127.0.0.1:10000/ --tcp-encryption=AES --tcp-encryption-keyfile=aes.txt -d auth,crypto --ssl-server-verify=none

Or with the browser:

xdg-open "https://127.0.0.1:10000/index.html?username=$USER&password=$PASSWORD&encryption=AES&key=$AES_KEY"

If that still fails, please post the server debug output (with -d auth,crypto,http as above) and keep an eye on your browsers console log, which may have more information.


Sat, 06 Jun 2020 16:00:44 GMT - Kyler Laird:

That solution works for me! I'm working again.

Thank you!


Tue, 09 Jun 2020 05:50:18 GMT - Antoine Martin:

FYI: in 4.1 you can now use the nicer syntax:

xpra attach tcp://user:password@127.0.0.1:10000/keydata=0123456789ABCDEF

See ticket:2794#comment:2 for details.


Sat, 23 Jan 2021 06:01:01 GMT - migration script:

this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/2793