Posted on by Wladimir Palant

It’s this time of year again, sending emails from Thunderbird fails with an error message:

Expired certificate message in Thunderbird

The certificates I use to sign my emails have expired. So I once again need to go through the process of getting replacements. Or I could just give up on email signing and encryption. Right now, I am leaning towards the latter.

Why did I do it in the first place?

A while back, I used to communicate a lot with users of my popular open source project. So it made sense to sign emails and let people verify — it’s really me writing. It also gave people a way to encrypt their communication with me.

The decision in favor of S/MIME rather than PGP wasn’t because of any technical advantage. The support for S/MIME is simply built into many email clients by default, so the chances that the other side would be able to recognize the signature were higher.

How did this work out?

In reality, I had a number of confused users asking about that “attachment” I sent them. What were they supposed to do with this smime.p7s file?

Over the years, I received mails from more than 7000 email addresses. Only 72 signed their emails with S/MIME, 52 used PGP to sign. I only exchanged encrypted mails with one person.

What’s the point of email signing?

The trouble is, signing mails is barely worth it. If somebody receives an unsigned mail, they won’t go out of their way to verify the sender. Most likely, they won’t even notice, because humans are notoriously bad at recognizing the absence of something. But even if they do, unsigned is what mails usually look like.

Add to this that the majority of mail users are using webmail now. So their email clients have no support for either S/MIME or PGP. Nor is it realistic to add this support without introducing a trusted component such as a browser extension. But with people who didn’t want to install a dedicated email client, how likely are they to install this browser extension even if a trustworthy solution existed?

Expecting end users to take care of sender verification just isn’t realistic. Instead, approaches like SPF or DKIM emerged. While these aren’t perfect and expect you to trust your mail provider, fake sender addresses are largely a solved issue now.

Wouldn’t end-to-end encryption be great?

Now we know of course about state-level actors spying on the internet traffic, at least since 2013 there is no denying. So there has been tremendous success in deprecating unencrypted HTTP traffic. Shouldn’t the same be done for emails?

Sure, but I just don’t see it happen by means of individual certificates. Even the tech crowd is struggling when it comes to mobile email usage. As to the rest of the world, good luck explaining them why they need to jump through so many hoops, starting with why webmail is a bad choice. In fact, we considered rolling out email encryption throughout a single company and had to give up. The setup was simply too complicated and limited the possible use cases too much.

So encrypting email traffic is now done by enabling SSL in all those mail relays. Not really end-to-end encryption, with the mail text visible on each of those relays. Not entirely safe either, as long as the unencrypted fallback still exists — an attacker listening in the middle can always force the mail servers to fall back to an unencrypted connection. But at least passive eavesdroppers will be dealt with.

But what if S/MIME or PGP adoption increases to 90% of the population?

Good luck with that. As much as I would love to live in this perfect world, I just don’t see it happen. It’s all a symptom of the fact that security is bolted on top of email. I’m afraid, if we really want end-to-end encryption we’ll need an entirely different protocol. Most importantly, secure transmissions should be the default rather than an individual choice. And then we’ll only have to validate the approach and make sure it’s not a complete failure.

Categories: Comment [4]

Posted on by Wladimir Palant

TL;DR: The answer to the question in the title is most likely “no.” While the OPAQUE protocol is a fascinating approach to authentication, for web applications it doesn’t provide any security advantages.

I read an interesting post by Matthew Green where he presents ways to authenticate users by password without actually transmitting the password to the server, in particular a protocol called OPAQUE. It works roughly like that:

The server has the user’s salt and public key, the client knows the password. Through application of some highly advanced magic, a private key materializes in the client, matching the public key known to the server. This only works if the password known to the client is correct, yet the client doesn’t learn the salt and the server doesn’t learn the password in the process. From that point on, the client can sign any requests sent to the server, and the server can verify them as belonging to this user.

The fact that you can do it like this is amazing. Yet the blog post seems to suggest that websites should adopt this approach. I wrote a comment mentioning this being pointless. The resulting discussion with another commenter made obvious that the fundamental issues of browser-based cryptography that I first saw mentioned in Javascript Cryptography Considered Harmful (2011) still aren’t widely known.

What are we protecting against?

