webui-dev / webui

Use any web browser or WebView as GUI, with your preferred language in the backend and modern web technologies in the frontend, all in a lightweight portable library.
https://webui.me
MIT License
2.63k stars 157 forks source link

Security issues of data transferred between processes #237

Closed delphius closed 9 months ago

delphius commented 9 months ago

Since the library implements a scheme of not purely intra-process, but inter-process interaction via websockets (over tcp/ip), then in industrial use the question arises of protecting the transmitted data (ssl/tls/etc) within the means provided by the library. Has someone already solved this issue or approached at least conceptually?

On the surface lies the option of using an alternative secure connection over any protocol between the program and the browser, but then some of the meaning embedded in the library is lost. By leaving the connection unprotected, we endanger the reading/manipulation of the transmitted data between the frontend and the backend

On the other hand, all processes and interactions take place within the same operating system and on localhost, so the question may not be very relevant. Who thinks what?

hassandraga commented 9 months ago

TLS encrypted is already implemented in a separated beta test branch with TLS. I'm planning to importing it to the main branch when we have a stable version (before making it public).

The issue is I still don't know how we should provide TLS.

  1. Making WebUI TLS only (mean OpenSSL lib must be available to build WebUI)
  2. Providing two version, webui-2, webui-2-tls. And the user can choose between them.

However, this will need updating all the build scripts and all CI. So, let's add it at the end but not now.

delphius commented 9 months ago

TLS encrypted is already implemented in a separated beta test branch with TLS. I'm planning to importing it to the main branch when we have a stable version (before making it public).

I see! Lightweight in the name of the library will be slightly heavier due to OpenSSL :)

So, let's add it at the end but not now.

Of course, this question just lay on the surface and it was worth asking now

hassandraga commented 9 months ago

Thank you for pointing this out @delphius 👍 We should start a discussion on which approach we should take. 1 or 2.

delphius commented 9 months ago

The issue is I still don't know how we should provide TLS.

For me, the issue of tls support is not a question of a separate version of the library, but in general a question of complex security of the tool that I rely on during development, so for me this question is not worth it at all, I choose option 1

hassandraga commented 9 months ago

Me too, I vote for 1. My point is that WebUI should provide only encrypted communication.

fibodevy commented 9 months ago

If it will require additional file, I vote for makig it optional and enabled with the API.

If it will significantly increase the library size, I even more vote for it to be optional and use a shared library.

If only websocket data exchange needs to be encrypted I guess I vote for using libsodium instead of openssl.

delphius commented 9 months ago

If it will require additional file, I vote for makig it optional and enabled with the API.

In any case, I agree that for good, this is not a question of the build option of the program, this support should be inside and should be optional.

If it will significantly increase the library size, I even more vote for it to be optional and use a shared library.

two files .dll for Windows (total 5 MB), built-in support for almost all Linux distributions (3.x+) with static linking, it should be significantly less

If only websocket data exchange needs to be encrypted I guess I vote for using libsodium instead of openssl.

Yes, we just need to transfer the ws connection to the wss connection and this is not a reason to pull whole cryptolib with us

fibodevy commented 9 months ago
  1. Dev tools allows to see websocket messages
  2. The library can be replaced with a proxy one
  3. WebUI API can be hooked
  4. CreateProcess API can be hooked to enable remote debugging in the browser
  5. Browser executable can be replaced or modified
  6. The certificate can be extracted and the traffic decrypted
  7. The certificate can be replaced in the host app
  8. Custom extension can be loaded into the browser and replace WebUI JS object

So if everything is happening on the same machine, what is this encryption for?

hassandraga commented 9 months ago

I agree @fibodevy, this is why I spent time implementing TLS, but then I threw it in another Branche.

The only scenario TLS comes in handy is when your project has a password, and you use webui_get_url() to let a user connect from another machine. Then TLS is a must, but WebUI is a GUI library designed to use a local web browser as UI in a local machine. And if the attacker already has access to the local machine, he can change the local certificate or read the keyboard events... TLS is for remote communication, and WebUI is for local communication.

However, we should do more research and vote at the end.

delphius commented 9 months ago

