Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage Presentations How to Use Encryption for Defense in Depth in Native and Browser Apps

How to Use Encryption for Defense in Depth in Native and Browser Apps



Isaac Potoczny-Jones discusses the pros and cons of application-level and end-to-end encryption. He also covers the attack surface of application-level encryption in the browser, how it is very different from native clients, and how WebAssembly and WebCrypto help.


Isaac Potoczny-Jones is the founder and CEO of Tozny, a privacy and security company specializing in easy to use cryptographic toolkits for developers. He has worked with agencies including DARPA, the Navy, Air Force Research Laboratory, the Department of Homeland Security, the National Institute of Standards and Technologies, and other elements of the DoD and intelligence communities.

About the conference

Software is changing the world. QCon empowers software development by facilitating the spread of knowledge and innovation in the developer community. A practitioner-driven conference, QCon is designed for technical team leads, architects, engineering directors, and project managers who influence innovation in their teams.


Potoczny-Jones: I'm Isaac Potoczny-Jones. I'm going to be talking today about how to use encryption for Defense in Depth at the application layer, for the most part, including a relatively controversial section of the talk about doing encryption in the browser. That's why if you've collected some vegetables to throw at me, that's where you're going to use these.

My background, I'm not a mathematician type of cryptographer. I work at the application layer, at the protocol layer. I pick up great libraries like OpenSSL, Bouncy Castle, Libsodium. I try to use them correctly in the products and services that we provide, which are tools for developers. That's a little bit of my background. Generally speaking, and in all my talks, you'll find I'm not really talking too much about doing things like writing a new version of AES, or coming up with an encryption system. I'm really talking about how to use crypto correctly in your application.

Here's my metaphor that we're starting out with today. When we think about driving safety, we address both the road and we address the car. My metaphor here, road is infrastructure, and car is application. When we secure the road, we think of stoplights, speed limits, gentle curves, lines on the road, no passing zones. That's how we secure the road. When we encrypt in the infrastructure, we talk about HTTPS, and TLS, VPNs, IPSec, Full Disk Encryption, and that little flag in your database that says, "Let's go ahead and encrypt things in the database." That's how we think about encryption in the infrastructure. There are a lot of other types of encryption. A lot of other types of security in the infrastructure, but that's the metaphor here.

Securing the Car and the Application

When we think about securing the car or making the car safe, we think about seatbelts, crumple zones, airbags, and horns, and hopefully, doing a good job as a driver. On the other hand, when we think about securing the application, most of the time we think of malware, we think of buffer overflows, side channel attacks, broken authentication, your OWASP Top 10. We tell each other to be better programmers. What we usually don't talk about is encryption. I have some theories. For some reason, we typically think about encryption as belonging to the infrastructure and less as belonging to the application. I'll argue, when you're baking HTTPS into your app, that's still infrastructure-layer encryption. That's not really application-layer encryption. I'm talking about payloads. I'm talking about data. That's the metaphor here. That's the argument.

More application-layer security, when you're thinking about securing your application, you should include application-level data encryption, because this provides a massive amount of Defense in Depth. It can provide really powerful privacy controls. It can give you application-layer control over encryption in a way that you can enforce your policies at an application-layer policy thing. You're not just thinking about the network. You're not just thinking about the VPN. You're thinking about the logic of the application itself.

Application-Level Means More Programming

Application level crypto means that there's going to be more programming involved. We're programmers, and we know that we're going to make some mistakes. The great thing about doing encryption in the infrastructure is that lots of applications share that same infrastructure. If you have a secure infrastructure, your apps can ride on top of it and inherit the security of that infrastructure. That's great. That's important. I'm not saying we shouldn't do that. With application-level cryptography, we're going to write more code. We're going to make more mistakes. We're going to get it wrong. Frankly, the encryption community hasn't been great at making developer tools that are easy to use, that are correct. Someone shouted out Libsodium. They get a lot of credit for doing a good job of making developer tools, making an interface to their system that makes it easier to do correct crypto. As a result of these challenges, we don't do much application-layer crypto, even though we probably should.

There's a bunch of studies about this. There's a bunch of studies about specific platforms like Android and people making mistakes in those platforms. Typically, what happens is that you have more or less the ability to use a correct and secure cryptographic library like AES, might be provided by some library. There's nothing wrong with that implementation of AES, maybe you don't use it. Maybe you use an old fashioned cipher, or maybe you use it incorrectly. Java is famous for having an AES implementation and the default mode is to use ECB, which is an insecure mode of AES. AES is a very popular cipher. It's very secure. It's been checked all up and down. Then the default mode is insecure in Java. It really stinks. As a result, there are lots of misuse vulnerabilities in applications that are just using crypto wrong.

Infrastructure-Layer Encryption in the Browser - HTTPS

