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

Implement Native Messaging for browser extensions #287

Closed
jsha opened this issue Feb 11, 2017 · 39 comments
Closed

Implement Native Messaging for browser extensions #287

jsha opened this issue Feb 11, 2017 · 39 comments

Comments

@jsha
Copy link

jsha commented Feb 11, 2017

Chrome and Firefox support Native Messaging, which is a way for browsers to communicate with native processes more securely than using an HTTP server on localhost.

The Chrome documentation describes it well, but in short, the native application provides a manifest file at a well-known location, listing some information about itself and the set of extensions that are allowed to access it. When the extension requests, the browser will start up the native process, capturing its stdout/stdin, and pass JSON messages to and from it.

This would require significant changes on both the KeePassXC side and the browser extension side, but it would have two main advantages:

  • Clearer and stronger security properties than the HTTP API.
  • Possibly simpler experience installing a browser extension, since no initial handshake and authorization is required.
  • Better user experience when the browser extension needs to talk to KeePassXC, but it is not yet running.
@phoerious
Copy link
Member

You're not the first to suggest better and more secure browser integration (#259 and #88). The problem is that it really is a lot of work and we can't also maintain and support two browser extensions alongside KeePassXC. If someone steps forward and builds those browser extensions, we may implement support in KeePassXC for it, but the other way round is just not possible.

@louib
Copy link
Member

louib commented Feb 17, 2017

Would the database / password / keyfile combination live in the browser extensions? The way I see it, a new host process is spawned every time a message is sent from the extension, so there's no way for the host to preserve some state, it has to be passed by the extension every time a message is sent.

@jsha
Copy link
Author

jsha commented Feb 17, 2017

The host process is started once when the browser extension calls runtime.connectNative, and stays running until the extension closes the "port." The host process has access to the filesystem and can store state there.

There is a good question about what to do if there is already a running KeePassXC. The simple answer to let both run, and rely on DB merges. The more complex answer is to have a stub process for KeePassXC that tries to connect to a running process via IPC, and if there is none, starts one.

@louib
Copy link
Member

louib commented Feb 17, 2017

@jsha

Better user experience when the browser extension needs to talk to KeePassXC, but it is not yet running.

This implies that the opening of the database (asking for database filename / password / keyfile) would take place in the chrome extension. is that correct?

@phoerious
Copy link
Member

phoerious commented Feb 17, 2017

I suppose it would be possible to simply raise an existing KeePassXC instance. Having password and keyfile inside the browser extension is out of the question.
The main difference to KeePassHTTP here is probably that a KeePassHTTP server must already be running while native messaging can start KeePassXC on demand.

@jsha
Copy link
Author

jsha commented Feb 17, 2017

Yep, my thought was similar to what @phoerious said - KeePassXC could bring itself to the foreground if it needs the database unlocked.

@varjolintu
Copy link
Member

I'm currently experimenting with this. At this point I have a fork of chromeIPass that connects to a python host and sends a version query request and receives a reply (Settings -> About shows KeePassXC version instead of KeePassHTTP version). So it's a start. Because KeePassHTTP is not needed at all (or HTTP Requests) the whole protocol needs to be rewritten. Takes a while.

The next big step is to connect to a dummy database and receive a hard coded entry for a single page.

@GuilloOme
Copy link

@varjolintu, do you have a branch/repo where we can check how you do that? I'll be very interested to see the code! Thank

@varjolintu
Copy link
Member

@GuilloOme, not yet. I'll make a public fork when I've done some more experimenting.

@varjolintu
Copy link
Member

At this point I have a "working" way to deliver a generated password and multiple credentials to the browser tab. Also filling the password works based on the username entry. But still, some work needed before a public fork.

I was wondering if any encryption is needed between KeePassXC and the browser extension? Is Native Messaging really secure enough for transferring the data as plaintext?

@jsha
Copy link
Author

jsha commented Feb 22, 2017

Is Native Messaging really secure enough for transferring the data as plaintext?

In my opinion, yes. Here's my reasoning:

If there is software on your machine that can intercept stdin / stdout, or wrap KeePassXC in a fake binary that will proxy stdin / stdout, that software already has sufficient privilege to read your KeePassXC passwords directly from memory, or present a fake version of KeePassXC to collect your password and keyfile.

@GuilloOme
Copy link

According to our devop at @delvelabs, depending of the secure level of your OS, it could be very easy to read what is shared between process in the same userland.

