facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
229.86k stars 47.06k forks source link

React Cryptography (Encryption, Key Exchange, Signatures, etc.) #17154

Closed paragonie-scott closed 5 years ago

paragonie-scott commented 5 years ago

Hello Facebook and React developers the whole world over:

My name is Scott. I've previously been involved in the modernization of PHP's cryptography features (removing mcrypt, adding libsodium), which in turn led to HHVM implementing their own sodium extension.

Consequently, if you're using PHP 7.2 (or newer), or HHVM 3.20 (or newer), support for libsodium is baked into the programming language and you don't have to install anything extra.

(Unless your operating system's packaging team is asleep at the wheel with PHP security. Let me know if you encounter that so I can hopefully help address these deficiencies in the ecosystem.)

As part of my security work, I've been exposed to an increasing number of React apps in recent months. I like the philosophy of React (especially learn once, write everywhere).

Most of the time, the sheer fact that folks are developing in React instead of writing their own framework means I don't get to include client-side vulnerabilities (e.g. DOM-based XSS) in my reports. However, I have found a substantial number of cryptography-related issues in people's React apps.

To be clear: That isn't React's fault at all. It's a symptom of the greater JavaScript ecosystem and a general lack of easy-to-use, secure-by-default, and easily discovered cryptography libraries. (As the linked thread shows: AWS's libraries are almost certainly good, but at the time of that tweet, you wouldn't find them searching for "encryption" on NPM.)

I would like to severely reduce the prevalence of trivial cryptography bugs in React projects. There are a couple of directions we can go with that goal in mind. I'm going to propose one below, but by no means is my idea the only solution.

(If so desired, I can separate this into two separate Github issues: One for the problem, the other for my proposed solution. Please let me know if that's easier to digest.)

Proposal

Background and Prior Development

The state of JavaScript cryptography is pretty terrible. Writing performant code with libsodium requires manually sizing your own buffers, etc. Writing cross-platform code requires writing code a completely different way.

Not using libsodium is perilous in other ways, and has motivated other security companies to recommend developers just "use the NaCl/libsodium default" in most places where cryptography touches application code.

In the spirit of scrypt-for-humans, I developed a cross-platform, permissively licensed, asynchronous JavaScript module called Sodium-Plus.

In the same spirit as React, we want users to be able to learn Sodium-Plus once, and write code anywhere. It runs in Node.js and in a web browser, with a near-identical API and user experience. (The delta in the UX is the unavoidable difference between loading a module with require and including a script tag in the HTML.)

You can read the documentation online.

By default, Sodium-Plus uses libsodium.js, which is the result of libsodium compiled into WebAssembly, which runs in the browser. However, if you install sodium-native alongside sodium-plus, it will opportunistically use sodium-native instead with no code changes.

(Sodium-native is much faster than libsodium.js, but isn't browser-compatible.)

The Meat and Potatoes of the Proposal

Given the challenges of the JavaScript ecosystem, and the availability of usable cryptography, I'd like to recommend the inclusion of sodium-plus in a future release of React.

React doesn't currently provide a cryptography interface. If React ever does provide one, libsodium is almost certainly the best one to provide (prior to the maturity of post-quantum cryptography, of course).

React doesn't necessarily have to provide a cryptography interface, but React apps would be a lot safer if most developers had a stable, easy to use, misuse-resistant, cross-platform suite of cryptography tools at their disposal.

Out of all the existing options for libsodium, ours is the only one that provides an async/await-ready API, works everywhere, and has the same API and developer experience where ever you encounter it (in the same spirit as React).

I believe the arguments above make a strong case for sodium-plus as the best choice for the React community. If anyone has criticism to offer Sodium-Plus or my proposal in this Github issue, I welcome it. (If your criticism would be deemed off-topic for this discussion, scott at paragonie dot com goes directly to me.)

marvinhagemeister commented 5 years ago

Honest question (I might be missing something): What's the use case for shipping a crypto library to the browser?

paragonie-scott commented 5 years ago

What's the use case for shipping a crypto library to the browser?

Your honest question is an excellent one.

Before I delve too deep into the weeds in answering it, I should point out: people are already doing it. So even if the remainder of my comment doesn't seem adequate to anyone reading it, keep in mind that people are already running with scissors, almost certainly including within the React community.

(EDIT: I looked closer at that last library after leaving my comment here and, indeed, it's vulnerable to Vaudenay's padding oracle attack. Use of unauthenticated CBC mode is a common problem in React apps that employ encryption.)

A Short and Non-Exhaustive List of Use-Cases for In-Browser Cryptography:

  1. Compatibility with desktop and/or mobile native apps that offer encryption, wherein your threat model deems "running JavaScript delivered from a webserver" acceptable.
  2. Offloading (at least part of) password hashing to the client side (to minimize the impact of DoS attacks against password hashing algorithms).
  3. Building towards password-authenticated key exchanges (PAKEs), such as OPAQUE or SPAKE2+EE (possibly with blind salting; I call that mode BSPAKE). When these land in browsers in the future (possibly in 2020 or early 2021), client-side in-browser cryptography allows us to polyfill these new features and adopt better security standards sooner (rather than later).

As I mentioned in the first list item, the acceptability of in-browser cryptography features will depend on the developers' and their users' threat models. If you're trying to build something like Signal or WhatsApp, this is a big no no. But most of us aren't trying to do that, so we can relax our requirements a lot.

This must absolutely not be used for trying to replace HTTPS, as that's a foolhardy idea.

Generally, I feel that the priority should be 1) improving the security of mobile/desktop native apps, then 2) making sure these changes don't break compatibility with web browser applications.

aweary commented 5 years ago

Thanks for all of the thought and effort you put into this proposal @paragonie-scott. While I agree that there is some obvious value to having a strong and consistent cryptography interface on the web, I don't think including one with React is the right path.

React doesn't really have any built-in features that require a strong cryptography library. React is focused solely on building user interfaces, which doesn't have a ton of crossover with the cryptography problem space. While it's true that there are use cases for cryptography libraries in the browser, there's really no use case that is specific to React.

React is also very sensitive to code size and libsodium is almost five times larger than React itself. I don't think we could justify including a dependency like that, especially considering that strong crypto is currently a relatively niche use case on the web.

If you want to provide an easy-to-learn, strong crypto interface for the web I recommend working with the W3C on features such as Web Cryptography API.

paragonie-scott commented 5 years ago

While I agree that there is some obvious value to having a strong and consistent cryptography interface on the web, I don't think including one with React is the right path.

As I stated in my first post:

(If so desired, I can separate this into two separate Github issues: One for the problem, the other for my proposed solution. Please let me know if that's easier to digest.)

If the proposed solution is not desired, rather than closing the issue wholesale, can we continue to discuss alternatives?

For example: Instead of including sodium-plus with React, I could contribute documentation (tutorials, etc.) for adding cryptography features to React projects.

If you want to provide an easy-to-learn, strong crypto interface for the web I recommend working with the W3C on features such as Web Cryptography API.

While I appreciate the gesture, that's a non-starter. WebCrypto is a potluck standard, designed by committee, and originally intended for DRM not secure general-purpose cryptography. Its fate is similar to the JOSE standards: They will dig their heels in on refusing anything that deviates from their "cipher agility" based design.

There is no hope for improving WebCrypto.

fardarter commented 5 years ago

"I could contribute documentation (tutorials, etc.) for adding cryptography features to React projects."

This seems like a good idea. One of the best/worst parts of the React ecosystem is the use-what-you-need culture.

However, I know for sure there are cases where people need crypto, so a best practice implementation guide would be extremely helpful.

aweary commented 5 years ago

For example: Instead of including sodium-plus with React, I could contribute documentation (tutorials, etc.) for adding cryptography features to React projects.

Please feel free to do so! If you think the official documentation should include these resources you can file an issue in the reactjs/reactjs.org repo. I'll add a caveat though: the documentation has historically only contained resources directly related to the React API itself. Even for much more common topics like routing, we shy away from officially endorsing third-party solutions (as @fardarter alluded too).

There's also the question of whether there's any React-specific content here? It seems like web crypto is a very generic topic and the solution would look mostly the same regardless of the framework you're using.