Let's take a step back and talk about our favorite infrastructure-layer encryption that's in the browser. This is a nice one to talk about because it bridges your application with your infrastructure. It's HTTPS. I have to say HTTPS over again, I found that's actually really hard to do, but I'll try. Conceptually, we love HTTPS. Conceptually, what we think of is we have, for instance, a client like a mobile device, then we have HTTPS and the data goes to the server. The data is encrypted and secure. That's wonderful. Let me introduce you too, to this idea of red and black. In some of my diagrams here, we'll use black to mean encrypted data. We'll use red to mean unencrypted data. That's fairly standard in the industry. We think about these two secure worlds. You're secure on the mobile. You're secure on the server. Then you have to talk between them, that ugly, insecure world between. You use HTTPS, and now you're happy. That's vastly oversimplified.

A More Realistic System Architecture

How many people have infrastructure that's this simple nowadays? Is there anybody in this room doing such a simple app? The one you work on, on the weekend, maybe? I pulled this infrastructure diagram off of just some random AWS thing that said, "Here's how you should do serverless." I think that's what it was. Yes, it's just a lot more complicated, especially nowadays, we can just click a couple buttons, and we have 100 servers. It's awesome. Nobody is really doing one device to one server right now. We have a lot more complex infrastructure, and unfortunately, oversimplifying the view of infrastructure makes infrastructure-layer crypto seem much easier than it really is. We're encrypting something like the wire, and it's great with HTTPS. There's a lot of other infrastructure that doesn't get protected by that.

Let's take a step back again and simplify a little bit. Let's take probably the simplest possible thing that's being implemented in this room. You have a mobile app. You have a load balancer somewhere that you're terminating your TLS connection. You have your server. If you're doing a really good job, you're probably also encrypting data locally on your device. If there's any sensitive information, maybe you're doing at the application layer, or maybe you're trusting in the fact that that device has full disk encryption like in modern mobile apps and operating systems. Then inside your network, maybe you're doing a good job and you're using IPSec. Then when you're writing to the database, maybe you're doing a good job, and you're doing some database-layer encryption. This is four or five different encryption implementations already, especially if you have multiple apps, and then you have a browser where you don't know what to do at all. Already, we're seeing that in reality, even for a relatively simple situation, HTTPS alone only covers one part of that. You still have to do a lot of work to cover all the other parts, let alone going back a slide to something as complicated as this.

Of course, there are a lot of challenges with HTTPS around trust and who gets to say who's trusted and why. These are the certificates I pulled out of my operating system to see how many trusted issuers of certificates are in macOS. It's 169. That one I highlighted is the one that issues the certificates for our website, because we use Let's Encrypt. As a result, we inherit this really massive trust infrastructure that's completely outside of the user's control. Then on top of that, of course, enterprises might issue certificates and put them in their employees' laptops, and all that stuff. It's really a lot more complicated.

Attack Models (HTTPS is Great, But)

I'll say that HTTPS is great. It's great in part because it's been attacked so many times, and it's been attacked so effectively, we've figured out how to secure it pretty darn well. There are still a lot of great attacks against HTTPS. For instance, you can just subpoena the company storing the data and tell them to give you the data because the data is available to that company and they can get it. It has nothing to do with HTTPS at that point. You can break into one of the systems that's not secured by HTTPS. You can break into one of the systems that's not secured by HTTPS or TLS, one of those intermediate systems where you've received, you've decrypted the data, and you've sent it along its way. Maybe the load balancer or something else that really doesn't need any of that data, it really just relays it. It's now a vulnerability point, because the data is in plain text at that system. Lots of people have misconfigured HTTPS over the years, and there's good ways of making sure you haven't done that, but somebody probably is doing that right now. You could steal a key. You could subvert a certificate authority. You could Mallory-in-the-Middle an enterprise-issued certificate. This is where, for instance, your employer issues their certificate on your laptop. Now they can jump in between in your traffic, probably to make sure you're not copying celebrity's health records off onto the internet, or something like that. They have probably good reasons for doing that. It does undermine HTTPS.

You can trick the user by getting them to think that they're at the website that you want them to think they're at. That they're at the bank sites, but they're not. That's called phishing. Of course, JavaScript is complicated. We're pulling in resources from lots of different places. These can have impacts on the security of what's running inside the browser. There's lots of great ways to attack HTTPS. If this is all you're doing, which is probably not all you're doing, but if this is all you're doing, it's just not enough.

Why HTTPS Is Not Enough

I get that question a lot, is HTTPS enough? I'll argue it's not. This is why. Generally speaking, it simply doesn't protect the data everywhere the data goes. It relies on a massive trust infrastructure that's completely outside the user's control, outside of your control, probably. It's necessary but it's not sufficient for making really strong privacy and security guarantees for your system.