Since it is not possible to make sure that an OS is safe enough, it should be encrypted…

And from here, start the debate around the key management between processes 😉

@jsha
Copy link
Author

jsha commented Feb 22, 2017

@GuilloOme: The question is: Is it easier to read stdin/stdout between processes than it is to read another process' memory, or to wrap another binary?

@GuilloOme
Copy link

GuilloOme commented Feb 22, 2017

As I understand, there is 3 ways that we could access to what we want to protect (ie. readable password).

Assuming we are in the same userland (no privilege escalation needed):

  • Read inter process communication done through stdin/stdout: Quite simple (depending of the OS security, any could already have access to process stdio without privilege escalation and/or exploit)
    • Mitigation: Message encryption
  • Access other process memory space: Possible but difficult
    • Mitigation: Hardening of the OS but not a concern for the current situation/feature (if memory is accessed, so the Native Messaging is the least of our concern since the all password are store in memory)
  • Wrap a binary (KeePassXC or the browser): Targeted attack = difficult but plausible
    • Assuming the wrapper access only the IO of the process in question, then the mitigation will be: Message encryption + manual acceptation of password access by the user himself
    • Assuming the wrapper access more than just the IO, then the concern is the same as the case where the memory space is accessed…

Is there an other case, we would want to cover?

The next concern will be key management…

@varjolintu
Copy link
Member

The key management and transferring the data between the browser extension is not an easy task. One way to do it might be:

  • Ask a separate password in the browser extension (but do not save it, only in memory)
  • Ask the same password in KeePassXC (maybe a separate setting 'Browser Extension')
  • Do not use the password directly but encrypt is using for example session25519 at both ends. However, if the encrypted password is saved in the KeePassXC database, it should be safe enough..
  • Use this encrypted password as a key at both ends
  • Browser extension creates a new nonce for every browser session
  • Browser extension transfers this nonce to KeePassXC
  • Use TweetNaCl's secretbox for transferring entirely encrypted messages and use the password generated key and newly randomized nonce

Pros:

  • The key is never transferred. Nonce is. But the nonce is not enough for decrypting the traffic.

Cons:

  • Complex. And more passwords..

@phoerious
Copy link
Member

phoerious commented Feb 23, 2017

I think the most important thing here is authentication over encryption. Encrypting the password while sending it to another process doesn't make things much more secure. What's very important though, is that once your database has been unlocked, only a privileged (authenticated) process can request a password. We probably need some sort of challenge-response protocol for that.

@GuilloOme
Copy link

For the secure session management and the crypto, I was talking with our crypto guy here at @delvelabs and he were recommending trying using a lib/protocol like libsignal-protocol made and tested against this kind of concern availlable in JavaScript and C (never tested on our side throw). But still if it's too much work, we should drop the encryption (for now) and focus on authentication. As @phoerious was saying if every request are blindly accepted, no need for an encryption layer…

The way the current implementation of KeePassHTTP behave seems correct: requesting a user action through a dialog for each request and a one time authentication for the browser extension.
It could be a good start for the new browser extension and then upgrading it with more robust messaging/session security in the future.

@TheZ3ro
Copy link
Contributor

TheZ3ro commented Feb 24, 2017