On the other hand, all processes and interactions take place within the same operating system and on localhost, so the question may not be very relevant.

I said this from the very beginning, that in the context of working on a local machine, it generally makes no sense. But we discussed it and came to the same conclusions.

For myself, I conceptually realized that the only way to guarantee data security is to use the browser only for input/output via the api from the programming language with all correctness checks and so on on the server (language) side, perceiving the browser side as a hostile environment

fibodevy commented 9 months ago

So, since everything runs on localhost, encryption with TLS makes no difference.

I mean, TLS works on higher layer, the browser decrypts it. If you have access to the browser, and you do, then the traffic is almost transparent for you. Simplest method is devtools and just look at incoming WS messages.

Encryption would be required if there was a danger that a third party was watching the traffic. And there is no third party, there is only WebUI and the user. On the same machine.


I mentioned libsodium because that can be used to add real security to the communication.

libsodium can work at a lower layer, in WebUI JS and as a shared library, or static linked, but I would prefer this functionality as an addon. Lightweight is really important.

two files .dll for Windows (total 5 MB), built-in support for almost all Linux distributions (3.x+)

libsodium is less than 0.3 MB

How to make it much more difficult to tamper with the data?

libsodium has ready to use mechanism to exchange async keys, then using those keys exhange actual symmetric encryption key. libsodium uses ChaCha20 (and its variants), many sources say its better than AES. Its fast, its not hardware boosted but really fast. Implementation of ChaCha20 is really small, I mean implementation from scratch, you can fit in 50 lines of code. But along with symmetric encryption you need the keys exchange mechanism, assymetric, so full libsodium lib is needed.

AFAIK TLS uses ChaCha20 among other algos.

The user would see some encrypted meaningless data, 99% of them will give up at at this point. Remaining 1%, well, its their machine, you cant be 100% sure.


For myself, I conceptually realized that the only way to guarantee data security is to use the browser only for input/output via the api from the programming language with all correctness checks and so on on the server (language) side, perceiving the browser side as a hostile environment

Imho thats obvious. You send only the data you want the user to see. And never trust the input, always validate.

I will add here that I think the text editor example is poorly designed. Its fully written in JS, the only job for the executable is to open the browser. JS should communicate with the app to open the file, read, save, close etc. All the logic should be inside executable. This example shows improper usage of WebUI.


One last thing, in the branch with TLS if you dont provide certificate yourself, it generates 2K self signed cert. How long does it take? 5s? 10s? Kinda long before window shows up. Elliptic curve async keys are generated instantly. Another point for libsodium.

But I wouldnt do it right now, lets release 2.4.0 and think about encryption for next release.

delphius commented 9 months ago

I mean, TLS works on higher layer, the browser decrypts it. If you have access to the browser, and you do, then the traffic is almost transparent for you. Simplest method is devtools and just look at incoming WS messages.

Only if we cram the entire browser side into webasm in the future and a separate direct encryption channel without a browser is implemented

Encryption would be required if there was a danger that a third party was watching the traffic. And there is no third party, there is only WebUI and the user. On the same machine.

There is always a third party :)))))) Or it will definitely appear sooner or later and you need to be fully armed

Lightweight is really important.

This is actually a killer feature of the library

libsodium has ready to use mechanism to exchange async keys, then using those keys exhange actual symmetric encryption key.

We should probably be talking about implementing TLS 1.3 with the minimum possible amount of code while maintaining the security level

libsodium uses ChaCha20 (and its variants)

Its a fork of NaCl from djb it implements aes/aegis/chacha20

many sources say its better than AES

This is a controversial statement, rather ChaCha20 provides AES-comparable cryptographic strength at a much higher speed without hardware support. And AES has been and remains the world standard with the widest possible support and implementation.

AFAIK TLS uses ChaCha20 among other algos.

Just the same, Google once pushed it to ensure that its browsers work on mobile devices without hardware support. And now it's in TLS 1.3 only because if there is a critical vulnerability in AES, then there will be somewhere to jump painlessly for a while. All other things being equal, AES will be used with or without hardware support.