Before we can have a meaningful discussion on the advantages of an approach we need to decide: what are the scenarios we are protecting against? In 2018, there is no excuse for avoiding HTTPS, so we can assume that any communication between the client and the server is encrypted. Even if the server receives the password in clear text, a responsible implementation will always hash the password before storing it in the database. So the potential attacks seem to be:

  • The server is compromised, either because of being hacked or as an inside job. So the attackers already have all the data, but they want to have your password as well. The password is valuable to them either because of password reuse (they could take over accounts on other services) or because parts of the data are encrypted on the server and the password is required for decryption. So they intercept the password as it comes in, before it is hashed.
  • Somebody succeeded with a Man-in-the-Middle attack on your connection, despite HTTPS. So they can inspect the data being sent over the connection and recover your password in particular. With that password they can log into your account themselves.
  • A rather unlikely scenario: a state-level actor recorded the (encrypted) contents of your HTTPS connection and successfully decrypted them after a lengthy period of time. They can now use your password to log into your account.

Does OPAQUE help in these scenarios?

With OPAQUE, the password is never sent to the server, so it cannot be intercepted in transit. However, with web applications the server controls both the server and the client side. So all it has to do is giving you a slightly modified version of its JavaScript code on the login page. That code can then intercept the password as you enter it into the login form. The user cannot notice this manipulation, with JavaScript code often going into megabytes these days, inspecting it every time just isn’t possible. Monitoring network traffic won’t help either if the data being sent is obfuscated.

This is no different with the Man-in-the-Middle attack, somebody who managed to break up your HTTPS connection will also be able to modify JavaScript code in transit. So OPAQUE only helps with the scenario where the attacker has to be completely passive, typically because they only manage to decrypt the data after the fact. With this scenario being extremely uncommon compared to compromised servers, it doesn’t justify the significant added complexity of the OPAQUE protocol.

What about leaked databases?

Very often however, the attackers will not compromise a server completely but “merely” extract its database, e.g. via an SQL injection vulnerability. The passwords in this database will hopefully be hashed, so the attackers will run an offline brute-force attack to extract the original passwords: hash various guesses and test whether the resulting hash matches the one in the database. Whether they succeed depends largely on the hashing function used. While storing passwords hashed with a fast hashing function like SHA-1 is only marginally better than storing passwords as clear text, a hashing function that is hard to speed up such as scrypt or argon2 with well-chosen parameters will be far more resilient.

It is a bit surprising at first, but using OPAQUE doesn’t really change anything here. Even though the database no longer stores the password (not even as a hash), it still contains all the data that attackers would need to test their password guesses. If you think about it, there is nothing special about the client. It doesn’t know any salts or other secrets, it only knows the password. So an attacker could just do everything that the client does to test a password guess. And the only factor slowing them down is again the hashing function, only that with OPAQUE this hashing function is applied on the client side.

In fact, it seems that OPAQUE might make things worse in this scenario. The server’s capability for hashing is well-known. It is relatively easy to tell what parameters will be doable, and it is also possible to throw more hardware at the problem if necessary. But what if hashing needs to be done on the client? We don’t know what hardware the client is running, so we have to assume the worst. And the worst is currently a low-end smartphone with a browser that doesn’t optimize JavaScript well. So chances are that a website deploying OPAQUE will choose comparatively weak parameters for the hashing function rather than risk some users to be upset about extensive delays.

Can’t OPAQUE be built into the browser?

Adding OPAQUE support to the browsers would address a part of the performance concerns. Then again, browsers that would add this feature should have highly-optimized JavaScript engines and Web Crypto API already. But the fundamental issue is passwords being entered into untrusted user interface, so the browser would also have to take over querying the password, probably the way it is done for HTTP authentication (everybody loves those prompts, right?). A compromised web server could still show a regular login form instead, but maybe the users will suspect something then? Yeah, not likely.

But wait, there is another issue. The attacker in the Man-in-the-Middle scenario doesn’t really need your password, they merely need a way to access your account even after they got out of your connection. The OPAQUE protocol results in a private key on the client side, and having that private key is almost as good as having the password — it means permanent access to the account. So the browser’s OPAQUE implementation doesn’t merely have to handle the password entry, it also needs to keep the private key for itself and sign requests made by the web application to the server. Doable? Yes, should be. Likely to get implemented and adopted by websites? Rather questionable.

Categories: Comment [2]

Posted on by Wladimir Palant

A few days ago Google announced ensuring privacy for your Android data backups. The essence is that your lockscreen PIN/pattern/passcode is used to encrypt your data and nobody should be able to decrypt it without knowing that passcode. Hey, that’s including Google themselves! Sounds good? Past experience indicates that such claims should not always be taken at face value. And in fact, this story raises some red flags for me.