The main problem with KeePassHTTP is that the key is randomly generated by the client (I don't even know if CSPRNG is used here) and sent base64 encoded plaintext to the KeePassXC server.
This is fine if your PC is safe and you limit your connection to 127.0.0.1

I kind of like @varjolintu proposal and I came out myself with a similar idea some week ago.

Some discussion point:

  • Not saving the password in the browser is good, but then the user will need to enter it everytime (so here we have another "master password" to remember)
  • Instead of letting users enter their password, the browser extension can generate a random 26-char long string from a CSPRNG showing that to the user. the user will manually enter that password in KeePassXC. This need to be safely stored in both browser and server.
  • We can strip session25519 and use only scrypt to derive a key from the 26-char long password (on both client and server)
  • Implement a simple challenge-response with hash(client_nonce+server_nonce+key)
  • Every nonce must come from a CSPRNG
  • All the responses/passwords will then be AES-256 encrypted with key by the server and decrypted by the client.
  • Alternatively 2 key can be derived from scrypt, one used for the challenge-response and one used for encryption

This will authenticate the client and can address MitM and replay-attack (since the nonce is randomly renerated everytime and the attacker can't guess the nonce).
But still the encryption will suffer offline attack (an attacker that logs every request can then brute-force the password).
Using scrypt will make life little harder for an attacker depending on the keylenght and scrypt parameters.
Storing the password safely is the big problem here. Storing that only on KeePassXC won't save the user as long as the db is in memory but not storing it at all will need the user to re-enter the password everytime in both browser and server.

Sorry for the wall of text.

Also now I remember this from last year https://medium.com/@rosshosman/1password-sends-your-password-across-the-loopback-interface-in-clear-text-307cefca6389#.kva4n6ofq

@jsha
Copy link
Author

jsha commented Feb 24, 2017

@GuilloOme:

Read inter process communication done through stdin/stdout: Quite simple (depending of the OS security, any could already have access to process stdio without privilege escalation and/or exploit)
Access other process memory space: Possible but difficult
Wrap a binary (KeePassXC or the browser): Targeted attack = difficult but plausible

I disagree with this ranking of difficulties. Instead, I would ask where the security boundaries are. For KeePassXC, I would say that the defensible security boundary is "code execution on the local machine." If an attacker has local code exec, there is nothing KeePassXC can do to defend against it. Adding a layer of encryption or authentication to local process IPC merely adds complexity and UX friction, without adding significant security.

@varjolintu
Copy link
Member

There's a development version available at: chromeKeePassXC

The implementation is almost identical to chromeIPass except JSON data is changed to lower case. The random key generation is using tweetnacl-js and I'm also considering if it would be easier to use secretbox to encrypt the whole message instead of transferring the message in plain text with encrypted elements. This version is far from perfect, but it is a start.

To get this to work needs changes from KeePassXC side. Maybe it would be easiest to implement receiving a test-associate message first.
Examples available here.

The .json file should be installed to following locations (edit the path for KeePassXC or test client):

  • macOS (Chrome): $HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts
  • macOS (Chromium): $HOME/Library/Application Support/Chromium/NativeMessagingHosts
  • Linux (Chrome): $HOME/.config/google-chrome/NativeMessagingHosts
  • Linux (Chromium): $HOME/.config/chromium/NativeMessagingHosts

@varjolintu
Copy link
Member

varjolintu commented Apr 3, 2017

Some major changes made to the extension:

Transmitting messages between KeePassXC and chromeKeePassXC is totally rewritten. This is still under development.
Now the requests are encrypted by TweetNaCl.js box method and does the following:

  1. chromeKeePassXC generates a key pair (with public and secret key) and transfers the public key to KeePassXC
  2. When KeePassXC receives the public key it generates its own key pair and transfers the public key to chromeKeePassXC
  3. All messages (excluding get-databasehash) are now encrypted.
  4. When chromeKeePassXC sends a message it is encrypted with KeePassXC's public key, a random generated nonce and chromeKeePassXC's secret key.
  5. When KeePassXC sends a message it is encrypted with chromeKeePassXC's public key etc.
  6. Databases are stored based on the current public key used with associate. A new key pair for data transfer is generated each time chromeKeePassXC is launched.

@TheZ3ro
Copy link
Contributor

TheZ3ro commented Apr 3, 2017

What do you mean with

Databases are stored based on the database hash instead of the key because a new key pair is generated each time chromeKeePassXC is launched.

I personally don't really like adding many encryption algorithm all for different things. This will make HTTP code more complex and KeePassXC won't be compatible with KeePassHTTP anymore, unless we keep both new and old version

@varjolintu
Copy link
Member

In chromeIPass databases used with the plugin are stored with the id, hash and the key. In the future, only with id and hash because keys are always generated each time a database connection is being made with KeePassXC (or test host).

In chromeIPass the JSON message itself was unencrypted but each element was hashed or encrypted with AES. So instead of encrypting the items one by one only the message itself is encrypted. In this case there's actually less encryption.

And yes, this breaks the compatibility with KeePassHTTP but I think the compatibility remains with few changes to the KeePassXC side. I will try to test this by myself.

@TheZ3ro
Copy link
Contributor

TheZ3ro commented Apr 3, 2017

In chromeIPass the JSON message itself was unencrypted but each element was hashed or encrypted with AES. So instead of encrypting the items one by one only the message itself is encrypted. In this case there's actually less encryption.

I mean, we need to add new code for encryption algorithm in KeePassXC since chromeIPass uses AES (and we already needs it for decrypting the database). What algo are you using for NaCL box?

@varjolintu
Copy link
Member

I mean, we need to add new code for encryption algorithm in KeePassXC since chromeIPass uses AES (and we already needs it for decrypting the database). What algo are you using for NaCL box?

It uses the default, Curve25519-XSalsa20-Poly1305. I suppose it's pretty straight forward method to just include libsodium to the project.

@droidmonkey droidmonkey changed the title Consider implementing Native Messaging for browser extensions Implement Native Messaging for browser extensions Apr 4, 2017
@varjolintu
Copy link
Member

At this point I already have a working Qt test host with libsodium. The next step will be a test integration to KeePassXC. I'll try to maintain the compatibility with KeePassHTTP.

@varjolintu
Copy link
Member

varjolintu commented Apr 23, 2017

It's already possible to connect chromeKeePassXC to a database, use the password generator etc.. Still need a lot of work. But it's moving forward in tiny steps.

You can keep track of the process through these branches:
https://github.com/varjolintu/keepassxc/tree/feature/chromeKeePassXC
https://github.com/varjolintu/chromeKeePassXC

@varjolintu
Copy link
Member

Currently you can use the following releases to test the Native Messaging feature:
chromeKeePassXC 0.1.1
KeePassXC 2.1.4-browser

Be sure the extension ID matches with the installed JSON file. See this guide for further information.

All the chromeIPass features are now working. The extension itself is not yet finished (mostly the popup status display) and the KeePassXC side also needs some work.

If you want to build the branch by yourself use -DWITH_XC_BROWSER option with cmake. Also, boost and libsodium libraries must be installed.

Any comments or suggestions are appreciated. One big question is the boost dependency. Only library needed is the boost_system (for asio). If boost dependency is a big no it's always possible just include the plain asio c++ library instead.

@varjolintu
Copy link
Member

Latest releases:
chromeKeePass 0.1.3
KeePassXC 2.1.4-browser2

@phoerious
Copy link
Member

Thanks for the work. We should review this soon and then decided to how to proceed with upstream merging.

@varjolintu
Copy link
Member

I'm also trying to make the extension Firefox WebExtension compatible. The extension already loads but the functionality is still limited. Please notice that I'm using Firefox Nightly for testing the extension. The stable version is not up-to-date enough.

@varjolintu
Copy link
Member

varjolintu commented May 28, 2017

Latest releases:
chromeKeePass 0.1.7
KeePassXC 2.1.4-browser-beta1

The browser extension works with Firefox (nightly) also without any major problems. The KeePassXC branch is now merged with develop (was behind around 400 commit.. oops).

The single instance feature didn't change the way Native Messaging works, and this is the biggest "turn-off". The KeePassXC process much be opened with the browser instance. If it is open before opening the browser Native Messaging cannot find the KeePassXC host. This is really bad, and it what makes it worse is that when closing the browser KeePassXC is closed also (and it doesn't even prompt you for saving unchanged stuff.. just closes). But if you use KeePassXC just with a browser, this almost works as it should.

Edit:
I managed to implement a feature that allows relaunching of KeePassXC straight from the popup in case it has been exited.

@droidmonkey
Copy link
Member

The only fix for this is to create a small application that acts as a proxy between chrome/firefox and the running instance of keepasxc. This might be relatively easy to do, the proxy would simply pass the stdio to the running keepassxc instance as-is.

@varjolintu
Copy link
Member

The only fix for this is to create a small application that acts as a proxy between chrome/firefox and the running instance of keepasxc. This might be relatively easy to do, the proxy would simply pass the stdio to the running keepassxc instance as-is.

The problem is that Native Messaging needs to launch a new process. It cannot connect to a process that exists. So QProcess is out of the game. Maybe QLocalSocket could be the answer. I should definitely investigate this approach.

@droidmonkey
Copy link
Member

Yah, the proxy application gets created at-will by the plugin while the main keepassxc instance stays running.

@varjolintu
Copy link
Member

I have been experimenting with a proxy application which uses UDP sockets on localhost to transfer messages. It already works pretty well but still needs some work.

The next version of the browser extension will be renamed to keepassxc-browser. One good reason for this is that Mozilla doesn't allow browser extensions which carry a name of another browser.

@TheZ3ro
Copy link
Contributor

TheZ3ro commented Jun 14, 2017

@varjolintu Nice, you are doing a great work! 😉

@lofidevops
Copy link

From #259

With the release of KeePassXC-Browser I believe this can be closed.

...same here?

@varjolintu
Copy link
Member

Yes. Let's close this. Many information here is outdated.

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

No branches or pull requests

8 participants