diff --git a/content/blog/2020/07/2020-07-03--keybase-website.md b/content/blog/2020/07/2020-07-03--keybase-website.md new file mode 100644 index 0000000..43e9d7d --- /dev/null +++ b/content/blog/2020/07/2020-07-03--keybase-website.md @@ -0,0 +1,143 @@ +--- +title: "State of the Keybase website" +author: Yarmo Mackenbach +slug: keybase-website +date: "2020-07-03 14:00:00" +published: true +--- + +## Disclaimer + +Two days ago, I launched [Keyoxide.org](https://keyoxide.org) which provides a few similar functions as [Keybase.io](https://keybase.io) but in an Open Source package. I've been wanting to write this post for a while but felt it could be perceived as disingenuous if posted before making my own project public. Therefore, I post this now. + +## The Keybase.io website + +I have opinions about the Keybase service, but this post is not about that. This is about the facts behind their website, [Keybase.io](https://keybase.io) and more specifically their [encrypt](https://keybase.io/encrypt) page, the one you use to **encrypt private and confidential messages**. + +When you load that specific page, make sure to load it in a private session or window to eliminate cached resources. What do you notice? + +It is slow. Really slow. I noticed so too and decided to run a [Webpagetest (link to result)](https://www.webpagetest.org/result/200627_0Q_044080ef3ab8a678721658c90d2f4706/). Out of three runs, we analyze only the median run (so not the best one, not the worst one). + +![Keybase encrypt Webpagetest overview](/content/img/keybase_encrypt__wpt_overview.png) +*keybase.io/encrypt* + +## The content loaded + +It takes **6.25 seconds** to fully load the **2.9 megabytes** that are used on this page. That is hefty for a page that is essentially a single form. I mean, look at it: + +![Keybase encrypt page](/content/img/keybase_encrypt.png) +*Why 2.9 megabytes?* + +That's a regular web form. What could possibly be **2.9 megabytes**? The javascript? + +![Webpagetest run 1 overview](/content/img/keybase_encrypt__wpt_1.png) +*How many requests? How many bytes?* + +Most requests are fonts. That makes sense. Earlier, we saw the page only makes **12 requests**, so I could imagine a few of those being several fonts files. Fortunately, fonts are only **6.5%** of the bytes loaded, so we'll forgive them. + +**90 percent** of the bytes are due to javascript and images‽ That's **2.6 megabytes** for a form! What images? + +## Javascript and image(s) + +Let's grab the [waterfall](https://www.webpagetest.org/result/200627_0Q_044080ef3ab8a678721658c90d2f4706/1/details/#waterfall_view_step1) and see what is going on: + +![Webpagetest run 1 waterfall](/content/img/keybase_encrypt__wpt_1_waterfall.png) +*Run 1 waterfall* + +At two points in time, the loading of the website stalls. The first stall is **2.6 seconds** for the file `sitewide-js.js`. The second stall is **2.5 seconds** for the file `footprints_transp.png`. Let's go. + +## sitewide-js.js + +This file is **4.7 megabytes** raw and **1.2 megabytes** gzipped. Let us look at a random excerpt: + +![Javascript excerpt](/content/img/keybase_encrypt__js_excerpt.png) +*Javascript excerpt* + +Content aside, this is not really optimized for performance: one could choose to minimize the javascript. Allow me to use `@node-minify/cli`. + +`JS_Parse_Error [SyntaxError]: Unexpected token: name «syms», expected: punc «;»` + +Hmm… Let's remove that one line which simply initializes a variable (only fix I could find). I can no longer guarantee it works but let's assume it does. + +Old version: **4.7 megabytes** raw and **1.2 megabytes** gzipped. +New version: **2.5 megabytes** raw and **0.7 megabytes** gzipped. + +First win! + +Well, I need to specify one thing: the website loads a gzipped version of the original file at a size of **1.23 megabytes**. When I `gzip` it on my local machine, the original file even becomes **1 megabytes**. I don't know what causes this discrepancy, but while we were able to reduce the raw files by 2.2 megabytes, the reduction could only become 0.3 megabytes once gzipped (on my machine™). + +## footprints_transp.png + +Have you found the image yet? It's the little image at the bottom of the dog (?) following footprints. Cute :) + +![Footprints image](/content/img/keybase_encrypt__img.png) +*Footprints image* + +Dimensions on page: **330 x 90 pixels** +Dimensions of file: **2836 x 770 pixels** + +That's only **8.6 times** larger than it needs to be. The bigger crime is the size: **1.4 megabytes**. Which is 50% of the website. You guessed it. This could be better. + +Using [imagecompressor.com](https://imagecompressor.com/), I can compress this full-sized image down to **398 kilobytes** (reduction of **71%**). And I'm even allowing the full 256 colors. And the dimensions are still **8.6 times** larger than they need to be. + +Optimizing the compression and the image dimensions could yield even better results. I'm not going to bother. The devs didn't either. + +Still, second win! + +## Anything else we can learn? + +The source code contains this: + +``` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + K E Y B A S E + + crypto for everyone + + because no one we know ever + seems to have a public key. :-( + + No Google Analytics or other 3rd party hosted script tags on Keybase. + + And this has the added bonus that we'll never be able to serve ad code. + + \o/ \o/ + keybase team + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +``` + +I like it when devs get creative. The third paragraph is a bit alienating, but sure. + +Anything else? Given that this is all cryptography related, maybe some security related issues? + +## Security + +![Webpagetest security score](/content/img/keybase_encrypt__security.png) +*Webpagetest security score* + +I've ran quite a few webpagetests on different website, but a **0** security score is new to me. What does that even mean? + +Well, [it turns out](https://snyk.io/test/website-scanner/?test=200627_0Q_044080ef3ab8a678721658c90d2f4706) that the entire website is built using the following libraries: + +- jquery v1.11.3 (from [april 2015](https://blog.jquery.com/2015/04/28/jquery-1-11-3-and-2-1-4-released-ios-fail-safe-edition/)) +- bootstrap v3.3.5 (from [june 2015](https://blog.getbootstrap.com/2015/06/15/bootstrap-3-3-5-released/)) +- moment v2.7.0 (from [june 2014](https://github.com/moment/moment/releases/tag/2.7.0)) + +Besides the obvious aging, these libraries account for a total of six known and public security vulnerabilities, including [cross-site scripting](https://snyk.io/vuln/SNYK-JS-BOOTSTRAP-72890), [prototype pollution](https://snyk.io/vuln/SNYK-JS-JQUERY-174006) and [regular expression denial of service](https://snyk.io/vuln/npm:moment:20161019). All six security vulnerabilities have remediations. + +Let the [bootstrap v3.3.5 announcement](https://blog.getbootstrap.com/2015/06/15/bootstrap-3-3-5-released/) be a painful reminder: this is *pushing it*. + +## Wrapping up + +This should give a nice overview of what could go wrong with a non-optimized and aging website. This could happen to any website. But this is Keybase, the company that promises **"secure messaging and file-sharing"**. The same company that got [$10.8 million in a Serie A funding](https://www.crunchbase.com/organization/keybase). The same company that [won't allow us to see their server code](https://github.com/keybase/client/issues/24105). + +To paint a full and fair picture, there has been an [audit of the Keybase protocol [PDF]](https://keybase.io/docs-assets/blog/NCC_Group_Keybase_KB2018_Public_Report_2019-02-27_v1.3.pdf) which states that: + +> [...] there were weaknesses in the Keybase implementation; these were quickly fixed. + +The audit didn't include the website. I'll just end with another quote from the same audit: + +> Another common theme was the presence of legacy code. [...] +> This does not necessarily imply that legacy code is insecure, but complexity and security are intertwined – every new piece of code may contain a security vulnerability, and more code correlates with more risk. diff --git a/content/img/keybase_encrypt.png b/content/img/keybase_encrypt.png new file mode 100644 index 0000000..6c4bcb9 Binary files /dev/null and b/content/img/keybase_encrypt.png differ diff --git a/content/img/keybase_encrypt__img.png b/content/img/keybase_encrypt__img.png new file mode 100644 index 0000000..d800cd3 Binary files /dev/null and b/content/img/keybase_encrypt__img.png differ diff --git a/content/img/keybase_encrypt__js_excerpt.png b/content/img/keybase_encrypt__js_excerpt.png new file mode 100644 index 0000000..354be25 Binary files /dev/null and b/content/img/keybase_encrypt__js_excerpt.png differ diff --git a/content/img/keybase_encrypt__security.png b/content/img/keybase_encrypt__security.png new file mode 100644 index 0000000..fd2917c Binary files /dev/null and b/content/img/keybase_encrypt__security.png differ diff --git a/content/img/keybase_encrypt__wpt_1.png b/content/img/keybase_encrypt__wpt_1.png new file mode 100644 index 0000000..5358844 Binary files /dev/null and b/content/img/keybase_encrypt__wpt_1.png differ diff --git a/content/img/keybase_encrypt__wpt_1_waterfall.png b/content/img/keybase_encrypt__wpt_1_waterfall.png new file mode 100644 index 0000000..20215d0 Binary files /dev/null and b/content/img/keybase_encrypt__wpt_1_waterfall.png differ diff --git a/content/img/keybase_encrypt__wpt_overview.png b/content/img/keybase_encrypt__wpt_overview.png new file mode 100644 index 0000000..676e849 Binary files /dev/null and b/content/img/keybase_encrypt__wpt_overview.png differ