The trouble is, whatever you use on your phone’s lockscreen is likely not very secure. It doesn’t have to be, because the phone will lock up after a bunch of failed attempts. So everybody goes with a passcode that is easy to type but probably not too hard to guess. Can you derive an encryption key from that passcode? Sure! Will this encryption be unbreakable? Most definitely not. With passwords being that simple, anybody getting their hands on encrypted data will be able to guess the password and decrypt the data within a very short time. That will even be the case for a well-chosen key derivation algorithm (and we don’t know yet which algorithm Google chose to use here).

Google is aware of that of course. So they don’t use the derived encryption key directly. Instead, the derived encryption key is used to encrypt a proper (randomly generated) encryption key, only the latter being used to encrypt the data. And then they find themselves in trouble: how could one possibly store the encryption key securely? On the one hand, they cannot keep it on user’s device because data might be shared between multiple devices. On the other hand, they don’t want to upload the key to their servers either, because of how unreliable the encryption layer on top of it is — running a bruteforce attack to extract the actual encryption key would be trivial even without having Google’s resources.

So they used a trick. The encryption key isn’t uploaded to a Google server, it is uploaded to a Titan security chip on a Google server. Presumably, your Android device will establish an encrypted connection directly to that Titan chip, upload your private key and the Titan chip will prevent bruteforce attacks by locking up after a few attempts at guessing your passcode. Problem solved?

Not quite. First of all, how do you know that whatever your Android device is uploading the private key to is really a Titan chip and not a software emulation of it? Even if it is, how do you know that it is running unmodified firmware as opposed to one that allows extracting data? And how do you know that Google really has no means of resetting these chips without all data being cleared? It all boils down to: you have to trust Google. In other words: it’s not that Google cannot access your data, they don’t want to. And you have to take their word on it. You also have to trust them when they claim that the NSA didn’t force them into adding a backdoor to those Titan chips.

Don’t take me wrong, they probably produced the best solution given what they have to work with. And for most Android users, their solution should still be a win, despite the shortcomings. But claiming that Google can no longer access users’ backup data is misleading.

Categories: Comment [0]

Posted on by Wladimir Palant

Two days ago I decided to take a look at Keybase. Keybase does crypto, is open source and offers security bug bounties for relevant findings — just the perfect investigation subject for me. It didn’t take long for me to realize that their browser extension is deeply flawed, so I reported the issue to them via their bug bounty program. The response was rather… remarkable. It can be summed up as: “Yes, we know. But why should we care?” Turns out, this is a common response, see update at the bottom.

What is Keybase?

The self-description of Keybase emphasizes its secure end-to-end encryption (emphasis in original):

Imagine a Slack for the whole world, except end-to-end encrypted across all your devices. Or a Team Dropbox where the server can’t leak your files or be hacked.

So the app allows you to exchange messages or files with other people, with the encryption happening on sender’s computer in such a way that decryption is only possible by the designated recipient. This app is available for both desktop and mobile platforms. And for desktop you get a bonus: you can install the Keybase browser extension. It will add a “Keybase Chat” button to people’s profiles on Facebook, Twitter, GitHub, Reddit or Hacker News. This button allows you to connect to people easily.

Keybase chat button on Github

Clicking the button will open a chat window and allow you to enter a message directly in the browser. Only after that initial message is sent the conversation will be transferred to the Keybase app.

So what’s the issue?

The issue here is a very common one, merely a week ago I listed it as #6 in this article. The extension injects its user interface (the button and the chat window) into third-party websites, yet it fails to isolate it from these websites. So the first consequence is: the Keybase message you enter on Facebook is by no means private. Facebook’s JavaScript code can read it out as you type it in, so much for end-to-end encryption. This is quite contrary to the promise Keybase still makes on their Mozilla Add-ons and Chrome Web Store installation pages.

Keybase promises on the install page

Don’t believe that Facebook would intentionally spy on you? Maybe not, but by now it is pretty common to protocol all of user’s actions, for “site optimization” purposes — this includes anything entered into text fields of course. But in my opinion, that’s not even the worst issue.

A website could do more than passively spying on you. It could just as well instrument the Keybase user interface in order to send messages in your name, while also making this user interface invisible so that you don’t notice anything. Why would Facebook want to do something like that? Not necessary them, rather anybody who discovered a Cross-Site Scripting (XSS) vulnerability in one of the websites that Keybase integrates with. So if hundreds of people complain about you sending them spam messages via Keybase, it might be somebody exploiting the Keybase extension on your computer via an XSS vulnerability in Reddit. Have fun explaining how you didn’t do it, even though the messages were safely encrypted on your computer.