Let's talk a little bit about application-layer encryption, and what I mean by that. The simplest type is generally to do one-party or one-device encryption. That is to say that encryption and decryption is happening on the same device in the same place or by the same user. That's nicer. It's a little bit easier. For instance, maybe you're doing local encryption on Android. Maybe you've got a library like Bouncy Castle. Before you stick stuff in the database, you encrypt it maybe using the user's password. When you pull it back out of the database, you decrypt it again. You can also use the biometric. We publish a popular open-source library for Android that people use a lot for this type of thing called Java AES crypto. Go ahead and use it. It's really fun.

This is relatively easy. There's a lot of ways you can do this. You don't need to do any key sharing, which ultimately is one of the hardest things to do in cryptography. You can use the biometric. On a lot of mobile devices, the biometric can be used to protect keys on the key chain, for instance. Then you can encrypt and decrypt in the same programming language, which turns out to be a really big deal. It makes it a lot easier to do encryption and decryption, appears in the same programming language. That's a diagram there. You're encrypting and decrypting in your mobile. You're putting stuff on disk, or maybe it's even on the server, and you're just getting it back and you're decrypting it there. That's one-party or one-device encryption.

Things get exponentially harder when you're talking about communicating between users, or from a user to a server. Why is this so hard? It turns out, apropos of our libraries' discussion earlier, that encrypting and decrypting in different programming languages using something like AES is way bigger of a challenge than you would really expect it to be. The reason for that is AES has maybe, I don't know, 7, or 8, 10 different parameters that you have to send it. Things like the mode of operation, that's ECB, CBC, GCM. They're all three letters for some reason. Key size. You have to figure out a way to do verification of the data, whether you're hashing it and things like that. There are a lot of different things you have to decide. Lining up those things in different programming languages turns out to be relatively hard, especially when one language has a default. Sometimes you might even get stuff that's, for instance, you encrypted something in Java, and you go to decrypt it in Python. You realize that the modes of operation available in Python just are not even the same. You literally can't do that. You have to find some overlap in your entire platform, maybe multiple languages because you have multiple platforms to figure out how to encrypt and decrypt data in AES.

There is this library, Libsodium, that's been mentioned. It's great. It helps you really do a good job here. I highly recommend it. One of the downsides, for instance, if you do government or banking work is that it's not the NIST-certified cipher suites. It's a completely different cipher suite. It's not going to be FIPS certified probably in the foreseeable future. You might not be able to use it in your application if you have certain compliance requirements. That's a downside of Libsodium. If you don't have that problem, I think it's a really good tool to just pick it up and use it. It'll probably help you get things right versus going into javax.crypto, or something like that.

In addition, when you're communicating between two systems, there are a lot of things that, for instance, HTTPS takes care of for you, like side channel attacks, replay attacks, timing attacks. There's all these attacks that are really hard to mitigate and really subtle. If you're just encrypting your payload and you're saying, "It's fine. I'm encrypting my payload. I'm sending it over on a clear network and I'm receiving it on the other side and decrypting it." You're probably making some other mistake there that HTTPS is going to really help you with. Replay is a really good example of that. For instance, someone says, "Transfer $10 from my account to your account." They intercept that. Then they just keep replaying that same payload over again. If you don't think about that ahead of time, even though it's encrypted, they can't decrypt it, they can't re-authenticate. They can just rerun that thing over again. That's that attack. You probably still need something like HTTPS, anyway, to mitigate those attacks.

Then the hardest thing is key exchange, establishing trust and identity of the user, or the device, or the server. For instance, if you have two end users who are trying to talk to each other, they have to figure out some way of saying, "That's your cryptographic key. That's great." Most people have no idea what a cryptographic key is, of course. Maybe everybody in this room does but most people in the outside world don't. Even the first time you SSH to a server, you have this trust on first use model that says, "You've never been here before. Here's my key." You probably think, "Whatever, that's fine." You say, yes. That's not a terrible model, honestly. It's one way of establishing that trust. It's not really all that sound if you're in a really contested environment, for instance. Generally speaking, users can't key. This is the great irony, about the internet today, which is that so many people are watching what we do and know who we are and what we care about, and what we're going to vote, and everything. We can't identify ourselves. We can't prove who we are to anybody without using hundreds of passwords. It's crazy.

Application Layer plus HTTPS

If you combine these two pictures of application-layer encryption plus HTTPS, what you have is a little bit of a nicer model. You originate data. You encrypt it. I'm using this padlock icon to indicate an encrypted payload that's floating between these services. Maybe you use HTTPS. Maybe you have internal traffic. Maybe you write to one disk or the other. In each of those cases, it retains this envelope of encryption. This really helps. For instance, maybe you have some backup infrastructure where you're pulling stuff out of your database and putting it into a backup, or something like that. Have you really thought about whether the data maintains its encrypted integrity in that process, or if you're logging? Is the data encrypted while you're logging? You're accidentally logging sensitive information and things like that. When you have application-layer crypto, you can do stuff like this. You can ensure that, if it does get backed up to someplace else, probably you should still flip the switch over there and say that's an encrypted backup. If not, then at least you are encrypting the payload.