Implementation of ChaCha20 is really small, I mean implementation from scratch, you can fit in 50 lines of code. But along with symmetric encryption you need the keys exchange mechanism, assymetric,

Yes, there is much more needed to implement the minimum set of TLS 1.3:

So as they say: The first rule of cryptography club is: never invent a cryptography system yourself The second rule of cryptography club is: never implement a cryptography system yourself

so full libsodium lib is needed.

Exactly.

This example shows improper usage of WebUI

I also don't quite agree, this example involves imagination and makes you evaluate the potential scale, implementation details and security issues, this is the second question in this case

Elliptic curve async keys are generated instantly. Another point for libsodium.

Technically, this can be implemented using any modern reliable crypto library, but the requirement of lightness in combination with the lowest-level implementation of libsodium should give a good effect.

But I wouldnt do it right now, lets release 2.4.0 and think about encryption for next release.

I completely agree

hassandraga commented 9 months ago

How about we implement ChaCha20 + Poly1305 (symmetric)?

https://cr.yp.to/chacha.html https://cr.yp.to/mac.html

fibodevy commented 9 months ago

Lets release 2.4.0 first and then we will discuss security, no rush here, its just about preventing data tampering by the user.

I suggest doing #243, then release. Either send chunked data with JS, or merge data in C (per websocket connection). I guess holding websocket data buffer per connection is easier, and release the data (call handler) if its size reaches the packet length.

delphius commented 9 months ago

How about we implement ChaCha20 + Poly1305 (symmetric)?

Two questions arise. Firstly, how will a shared secret be generated, which will be used to generate the gamma for the cipher in chacha for both sides (otherwise, why encrypt and spend resources at all if we can just mask), and secondly, how to transmit the generated information about the shared secret, or simply use a secure generation function like curve25519

Lets release 2.4.0 first and then we will discuss security, no rush here, its just about preventing data tampering by the user.

This is just a discussion, not a call to action. For example, I will not help with the c library itself, although I understand a little about the implementation of cryptographic functions.

hassandraga commented 9 months ago

how will a shared secret be generated

Probably randen-rng is enough.

how to transmit the generated information

After we implemented the Token Secured Requests commit, I can say that only the legit UI can see webui.js. Which contain the secret token, and in future will also contain the secret generated key.

delphius commented 9 months ago

Probably randen-rng is enough.

This is only enough for the client or server to individually generate some random initial vector or number with some level of cryptographic strength. I mean a shared secret for both the client and the server at the same time.

After we implemented the Token Secured Requests commit, I can say that only the legit UI can see webui.js. Which contain the secret token, and in future will also contain the secret generated key.

This is a question of the origin of chicken and egg :) Any wsS connection can only be established on top of an already established secure connection, so token transfer and any such things are already happening inside a secure communication channel.

The only option other than this is hand-to-hand common secret transfer (for example, in the form of a protected parameter when launching the browser, or static/dynamic common secret in webui.js). But in my opinion it must be implemented with widely used standard cryptoprimitive

hassandraga commented 9 months ago

We all agreed that the best way to secure data transfer between UI and the backend application is through TLS (Already implemented in a separate test branch). But we also agree that implementing TLS involves the OpenSSL library as a third-party dependency, and the statically linked apps will still need the OpenSSL dynamic library. So, to avoid all those TLS complexities and pushed by the idea that WebUI works only on the same local machine, we decided to implement a custom, simple, non-complicated encryption data exchange between UI and backend through non-encrypted WebSocket protocol.

Right now, an attacker with a simple TCP sniffer can detect the Token when running the WebUI app, but using this Token won't work for the attacker because the WebUI app already used it. If we add TLS in the future, it will be even better, as the Token exchange will be done in an encrypted tunnel. So, the attacker should have access to the UI source (Dev-tool) to get the Token... even though the Token is already useless because it's already used.

The Token is safe to be transferred in a non-secured tunnel. Still, it's not a good idea to exchange the secret key in a non-secured tunnel, especially for symmetric cryptography... however, we could start with a simple private key exchange generated by the WebUI (backend) only. We move to another more complex way to make both the backend and UI create the key at the same time. But if the process is more complicated than we taught, let's use TLS and fix this forever.