What does Keybase think about this?

According to Keybase, “this is all clearly described on the install page and is known.” In fact, close to the bottom of that page you find the following:

What if my browser is compromised?

The Keybase extension uses a compose box inside your browser. If you fear your browser or the social network site’s JavaScript has been compromised — say by another extension or even the social network acting fishy — then just compose the message inside the Keybase app directly. Or send a quick hello note through the extension and save the jucier private details for inside the app.

To me, this is thoroughly confusing. First of all, “browser is compromised” to me sounds more like malware. Trouble is, malware affecting the browser will affect the Keybase app just as well, so the advise makes no sense. But let’s say that it really is “the social network acting fishy,” how are you supposed to know? And is Facebook spying on you “fishy” or just its usual self?

It’s not that this issue is unavoidable. Avoiding it is fairly easy, by isolating all of the extension’s user interface in an <iframe> element. This would prevent both the website and other extensions from accessing it. Disaster averted, nothing to see here. But according to Keybase:

there were technical reasons why iframes didn’t work, though I forget the details

I translate this as: “Using iframes required a slightly more complicated approach, so we couldn’t figure it out.” Also:

It’s such a minor feature for us, it’s not worth a fix.

I translate this as: “We will keep pushing this extension because it gets users to promote our app for free. But we don’t care enough to make it secure.”

And now?

The only advise I can give you: uninstall the Keybase browser extension ASAP. As to the app itself, it might be secure. But as experience shows, the claim “end-to-end encryption” doesn’t automatically translate into a secure implementation. Initially, I planned to take a closer look at the crypto in Keybase, to see whether I can find weaknesses in their implementation. But that’s off the table now.

Update (2018-09-10): After I wrote this, EdOverflow pointed out that he made a similar experience with Keybase in the past. He could demonstrate that the domain ownership validation approach used by Keybase is flawed, yet Keybase wasn’t really interested in fixing this issue. Why they don’t require their keybase.txt file to be always located within the .well-known/ directory is beyond me, it solves the security issue here without any obvious downsides.

And then I also found this older vulnerability report on HackerOne about the Keybase extension opening up XSS issues on websites. The reporter recommended staying clear of innerHTML and using safe DOM methods instead, something that I have also been preaching for years. The response he received sounded very familiar:

There was some reason our extension developer decided against that approach, though he agrees it’s better in theory.

In other words: “We don’t know how to do it, but we’ll claim that we have a good reason instead of asking for help.”

Update (2018-11-07): I now requested explicitly that Keybase makes my report on HackerOne public, and they promptly rejected. I’m not sure which part of my report they consider a secret, given that they consider it a known and documented issue. Maybe it’s merely our conversation that they don’t want to be public?

Categories: Comment [5]

Posted on by Wladimir Palant

Dear developers of password managers, we communicate quite regularly, typically within the context of security bug bounty programs. Don’t get me wrong, I don’t mind being paid for finding vulnerabilities in your products. But shouldn’t you do your homework before setting up a bug bounty program? Why is it the same basic mistakes that I find in almost all password managers? Why is it that so few password managers get AutoFill functionality right?

Of course you want AutoFill to be part of your product, because from the user’s point of view it’s the single most important feature of a password manager. Take it away and users will consider your product unusable. But from the security point of view, filling in passwords on the wrong website is almost the worst thing that could happen. So why isn’t this part getting more scrutiny? There is a lot you can do, here are seven recommendations for you.

1. Don’t use custom URL parsers

Parsing URLs is surprisingly complicated. Did you know that the “userinfo” part of it can contain an @ character? Did you think about data: URLs? There are many subtle details here, and even well-established solutions might have corner cases where their parser produces a result that’s different from the browser’s. But you definitely don’t want to use a URL parser that will disagree with the browser’s — if the browser thinks that you are on malicious.com then you shouldn’t fill in the password for google.com no matter what your URL parser says.

Luckily, there is an easy solution: just use the browser’s URL parser. If you worry about supporting very old browsers, the same effect can be achieved by creating an <a> element and assigning the URL to be parsed to its href property. You can then read out the link’s hostname property without even adding the element to the document.

2. Domain name is not “the last two parts of a host name”