As an aside, I think it's great to have transparent encryption at all these different layers, but especially we're using AWS, or something, and there are guards at the door, and nobody is jumping in and stealing the hard drives. Really, the attack model is remote. If it's transparent to you as the developer, the encryption is probably also pretty transparent to the attacker. If your application logic, for instance, allows an SQL injection attack and you've got full disk encryption, guess what? It's going to get decrypted and sent to the attacker. These things are good. They're important. I believe in them, especially for mobile devices and laptops. They're not a panacea.

End-to-End Encryption

Let's jump into end-to-end encryption. End-to-end is a type of application-layer encryption. You've probably heard a lot about end-to-end encryption lately. The idea here is end-to-end is where the data is encrypted on the device of the person who created the data, and is only decrypted by the intended receiver of the data. None of the intermediate parties have the keys for that data. That means none of the servers, none of the networks, and none of the wireless protocols, none of the governments have keys to decrypt that data. You have basically a server in the middle there facilitating communication between the parties, but they don't have the keys and they can't see the data. This is really powerful. It's really helpful for privacy. I'm a huge privacy advocate. It really helps to reduce the attack surface of the system and it helps to mitigate people intentionally or unintentionally violating the privacy of their users.

A venerable tool I mention here is the PGP, the pretty good privacy, GPG thing. How many folks here this was your first experience with encryption was PGP or GPG, something like that? Definitely, for me, we used to have parties in the Debian Community. I don't know if they still do this. We had parties where you could go to a city and you could say, "I'm coming to Cambridge, UK. I want to drink beer and sign crypto keys." You get 20 people showing up. It was amazing. That was how we establish trust between a lot of people and you could then build a recursive trust model. It's a really cool tool. It'll encrypt anything. Then you have to figure out your protocol on top of that, as far as, how do you send it? How do you manage other vulnerabilities around it? It lets you mark public keys of other users as trusted. It's very hard to use. It's very hard to use right. It's great. Lots of stuff is built on it. We like it.

More modern times, for some reason, end-to-end encryption is highly associated with chat apps today. This is probably a little bit of a fad. People are really into encrypted chat apps. This is great. It's probably partly because the Signal group released a protocol, an end-to-end encrypted chat protocol that a lot of people have just picked up and used. The idea here is the same. The chat message is encrypted by the sender. The chat message is decrypted by the receiver. The goal is people can communicate securely, so everyday people who want to have conversations with each other, who want to send credentials to other parties on your team, or something like that, or who just want to share information with reporters without being caught. Government employees sometimes seem to use these apps instead of their official email for their own reasons. These are pretty important and helpful apps. That's a good example. Pick up Signal, try it out. You'll get a good end user experience of what end-to-end crypto should be. It's pretty challenging to key. It's pretty challenging to know how to trust other people with a system like this, but at least it's doing a pretty good job here.

Something we really like about application-layer crypto with apps and desktop applications is that there's a code delivery model that we trust, or we think we trust. They have a really good workflow. For instance, on your phone, we install an app from the App Store. It has a digital signature. That signature is checked. That app probably has the encryption and decryption code baked into it. Probably, somebody has vetted at least the low-level primitives of that and hopefully has vetted the protocol it uses to pull together its encrypted workflow. Hopefully, it's vetted. Hopefully, you trust it. Then you use some out-of-band mechanism like a voice call to verify keys. Unlike browser-based apps, code doesn't change on the fly every time you open the app, or at least, for most apps, it doesn't. Your copy of the code is the same as everybody else's copy of the code. Once it's vetted, you can trust it. We like these properties. Because what you can do if you don't have properties like this, if you don't have a trusted application or don't know where the code came from. Is sure, it can be doing end-to-end encryption and sending your data to somewhere else, so exfiltrating your data, right in the application just over the internet. You have to trust the code, as well as trusting the primitives, and the algorithms, and the people.

Attacking End-to-End Encryption

End-to-end encryption is really so powerful that it solves a bunch of vulnerabilities. I put a little star there, an asterisk to say, really, it mitigates vulnerabilities. I love the word mitigates as a computer security professional, because it doesn't mean anything as far as what a good job you're doing. You can say it mitigates it a little bit. It mitigates it a lot, mitigates it completely. It's just a great word if you really don't know how security works, "It's mitigated".

