DEV Community

Cover image for Debugging PubNub's encryption hole
Sriram
Sriram

Posted on

Debugging PubNub's encryption hole

Introduction
I work as a Lead Product Engineer with MiniCorp, where we work towards bringing our customer's ideas to life. In the recent past we have seen a surge in the number of customers requiring in-app chat within their product.

In-App Chat
This is the age of SaaS / API, where there are ton of options for each niche. These products provide a complete package which would take months for us to build from scratch. In our search for In-app chat, 2 products stood out the most. TalkJS & PubNub

We use both these products depending on the client's requirements. While TalkJS doesn't provide a free version, PubNub offers a limited free version. While in the long run TalkJS's price model is easy to maintain, for clients who prefer to launch their MVP, PubNub's free version makes more sense.

PubNub's Encryption Claim
One of our recent clients had the requirement that all chat messages & attachments should be end-to-end encrypted and no unencrypted data should be stored on the server. We went ahead and started building the product with PubNub as they claim to have built-in encryption support

Here's the screenshot of PubNub docs showing file encryption via the SDK
Screenshot from PubNub docs at the time of writing

Implementation
We implemented the chat on our Ruby on Rails app with Javascript SDK. Everything worked as expected and we kick started our next phase of development which was building react native app.

PubNub provides a neat react SDK which is a wrapper on their Javascript SDK. As usual we were testing our implementation with plain text messages and everything worked as expected. The entire chat functionality was built & everyone was happy until we noticed a key detail.

The files uploaded were stored on PubNub's servers and we were assuming that all files were encrypted. To our rude shock, when we used the getFileUrl method and opened the link on a tab, we saw the unencrypted version of the file (hosted on PubNub's S3 account)

Analysis
We were so sure that we did some mistake, because files uploaded through the web version were getting encrypted properly. We were unsuccessful in our attempts and contacted the PubNub support. The support assured us that other clients were infact using the SDK and encryption was working for them.
enter image description here

Since we couldn't find any issue with our code, I decided to dig into PubNub's SDK source code.

1. Digging into sendFile method
We started with the public method that was mentioned in the document.
https://github.com/pubnub/javascript/blob/master/src/core/endpoints/file_upload/send_file.js#L64

if (PubNubFile.supportsEncryptFile && (cipherKey ?? config.cipherKey)) {
  file = await cryptography.encryptFile(cipherKey ?? config.cipherKey, file, PubNubFile);
}
Enter fullscreen mode Exit fullscreen mode

The 1st clue was looking at the PubNubFile.supportsEncryptFile boolean. It was a surprise seeing such a variable.

2. supportsEncryptFile boolean
I did a quick search of this variable name within the SDK repository. This threw an interesting finding that the variable was true for web & node, but false for react-native

static  supportsEncryptFile  =  false;
Enter fullscreen mode Exit fullscreen mode

3. Contacting support
With this interesting finding, I reached out to PubNub support. And the response was that I needed to encrypt the file with the JS utility method before using the sendFile method.
PubNub support 1

PubNub support 2

This was unexpected & nowhere mentioned in their docs. As we saw in the above snippet, sendFile method internally calls the encryptFile method if cipherText is provided.

4. Implementing encryptFile

Inspite of the above, I decided to give it a try. When I called the utility method directly, I ended up getting the following error

TypeError: undefined is not an object (evaluating 'cryptography.encryptFile')

5. Understanding the encryptFile method
While I assumed that manually encrypting the file would work, this was another surprise to see that the method could not be found. So I decided to dig deeper into the encryptFile method.

https://github.com/pubnub/javascript/blob/master/src/core/pubnub-common.js#L379

this.encryptFile  =  (key,  file)  =>  cryptography.encryptFile(key,  file,  this.File);
Enter fullscreen mode Exit fullscreen mode

https://github.com/pubnub/javascript/blob/master/src/core/pubnub-common.js#L358

const  cryptography  =  setup.cryptography;
Enter fullscreen mode Exit fullscreen mode

Here I noticed that the encryptFile method is a nice interface wrapper on the cryptography module. The cryptography module itself is an attribute to the setup, which is the constructor arugument.

cryptography variable is initialized on each module constructor depending on the platform

Web
https://github.com/pubnub/javascript/blob/master/src/web/index.js#L101

setup.cryptography  =  new  WebCryptography();
Enter fullscreen mode Exit fullscreen mode

Node
https://github.com/pubnub/javascript/blob/master/src/node/index.js#L23

setup.cryptography  =  new  NodeCryptography();
Enter fullscreen mode Exit fullscreen mode

And, this was not initialized on the react-native module
https://github.com/pubnub/javascript/blob/88027bb862608796a6648f175e88c6b7bb6030f3/src/react_native/index.js

Conclusion
As I mentioned before, working with 3rd party tools really fast tracks the development time & enables us to go-to-market sooner than if we built everything. But the trade-off is not having control when something doesn't work as expected.

Luckily PubNub has an open source repository which allowed us to dive deeper into this issue. If we had known the lack of encryption support for react native SDK, we could have taken a different call.

We have written to PubNub's support with all our above findings and hoping for a fix soon. I will update the post as & when I hear more updates from PubNub team.

Top comments (0)