I'm not a cryptography expert, so all I said is a personal suggestion; it can be correct or wrong.

delphius commented 9 months ago

I'm not a cryptography expert, so all I said is a personal suggestion; it can be correct or wrong.

Everything we talked about and discussed absolutely fits into the scheme intended for implementation. The only difference is in the tasks solved by the proposed security tools:

  1. The establishment of a secure connection is carried out with two main purposes: to ensure the confidentiality of the transmitted information for an external observer (using Chacha20) and its integrity (using Poly1305)
  2. In order to establish a secure connection between the client and the server to ensure the safe generation of a common secret that will be used to fulfill paragraph (1), we will need to use a reliable mechanism for such generation and distribution, for example curve25519
  3. In order to authenticate the parties and make sure that we are communicating with exactly the server (client) with which we wanted, we must use an authentication mechanism, for example, a one-time token (which is already implemented) or a certificate (as browsers do, unilaterally by default, only checking the server certificate or mutually if the security policy requires it, for example, a banking service or the like)

The only subtlety is that authentication is carried out BEFORE the establishment of a secure connection, during mutual checks and approvals of the parameters of the secure connection (the so-called handshake). I haven't looked at the goals and implementation of the token mechanism in webui yet, but as far as I know, a third-party channel or method is usually used to get a token (OTP). And if its transmission over an open channel is secure and fits into the authentication scheme, then we have implemented (or planned for implementation) all the necessary mechanisms and exactly in the order that provides reasonable sufficient security

And finally, do not forget that secure message transmission is a separate protocol on top of websockets, which requires its own message formats (handshake and session records), as well as processing on both sides (message parsing, errors handling)

But the good news is that the entire implementation of the listed list can really be put in 1k lines of code

hassandraga commented 9 months ago

I see, thank you @delphius, so the big picture is using Curve25519 + ChaCha20 + Poly1305 in C and JavaScript.

Honestly, Using OpenSSL dynamic lib will be less headache, quicker, easier, more secure and still our best option.

delphius commented 9 months ago
  • ChaCha20 Easy key leak

This is just a mechanism for turning plain text into a cryptographic ciphertext, it does not affect key leakage in any way, it is not intended to solve the problem of preventing leakage or key distribution. The main problem with chacha20, as with any other symmetric encryption algorithm, is that it requires the use of the SAME keys on both sides, while the mechanism for generating and transmitting them between the parties must be secure to prevent leakage (alternatively, they can be transmitted in some other way between the parties)

and can't authenticate the other parties.

The tasks of both chacha20 and poly1305 is not to authenticate the other parties, poly1305 only ensures the immutability of the ciphertext (in the simplest case, as well as the integrity of the open linked data that can be transmitted together with the message), like, for example, md5 hash for files

  • ChaCha20 + Poly1305 Useless. Key gets exchanged in plain text.

If the keys are not transmitted through an open channel, but in an alternative way (well, if of course it exists), then this bundle is already quite enough

  • ChaCha20 + Poly1305 + Curve25519 Okay. But need time to implement in C and JS.

This is a bundle that provides a sufficient level of security, used in modern protocols and their implementations, invented and developed specifically for a simpler and more efficient implementation by the famous DJB Only one task remains unsolved by this bundle is authentication of the parties to a secure exchange (sertificate/token/OTP/etc)

  • And it won't be as secure as OpenSSL (HTTPS).

It will provide exactly the same level of security of the transmitted information as OpenSSL, since it uses exactly the same set of cryptoprimitives, but the level of resistance to possible attacks, including through side channels, will be lower, due to the fact that the implementation of attack-resistant algorithms and their implementations is the goal and the highest priority of all crypto libraries without exception, and they are constantly being tested and refined by a large community of both developers themselves and business representatives using them to ensure the security of transmitted information, as well as enutziasts exploring the capabilities and durability of such algorithms and most importantly their implementations

Honestly, Using OpenSSL dynamic lib will be less headache, quicker, easier, more secure and still our best option.

The implementation of the specified limited set of cryptoprimitives + protocol is no more complicated than the implementation of the websocket protocol, which was performed on the webui server side. Alternatively, we can implement a switchable socket interceptor, both for the internal implementation and for openssl