It's really powerful for privacy. It's really powerful for security. For instance, for privacies, I'm not sure if people know the difference between a subpoena and a warrant. A warrant is a higher bar, but somebody can subpoena a web service and say, "I want this person's data," and they should probably give it to me. If you had that data at your home, somebody would need to come in and get a search warrant and come and take that data from you. There's a different standard for when you're storing data in the cloud for historical, legal, and technical reasons, and it's a lower standard. Now we're all storing everything in the cloud. It's a little bit of a challenge. We should use end-to-end crypto. Apple's done a really good job touting this to make that possible.

It mitigates misuse of data by your employees, by your third parties that might get access to your network, or what have you. It mitigates the undermining of HTTPS by governments, by certificate authorities, by employers. If you have an employer that's intercepting your HTTPS connections, and you ride an end-to-end encrypted protocol on top of that, they might be able to figure out who you're talking to depending on how the protocol works. They might be able to figure out the volume of traffic you're doing. They're not going to be able to figure out what you're saying, which is really helpful.

Attacks against end-to-end crypto, you can make it illegal. You can target people who are using end-to-end crypto for special, "Let's look closer at what they're doing because they're trying really hard to hide it." It sticks out because it's still not that common. You can force a developer to undermine the crypto. You could tell them, "For this person, don't give them good crypto." You can guess the password if your key is derived from a password, which hopefully isn't that common. By far the best attack is to hack the end device itself. It's end-to-end. That means, hack one of the ends.

This happened with WhatsApp earlier in the year. I like to make fun of this a little bit. Bloomberg put out this article that said, "WhatsApp's end-to-end encryption is a gimmick," because WhatsApp got hacked on the end. Somebody was able to intercept the traffic at that end. They got yelled out on the internet. They changed the title. Actually I had a hard time finding what the original title was because they changed it. This is a win actually for the proof of end-to-end crypto. Somebody actually had to target a zero-day unknown vulnerability in the application itself, instead of dragnetting the database for something else. Without end-to-end crypto, you have these mass volume type attacks like dragnet attacks. Let's get all of the data out of the database. Let's use that. Then you can troll through it and say, "What do we find in here? Who can we target? Is there something in here that's both a person of interest and compromising?" You can just look and find something like that, instead of saying, with end-to-end encryption you have to target a specific person, or a specific group of people.

That's a lot more expensive for the bad guys. They have to burn zero-day attacks, which can be very expensive to develop because these attacks get discovered. They have to spend more time figuring out how to get the attack on the specific device with a specific target individual. This is a win. Even though certainly it's a problem when things like WhatsApp gets hacked. We should all celebrate a little bit that we made the bad guys jump through so many hoops that they had to hack it, and you know about it because it's in the news. That means it got caught and it probably got fixed. When people are forced to hack the end devices, it's harder. It's more expensive. It's better for us as a community.

Here's the controversial part of the talk, application-layer encryption in the browser. What is end-to-end encryption or application-layer encryption in the browser? The idea here is you ship encryption code to the browser, which is probably written in JavaScript, or your protocol is written in JavaScript. Maybe the user logs in and you use a password derived key, and some maybe local session storage to manage a symmetric or asymmetric key there. Then when you are inside JavaScript in the browser, you encrypt data in the browser before sending it along its way. I'll talk a little bit about this great tool called ProtonMail. It's a web mail system that does this workflow basically for end-to-end crypto. Remember our previous pictures here. We had the data creation flowing through the server that couldn't see the data and the data use. There's this concept in end-to-end crypto that we're protecting the world. We're protecting the data against that server that the data is flowing through.

Attacking Browser-Layer Encryption

Here's the real problem with end-to-end crypto in the browser. That code came from the server. If you had this step one, get the code from the server. Step two, use that code to protect the data from the server. There's an inherent conflict there, where you could just undermine the code at the server level, or the organization can undermine the code. This is really a problem. The code that comes from the server to the browser could just exfiltrate the data. It could say, "Let's do end-to-end crypto. In the meantime, I'm also taking your data. I'm sending it to somebody else." You can target specific users. Everybody who's ever loaded this JavaScript before, they got good JavaScript. They looked at it, they said, "This is a great crypto protocol. Fantastic." Then I load it up and they just key off my username. It says, "It's Isaac, let's exfiltrate his data".

It can change moment to moment. Every time you load that page, you're getting a different source code that could do something different. It's not necessarily vetted. How can you trust it? Of course, the whole thing relies on the TLS delivery of the JavaScript source code in the first place from the server to the client. If you don't trust TLS, it's maybe you've got an all or nothing attitude that says, "We had TLS, in the first place. We have TLS again. If you can break TLS, then you can intercept." Remember this argument I made before, where if your employer intercepts your TLS connections, and you have an end-to-end crypto app where you can ride on top of that, this undermines that. If they really wanted to, they could intercept your connection. They could undermine your TLS. They can undermine your end-to-end crypto in the browser. Then you're shipping the data out to the employer again. That's the challenge with this model. I agree that this is a problem.