Many password managers will store passwords for a domain rather than an individual host name. In order to do this, you have to deduce the domain name from the host name. Very often, I will see something like the old and busted “last two parts of a host name” heuristic. It works correctly for foo.example.com but for foo.example.co.uk it will consider co.uk to be the domain name. As a result, all British websites will share the same passwords.

No amount of messing with that heuristic will save you, things are just too complicated. What you need is the Public Suffix List, it’s a big database of rules which can be applied to all top-level domains. You don’t need to process that list yourself, there is a number of existing solutions for that such as the psl package.

3. Don’t forget about raw IP addresses

Wait, there is a catch! The Public Suffix List will only work correctly for actual host names, not for IP addresses. If you give it something like 192.168.0.1 you will get 0.1 back. What about 1.2.0.1? Also 0.1. If your code doesn’t deal with IP addresses separately, it will expose passwords for people’s home routers to random websites.

What you want is recognizing IP addresses up front and considering the entire IP address as the “domain name” — passwords should never be shared between different IP addresses. Recognizing IP addresses is easier said that done however. Most solutions will use a regular expression like /^\d{1-3}\.\d{1-3}\.\d{1-3}\.\d{1-3}$/. In fact, this covers pretty much all IPv4 addresses you will usually see. But did you know that 0xC0.0xA8.0x00.0x01 is a valid IPv4 address? Or that 3232235521 is also an IPv4 address?

Things get even more complicated once you add IPv6 addresses to the mix. There are plenty of different notations to represent an IPv6 address as well, for example the last 32 bits of the address can be written like an IPv4 address. So you might want to use an elaborate solution that considers all these details, such as the ip-address package.

4. Be careful with what host names you consider equivalent

It’s understandable that you want to spare your users disappointments like “I added a password on foo.example.com, so why isn’t it being filled in on bar.example.com?” Yet you cannot know that these two subdomains really share the same owner. To give you a real example, foo.blogspot.com and bar.blogspot.com are two blogs owned by different people, and you certainly don’t want to share passwords between them.

As a more extreme example, there are so many Amazon domains that it is tempting to just declare: amazon.<TLD> is always Amazon and should receive Amazon passwords. And then somebody goes there and registers amazon.boots to steal people’s Amazon passwords.

From what I’ve seen, the only safe assumption is that the host name with www. at the beginning and the one without are equivalent. Other than that, assumptions tend to backfire. It’s better to let the users determine which host names are equivalent, while maybe providing a default list populated with popular websites.

5. Require a user action for AutoFill

And while this might be a hard sell with your marketing department: please consider requiring a user action before AutoFill functionality kicks in. While this costs a bit of convenience, it largely defuses potential issues in the implementation of the points above. Think of it as defense in the depth. Even if you mess up and websites can trick your AutoFill functionality into thinking that they are some other website, requiring a user action will still prevent the attackers from automatically trying out a huge list of popular websites in order to steal user’s credentials for all of them.

There is also another aspect here that is discussed in a paper from 2014. Cross-Site Scripting (XSS) vulnerabilities in websites are still common. And while such a vulnerability is bad enough on its own, a password manager that fills in passwords automatically allows it to be used to steal user’s credentials which is considerably worse.

What kind of user action should you require? Typically, it will be clicking on a piece of trusted user interface or pressing a specific key combination. Please don’t forget checking event.isTrusted, whatever event you process should come from the user rather than from the website.

6. Isolate your content from the webpage

Why did I have to stress that the user needs to click on a trusted user interface? That’s because browser extensions will commonly inject their user interface into web pages and at this point you can no longer trust it. Even if you are careful to accept only trusted events, a web page can manipulate elements and will always find a way to trick the user into clicking something.

Solution here: your user interface should always be isolated within an <iframe> element, so that the website cannot access it due to same-origin policy. This is only a partial solution unfortunately as it will not prevent clickjacking attacks. Also, the website can always remove your frame or replace it by its own. So asking users to enter their master password in this frame is a very bad idea: users won’t know whether the frame really belongs to your extension or has been faked by the website.

7. Ignore third-party frames

Finally, there is another defense in the depth measure that you can implement: only fill in passwords in the top-level window or first-party frames. Legitimate third-party frames with login forms are very uncommon. On the other hand, a malicious website seeking to exploit an XSS vulnerability in a website or a weakness in your extension’s AutoFill functionality will typically use a frame with a login form. Even if AutoFill requires a user action, it won’t be obvious to the user that the login form belongs to a different website, so they might still perform that action.

Categories: Comment [3]