p.s. I apologize for misunderstanding some nuances, I am not a native speaker and use a translator

hassandraga commented 9 months ago

This is just a mechanism for turning plain text into a cryptographic ciphertext

I know, but I meant using ChaCha20 alone is useless as we will share the key in plain text, which basically means the key is leaked... but yes, of course, ChaCha20 is not designed to solve this issue in the first place.

poly1305 is not to authenticate the other parties

I didn't know that. I taught Poly1305 is to verify and authenticate the other parties... Thank you for clarifying this 👍

It will provide exactly the same level of security of the transmitted information as OpenSSL

Except authentication of the parties as you said earlier, it remains unsolved.

is no more complicated than the implementation of the websocket protocol

I'm more worried on the JavaScript implementation, I don't know if there is a stable robust ChaCha20, Poly1305 and Curve25519 JavaScript libraries online.

hassandraga commented 9 months ago

I apologize for misunderstanding some nuances, I am not a native speaker and use a translator

Don't worry, understandable, you are all good 👍

delphius commented 9 months ago

Except authentication of the parties as you said earlier, it remains unsolved.

Authentication of the parties is performed BEFORE the establishment of a secure connection, only after authentication of the parties there is a transition to encryption, so technically authentication has no effect on the process of secure transmission itself. If authentication was unsuccessful, then the secure connection is simply NOT established and is reset by the party that identified problems with the authentication of the opposite party

But the task of verifying the authenticity of the connection side is relevant within the framework of connection security in general, in most cases, of course, authentication of the called party by the calling party, since almost all such protocols have the client-server format

fibodevy commented 9 months ago

ChaCha20 + Poly1305 + Curve25519

As @delphius explained this is a complete set and is really secure and battle tested. ChaCha20 is just encryption, Poly1305 is a hash, like md5, and Curve25519 is the elliptic curve i mentioned some time ago. All this is inside libsodium. Its designed in a way you dont make a mistake. libsodium is even included in PHP since ~7, pre-compiled. (yes its fork of NaCl, but NaCl is unfinished)

@hassandraga I can guarantee you, you can have HTTPS and some tokens to ensure webui.js is loaded once, and it will be trivial to get actual source od webui.js.

In the case od WebUI, HTTPS does not make sense AT ALL, really. Do what you want, in 1 day I will write an app to listen the traffic between WebUI <> browser.

You can protect the communication to some extent.

webui.js can be downloaded once, right? And if there is a page refresh, then new token is generated and webui.js can be downloaded again, right? If no connection to WS = webui.js can be downloaded, once, right? (dont have time to look at the source now)

In this case, I would use this token as key for ChaCha20. It just needs to be 32 bytes. This is really ENOUGH. It will discourage most ppl from trying further.

If you dont want to use Poly1305 with ChaCha20, its ok, any hash will be ok, its just to ensure nobody touched the encrypted data "on the fly". Its not even required in this case, but you can add some SHA3 and it will be ok.

Any encryption "good enough" will be just good enough. Since its all on localhost, if someone really wants to tamper with the traffic, he will, no matter what encryption you implement.


This above is really simple and IMHO "enough", but, here is the second part, to make it even more difficult. Above method will only require you to include some very low in size implementation of ChaCha20. The below, will require libsodium.

One thing to worry about is that token used as key will be transmitted as PLAIN TEXT. BTW I will mention here: I consider TLS as "PLAIN TEXT" too, since its on the same machine AND the browser takes care of it. So anything can see the token. And here comes the "assymetric key exchange system" from libsodium.

Instead of using token as encryption key, you make different key and "do some magic" that is ready to use in libsodium to exchange the "symmetric key".

TLS = no protection at all (ON THE SAME MACHINE) Internal encryption = some protection

@delphius libsodium can be static linked in Pascal, so no worries, I will show you how to do it like I showed you how to static link WebUI 😃

@hassandraga Its available as shared lib, static lib, and pure 100% JS code.