There's a good paper on this topic. I don't think it's a peer-reviewed paper, but it's easy to read. It's good. It's called, "Analysis of the ProtonMail Cryptographic Architecture." There's the link there at the bottom. The author here argues, it's just not end-to-end crypto if you do that, because of this basic attack. Maybe that's fair, maybe that's not. I think they could do a good job explaining if you want to delve into that a little bit further.

Remember that Real Services are More Complex

Remember, though, I would say that real services are a lot more complex than this thing, "If you don't trust the server anyway, how are you trusting them to give you your code?" What if your static hosting bucket is secure but you have an SQL injection in your API code? Attacks aren't all or nothing. You could be delivering perfectly good and secure encryption code to the browser from your bucket but the bad guy there in step three has an injection attack that gets data out of the API, but the data they get is encrypted. Remember, as we talk about these infrastructures that are more and more complex, you're using Defense in Depth here to protect your data at every place that goes across your infrastructure. Just because one part of your infrastructure is owned, that doesn't mean the whole infrastructure is necessarily owned. Maybe the first time the bad guy did this, it was detected, and this hole was closed. It's not necessarily all or nothing. It's not permanent vulnerability. It's something that can be protected. You can force maybe the organization to undermine stuff if you have a government that can do that to an organization. Whether you trust that organization or not, is up to you. It's definitely better than not doing it.

I would say this mindset where it's broken anyway, has been a little bit of a problematic mindset in the crypto community around doing encryption at the application layer in the browser, or calling it end-to-end encryption. The argument here goes like this, end users shouldn't use the web for sensitive stuff. It's possible to do it. It's impossible to do end-to-end encryption in the browser, or it's not really end-to-end encryption, or it relies on TLS anyway. I think that mindset's really problematic. I think it has this all or nothing attitude that says, "Because there are vulnerabilities, because there are ways around this, we shouldn't do this." I think that's the attitude that stopped a lot of people from putting encryption in the browser apps. If you ever do sit down and Google, "How do I do encryption in the browser," maybe the first page of Google is mostly going to tell you not to do it. I disagree with the first page of Google on that instance.

Defense in Depth: Application-Layer Crypto Mitigates Problems

Here are some good reasons to do it. This is a little bit of a grab bag about why application-layer crypto mitigates problems. It adds Defense in Depth. It reduces the need to trust your entire infrastructure. The API server and the admins on that server don't need to see the plain text of the data if they don't need to see it. The plain text isn't in the database. It's not subject to SQL injection attacks, insert any other infrastructure level attack here. Data has a way of spreading. Maybe it goes to your backup system. Maybe it goes into your logging system. Is that data appropriately encrypted at each and every point of your infrastructure? Again, web servers aren't just one thing anymore. Infrastructure is really complex and attacks are not all or nothing.

I will also say that from a legal standpoint, it's a lot harder to force a company to change their code to undermine your crypto than it is to get them to hand over data. In fact, I'm not aware in the U.S. of this happening. Maybe I'm totally wrong about that, and someone could tell me at the Q&A. This came up with Apple when they were trying to stop the use of data from this mobile device that was captured by a terrorist. This was a few years ago. They said, "Apple, you should just add a thing to your operating system to undermine the cryptography of the system." They said, "No, thank you." In the end, it didn't come to a head because the FBI found a workaround. I don't know how well this has been tested, but Apple fought it and the fighting worked.

Another thing is that attacks are detectable on the client. That doesn't mean they will get detected, but it definitely significantly increases the risk of getting caught when attacks are detectable both on the server and on the client. Also, browsers are really improving their ability to trust code. All this JavaScript security goodness we're getting into browsers lately is helpful. You can add application-level access rules and enforcement logic to your application. What I mean by that is, take end-to-end chat, for instance. The user basically makes an access control decision saying, "I would like only this person to receive this data." That access control decision is actually enforced with encryption. It's not just a flag on the server. The encryption says, "This is the only person that can get it." There's no access control system to just attack and undermine that and say, "I just flipped this bit and now I can get the data." It is actually cryptographically protected.

Think about your role-based access controller, or your application logic that enforces some access control. Can you bake cryptographic access control logic into your application more effectively than something like infrastructure or network based security? You're saying, "This part of the network can get the data because it's encrypted. This part of the network can't get the data." Or, you're saying something like, "This person, this admin can get the data or cannot get the data based on access control rules that are really application level, not infrastructure level".

You should do browser crypto anyway. Most people accept most default settings. That means the default security of your system is the security of your system. Lots of people use browsers for sensitive stuff. You should make the browser secure. My corollary there is ProtonMail, this web-based email service that's end-to-end encrypted is a good idea. Can you say, is browser-based crypto really end-to-end encrypted in that situation? I'd say, yes. I think it's a place where reasonable people can disagree. As long as you're sure you understand the security claims you're making of the system, you shouldn't be telling your users, "You don't have to trust us at all because there's end-to-end encryption." That's just probably a false claim on the face of it anyways. We shouldn't be pretending that end-to-end crypto is perfect security. We shouldn't be shocked when there are vulnerabilities. It's really relatively new to be doing this at large scale. We should be doing it and we should be trying to figure out how to do it.

WebAssembly and WebCrypto - How they do and don't help

Shifting gears here, let's talk a little bit about WebAssembly and WebCrypto, and how they interact with this type of application-layer security. WebAssembly, what it is. Just think of it as assembly language for the browser. You can code something up in a language like C, or something else, and ship it to the browser and have the browser execute it. You don't have to necessarily write your code in JavaScript. This can really help on the cryptography front. For instance, you could have a certified or a trusted library that's already written in C. A lot of crypto code is written in C. You can compile that and you can ship it to the browser, instead of reimplementing that in JavaScript. Then again, you have this cross-language compatibility thing that might get solved by doing that. You have a C library that's the core of your encryption and decryption system that's on all these different platforms, where most platforms have some binding to C nowadays. Basically, this is a binding to C in JavaScript that you can use.

We use a compiled Libsodium for compatibility across all of our systems. We have mobile apps. We have server stuff. We have a browser system. They all use Libsodium and so we compile that into WebAssembly in order to basically make that work in the browser with cross-compatibility, instead of trying to find a compatible JavaScript implementation of the Libsodium ciphers.

Also, JavaScript might be too slow for some uses. There's especially some really funny things, funny corner cases in a way about encryption, where you want it to be slow, like password-based key derivation. You don't want it to be slow for the user. You want it to be slow at scale. Having password-based key derivation implemented in a slower language, or implemented with a slow implementation is really problematic because it's slow for the end user. Then maybe you try to reduce friction and make it a little bit faster by reducing the number of rounds of complexity you add into that key, so that makes it easier for the attacker to attack that system. If you have a faster thing written in C, that's great.

What it doesn't do. It doesn't solve any of the vulnerabilities I was just talking about with end-to-end crypto in the browser. I'm sorry. It really doesn't solve that because you're still shipping crypto primitives to the browser, or you're at least still shipping the protocol to the browser. The JavaScript in the browser can say, "We'll just do that encryption stuff but we'll send it off to somebody else as well." That's how WebAssembly fits in all this. It's really great we're using it. I think it's a good idea. If you have a FIPS-certified library, for instance, then maybe you can get certified for the browser someday by using this thing.

WebCrypto is a totally different thing. What it is. It's an implementation of pretty common cryptographic protocols in the browser directly. The browser gives you an API to call out to things like SHA2 and AES. This is really great. For instance, we use it for cryptographically secure randomness. You can basically use the entropy that's collected in the user's operating system. Math.random is not secure. This is a way where you can do that, for instance.

It can be really fast. For instance, it can take advantage of hardware based acceleration. Most of your laptops and phones have one or more than one AES accelerator. AES is a very popular cipher. You have an accelerator in your computer that makes AES much faster than doing it just in software. You have to burrow through the operating system to get to those primitives so that you can take advantage of that acceleration. When you implement those primitives in the browser or you provide those primitives at the browser interface, then you can take advantage of that acceleration. In contrast, if you're shipping AES to the browser, and then running it there, then you get that slowdown because it's being interpreted real-time in the browser. You don't have to ship encryption primitives in JavaScript or whatever. You don't have to find compatible primitives across different programming languages maybe if you've found one that works in this browser API.

It doesn't actually solve any of these problems. You might be thinking, "If the browser has some trusted crypto, and maybe even the hardware has FIPS-certified crypto in it, or something, isn't that great?" It's good because we know that implementation of the algorithm is secure but you don't know whether your use of that implementation of the algorithm is secure. You don't really solve this problem where you can just exfiltrate data. You still have to implement the protocol in JavaScript, which can be undermined.

Bottom line here, WebAssembly and WebCrypto, they're very good. We're using them every day. They might help us do better with crypto and make it faster, make it easier to use. They don't fundamentally change the security stance of browser-based crypto. What does help? Modern JavaScript security helps. Basically, we saw all the people raising their hands, who are web developers who probably know these things better than I do. Things like strict transport security, where basically, for instance, you're telling the browser, only ever load this page over HTTPS to prevent downgrade attacks. Strict content security policies where you can list safe sources for loading your scripts from, so that somebody is not just injecting a script from somewhere. Subresource integrity, where you can only load scripts that you know are secure, using hashes. Things like these, so the advancements in the browser over the last few years, as well as the previous slide, that allow us to do a better job with the challenging problem of source code trust, as well as correct implementation of securing the browser.