But if using such big library, and its only 0.3 MB, not huge OpenSSL, I still would like to get this as an ADDON. Or make it default built-in and an option to DISABLE it to keep WebUI really "lightweight". For me lightweight is super mega important, what is taking 200 KB anyway? Civet? Or some included C std libs?

delphius commented 9 months ago

In this case, I would use this token as key for ChaCha20

This is also the first thing that came to my mind when I read about the open one-time authentication code that is being transmitted. Although this will essentially be equated to a key leak, but it will be ideal for the task of just masking traffic.

Moreover, the implementation of a full-fledged protocol is not required, it is enough to pass a websocket message through chacha20 and decrypt it on the other side, so we will get rid of the complexity of the protocol over the protocol.

The implementation of message authentication in such a configuration is absolutely meaningless and only a waste of resources (imho)

Instead of using token as encryption key, you make different key and "do some magic" that is ready to use in libsodium to exchange the "symmetric key".

since key premutations are carried out by both sides according to a well-known algorithm and the transmitted public shared initial secret is not tied to an irreversible function, this is also a rather pointless undertaking in fact, well, only if in order to obtain a symmetric encryption key with sufficient cryptographic strength from the crypto material source, but this also will not have any effect on security, except how to solve the problem of disguise

libsodium can be static linked in Pascal, so no worries, I will show you how to do it like I showed you how to static link WebUI

I would prefer just TLS 1.3 implemented on pure Pascal :)

hassandraga commented 9 months ago

it will be trivial to get actual source od webui.js

I agree, at the end of the day, WebUI apps are using web technologies, so UI source code and webui.js source code is visible in the browser.

in 1 day I will write an app to listen the traffic

I'm sure you can 👍

can be downloaded, once, right?

I never tried to protect WebUI apps from a manual "Human" attack, I mean, from someone actually using the machine. Whatever we do, there is always a way, especially for a web app... but instead, I tried to stop/prevent or at least make it harder for an automated unwanted script from talking to the backend. That's it. And I hope the latest Token implementation does the job.

It will discourage most ppl from trying further

That what I wished for by using the token for automated unwanted scripts

TLS = no protection at all (ON THE SAME MACHINE) Since its all on localhost, if someone really wants to tamper with the traffic, he will, no matter what encryption you implement

Let's all agree on this. It will be an excellent ground to make a final decision from. So, why add more complexity and compute time to encrypt and decrypt data in a project that whatever we do will be cracked because it's a localhost web app? And if someone wants the TLS encryption, we can simply add a new compile argument to produce the webui-2-secure library that uses OpenSSL.

hassandraga commented 9 months ago

pass a websocket message through chacha20 and decrypt it on the other side

Let's make webui-2 a plain text non-encrypted library, small and fast as possible. And webui-2-secure a fully TLS encrypted communication library using OpenSSL. What do you think guys?

I would prefer just TLS 1.3 implemented on pure Pascal :)

Even better if WebUI-Secure does this for Pascal, and all other wrappers :)

fibodevy commented 9 months ago

Let's make webui-2 a plain text non-encrypted library, small and fast as possible. And webui-2-secure a fully TLS encrypted communication library using OpenSSL. What do you think guys?

TLS will protect from seeing the traffic by the sniffers, and protecting the token. So maybe also add encryption of websocket messages using the token as the key? So the messages cant be seen in devtools.

Im ok with 2 versions, TLS and no TLS.

I have an idea where TLS will be actually required, but its for later 🙃

hassandraga commented 9 months ago

Good. I will implement TLS.

delphius commented 9 months ago

Even better if WebUI-Secure does this for Pascal, and all other wrappers :)

Yes, shure :)

I meant that today, as far as I know, there are no full-fledged TLS implementations in pure Pascal, otherwise I would not have to make my own Pascal TLS PoC https://github.com/delphius/fpmtls

I have an idea where TLS will be actually required, but its for later

Solid riddles :)

hassandraga commented 9 months ago

TLS is fully implemented. By default, WebUI generate a self-signed certificate, so browsers may show a warning. But user can use his own trusted certificate if needed. webui_set_tls_certificate(certificate_pem, private_key_pem).

Also, I updated the TLS code to support OpenSSL 3.x.