Conclusion: Do more Application-Layer Encryption

In conclusion, please do more application-layer encryption. People in this room probably care about security. That's probably why you showed up. Go back to your organizations, have a positive influence there. Build Defense in Depth into your security. Build privacy into your system. It can really substantially help. You don't know how it might impact people. You might really be saving a life, you never know. It's definitely harder for developers than infrastructure-layer encryption. A couple pieces of advice there, Libsodium is easier to pick up than raw AES but it's not NIST approved. Easy to use encryption is what we do. Have a look us up. I have a lot of other talks and some training material on our website there about developer's guide to encryption basically. If you're reading that and something is confusing, please drop me a line and we'll fix it. This is some of the practical advice I have.

Does Privacy Matter?

Lastly, I just want to take a moment to have you think about a question, "Does Privacy Matter?" Apple's been touting privacy. If you've been watching football lately, they have privacy ads during football. If Apple's doing it, there's probably a really good reason financially and from an economic standpoint that it's a good thing. If you or your employer are motivated by those things, maybe you should go and talk to somebody and say, "Apple is doing end-to-end crypto. They're doing a good job here. Maybe we should do better".

I'll also say, what do you think? Does privacy matter? Do you do something that somebody else would disapprove of? Do you believe something that somebody else would disagree with? Do you have something that someone else would want? Do you say something that someone else would fight against? Or, are you something that someone else would hate? If that's not true of you, maybe you could imagine that it's true of your end users, or your friends, or your colleagues. Privacy does matter. Please take the initiative. Build it into your applications. Do the right thing.

Moderator: There is another library, and not only Libsodium. There is another library called Themis, which has OpenSSL, BoringSSL integration and 12 different languages. You can have user-friendly encryption library and FIPS-compliant at the same time, which is great, I believe.

Questions and Answers

Participant 1: There was an off-hand comment you made talking about detecting attacks. You said, "Attacks are detectable on the client." You moved on. Could you elaborate on that? How you envision attacks being detectable on the client?

Potoczny-Jones: This is basically a question about the way security research works and the way vulnerabilities and malicious software are detected in the world. That's to say that if somebody is inside your infrastructure, they've effectively attacked you. That's inside your wall. Once they have to go outside that wall, once they're targeting people. It opens them up to either detection by reverse engineers. For instance, you're downloading something off of the web app store and you're reverse engineering, you're finding vulnerability and malicious software. Lots of security researchers have infrastructure for detecting attacks and finding attacks. Or if someone is just a high-value target, maybe there's a honeypot there that says, "This is their phone." You deploy an attack against that phone but it's not really their phone. You've just found that attack. Now that you've found it, you can mitigate it. You can fix that vulnerability. It's probably a bigger question around how malware gets detected in the world. The real argument I'm trying to make is when you have it inside your closed walls, you can maybe detect it. Once it's outside those walls, there are a lot of other people that may be involved and may be able to detect it. It's not a panacea though.

Participant 1: I'm specifically interested in detecting attacks. Let's say ProtonMail's application code, somebody, Man-in-the-Middle delivers something that'll exfiltrate some data once it's decrypted. Is there any way you can envision for that thing to be detected?

Potoczny-Jones: Sure. If they're just targeting you, maybe it only gets delivered once and you don't have any infrastructure to detect that. If they've broken into their GitHub and they've added some malicious code to their application, then everybody who receives that JavaScript can go look at it and see, in the browser, that somebody has injected something here and it's not secure. Security researchers all over the world are pounding on these applications and checking them every so often. Maybe even checking that the code is different from it was last time, so what's changed? Again, with targeted attacks, very hard, but with broader scale attacks it's much more effective.

Moderator: There is also I think that if your client application downloads a cryptographic library, a cryptographic code and you can't be sure if there is someone in the middle that changes code. What you can do is to have some integration checks, to make sure that the library you downloaded has the same checksum as library was supposed to download. If it's changed you can probably notice that. What is more as Defense in Depth, you need to have many of these checks during the application flow, not only once, because one check can be bypassed. You have many checks. You have this repeated authentication of integrity checks of your libraries.

Participant 2: You talked about WhatsApp being end-to-end secure? We also have the WhatsApp web version. Is the web version as well end-to-end secure with the same?

Potoczny-Jones: I actually don't know what the web version does, honestly. They might derive keys from passwords in order to add that layer. If somebody else knows, maybe just shout out. I wasn't aware that they even had a web version. Sorry about that. I would guess, probably they do something like that. It's just a guess.


See more presentations with transcripts


Recorded at:

May 07, 2020