w3c / webcrypto

The W3C Web Cryptography API
https://w3c.github.io/webcrypto/
Other
263 stars 53 forks source link

Please require a secure origin (Bug 25972) #28

Closed mwatson2 closed 7 years ago

mwatson2 commented 7 years ago

Bug 25972 from bugzilla:

As with service workers implementations want to require a secure origin in order to get access to cryptographic functionality. We should make that a requirement in the specification so that implementations do not have to reverse engineer each other.

In particular you want to refer to the origin of http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#entry-settings-object

Secure origin is defined by https://w3c.github.io/webappsec/specs/mixedcontent/

Ryan probably knows which flavor of secure origin is to be used here.

hhalpin commented 7 years ago

Add text saying "Developers should use Secure Origins, although this is not strictly required."

mwatson2 commented 7 years ago

Discussion on 7/11 call suggest requiring a secure origin, in the stronger sense of requiring the top-level browsing context to be secure.

mwatson2 commented 7 years ago

I assume that the secure origin test should be performed as the first step in each WebCrypto method. This would mean that errors due to WebIDL type conversion would still occur on insecure origins, but then you would get the secure origin test failure.

IIUC, Chrome's current implementation does algorithm normalization and possibly other parameter validation before checking for the secure origin.

mwatson2 commented 7 years ago

PR #122

bdhess commented 7 years ago

-1 for requiring a secure origin (though I'm okay with it being recommended).

The "Secure Messaging" use case, for example, doesn't require TLS in order to preserve the privacy of the exchange, so long as only ciphertext is transmitted. Simply put, we don't want the extra roundtrips associated with establishing a TLS session when they are superfluous to the actual security solution, since this leads to a worse end-user experience with no tangible benefit.

domenic commented 7 years ago

Whatever you do, please do not use the incumbent or entry settings objects here. Current settings object is more correct. See https://readable-email.org/list/public-script-coord/topic/multiple-globals-and-you

ddorwin commented 7 years ago

Looking at https://w3c.github.io/webappsec-secure-contexts/#new, would it be simpler to add [SecureContext] to GlobalCrypto, Crypto, and/or SubtleCrypto?

domenic commented 7 years ago

That would probably work, although it would have different normative effects than adding a guard. (Adding a guard causes errors at call time, whereas [SecureContext] prevents the interfaces from existing at all.)

mwatson2 commented 7 years ago

@bdhess Without a secure origin, you can only make security arguments with respect to passive attackers. An active attacker - one who can modify the pages served to the client - has full access.

Privacy against passive observation is not without value and this is the argument I have used in the past against requiring a secure origin. However, we'd need some evidence that there were users with that use-case as a requirement (my company was one of those, but no longer).

mwatson2 commented 7 years ago

@ddorwin Yes, that would seem to me to be simpler and clearer. However the secure context specification recommends both. I am not sure why, since if the interface is only exposed in secure contexts the guard could never fail. @mikewest ?

bdhess commented 7 years ago

@mwatson2 In our operating environment, there's no sensitive data or PII that's emanating from the client. Ultimately, our use of Web Crypto is entirely to provide security around server-provided assets. So the protection of client-side key material is therefore a concern for us, but as I understand it, out of scope for Web Crypto.

mikewest commented 7 years ago

Yes, that would seem to me to be simpler and clearer. However the secure context specification recommends both.

Adding the [SecureContext] IDL attribute to the relevant interfaces should indeed be enough. The API won't be exposed in non-secure contexts, and since the state of a context can't change, the explicit guard in the algorithm is redundant (though probably worth noting next to the algorithm in any event). If you're ok with the implications (hiding the interface rather than throwing), then that sounds like a totally reasonable way to address the issue.

annevk commented 7 years ago

Is it web-compatible to hide the API from insecure contexts? That's the main tradeoff solution-wise.

mikewest commented 7 years ago

Safari and IE both ship prefixed variants of the crypto.subtle APIs. I imagine developers would have to be feature-detecting anyway if they don't want to error out on those browsers.

ericroman920 commented 7 years ago

Marking with [SecureContext] SGTM.

-1 for requiring a secure origin (though I'm okay with it being recommended).

Note that Chrome already doesn't allow Web Crypto over non-secure origins. So clients needing to support Chrome will have to address this in their implementation regardless.

IIUC, Chrome's current implementation does algorithm normalization and possibly other parameter validation before checking for the secure origin.

Chrome (currently) does the origin check after (most) WebIDL checks on the parameters, but before things like "algorithm normalization", and rejects with NotSupportedError in that case. Hence it is not very consistent as far as what parameters have been processed. Marking with [SecureContext] avoids that problem.

What should happen when structured (de) cloning a CryptoKey into an insecure context? Under the crypto.subtle is hidden model, I presume you could still postMessage a CryptoKey to an insecure context. Is that relevant?

bdhess commented 7 years ago

@ericroman920

Note that Chrome already doesn't allow Web Crypto over non-secure origins. So clients needing to support Chrome will have to address this in their implementation regardless.

So the contention is, since Chrome has already defined a behavior, there's no point in discussing changes that would make the spec inconsistent with Chrome's present behavior? This seems like a really backwards way of developing community standards. I'm not the first person to object to this behavior:

https://www.w3.org/Bugs/Public/show_bug.cgi?id=25972#c6 https://www.w3.org/Bugs/Public/show_bug.cgi?id=25972#c20 https://www.w3.org/Bugs/Public/show_bug.cgi?id=25972#c22

From the record, it's pretty clear that Chrome came to this decision based on the assumption that there exists no scenario in which there is value in being able to use the Web Crypto API in an insecure context. Including myself, there are at least four commenters who've disputed that.

annevk commented 7 years ago

I suspect that were you to ask the people that objected to this in the past again, they are probably more agreeable to this now. E.g., Mark commented in this very thread saying so.

Also, the concerns Ehsan raised there were mostly about the original solution easily being bypassed. Secure contexts addresses those concerns. He didn't really state any use cases for Web Crypto in insecure contexts and I don't really think you have clearly done so either.

mwatson2 commented 7 years ago

New PR using [SecureContext]: #131

ericroman920 commented 7 years ago

A problem with PR #131 is that it marks window.crypto as [SecureContext].

This means that window.crypto.getRandomValues() is no longer accessible on insecure contexts.

I don't believe we want that change -- getRandomValues() has always been available on insecure contexts.

Instead I propose marking only SubtleCrypto (i.e. window.crypto.subtle) as [SecureContext].

mwatson2 commented 7 years ago

Yep, I missed that. I'll update it.

vijaybh commented 7 years ago

I'd lean towards requiring a secure origin here. I feel a little sad about losing hash capability in insecure origins (since that is not sensitive in any way, similar to getRandomValues) but overall given the current threat landscape I believe it is better to strongly protect key material even if we take this bit of collateral damage.

mwatson2 commented 7 years ago

The CfC for this issue was approved.

jeremyVignelles commented 6 years ago

Hi, I don't think that enforcing HTTPS everywhere is a good solution, especially with that API.

I am currently developing a distributed app that works on a local network, without internet access. Each node has a little "admin" interface. Due to the cost of having and maintaining a PKI, especially for small clients, I don't want this admin interface to be on HTTPS. Having a self-signed certificate is not an option either, because of all the security warning that would cause the user to skip the warning without even looking at it, making the whole thing as vulnerable as plain HTTP.

For the solution I designed, I implemented a key exchange mechanism over HTTP and encrypt only sensitive data as needed. But on first try with chrome on another computer, I realized that my thing is entirely broken.

I want to use new APIs, but the work of having HTTPS on each node is a no-go for me, and in the end of the day, I can't make my site any safer by using this API... that's really too bad. It's like telling a homeless to give his address when he wants to buy a blanket to cover himself...

That being said, I suppose that I need to find a crypto library... Which is just the same as using webcrypto from insecure origin, but with more js weight, less maintained code and less performance.

zpdDG4gta8XKpMCd commented 6 years ago

this pisses me off greatly, i just want to calculate my sha1 using your super secure web crypto shit, and it cant because well, i don't care about being all cryptic, i just need my sha1 please, looks like you got lost in definitions, not everything named "secur..." needs to be turned off

m5x commented 6 years ago

I do not want to be rude but whoever came with the idea that crypto should not be available in non-secure context was probably only thinking about his narrow set of usage scenarios. We have an intranet exam server for schools which they use separately in every class room for computer-based final exams. We cannot use HTTPS because schools cannot buy certificate for every intranet computer they want the exam server to run on and we do not want to use self-signed certificate because browsers would issue warnings which we want to avoid. So we use window.crypto.subtle to implement our own asymmetric cryptography to protect sensitive information on the wire. Now we will have to use some JS crypto library which will only increase bundle size and decrease performance.

bin-y commented 5 years ago

Can someone stop this environmentally unfriendly idea since everyone can calculate hash and encrypt data without this API but with bigger code size and lower speed.

X-Ryl669 commented 5 years ago

Please reopen this. There is no justification given for the need of secure origin limitation. The use case of using this on HTTP is too important not to account for it. The most common use case:

  1. Non-internet connected website, there is no way to order/maintain a valid certificate
  2. The server can not implement a valid SSL certificate (like a small microcontroller that can't embed certbot).
  3. System without maintenance (certificate tends to be more and more time-limited) and provider don't update them. Using webcrypto to store a secret, keypair once HTTPS is valid in localStorage it could be used to ensure everything is still ok even after HTTPS is failing.
    1. During development, it's much more difficult to use this code since we might not be exposed to the internet, and thus, might not have a valid certificate.

This bug actually worsen the security because:

  1. One could use a JS library for what's implemented here, and serve on HTTP.
  2. Doing so, he might use a less verified code (more prone to bugs than a highly used implementation)
  3. A MITM could capture the library and implement a backdoor in it (while using webcrypto, this does not happen since it's embedded in the browser)
  4. It's possible with subresource integrity (SRI) to ensure the JS is not tampered. It's not possible to ensure main HTML integrity, but if an attacker is able to intercept the main HTML page, he can also set up a phishing site as well in HTTPS with a "looks identical URL".
  5. Provided the event that the first time the attacker is intercepting the page is unlikely, the JS code can use localStorage to store the a key pair and use DH to derive a secret from it and the server's key. Then, in any later session, the server can send a JS encrypted file or content with this secret. Any interception will fail since the MITM has no way to know the secret and decrypt the file/content. He can not gain access to the server without this, making the attack very unlikely. The only advantage could be to learn the client password, but that's not enough to do anything with it.
  6. In all cases, using SRP is enough to ensure that a MITM could not capture the password or an auth token in all cases. SRP is implementable in JS too.
  7. This increase the storage required on the server (to store equivalent JS files for all features required), and also adds more battery/energy consumption on the client (since the crypto is now done in JS instead of optimized binary/hardware)
  8. This forces to reinvent the wheel.
  9. This make Chrome less interesting to use since all other browsers except Chrome allow Webcrypto over HTTP. Thus this again leads to bad web usage/feeling.
  10. HTTPS does not prevent XSS attacks and enforcing this does not make it harder since, nowadays, anyone can have HTTPS. It's still possible to steal keys whenever a malicious cross site javascript is run on your fine HTTPS page with webcrypto and hooks the methods/password field/whatever.

Currently, the only reason why one might want webcrypto.subtle to only be restricted to HTTPS is:

  1. To enforce developers to use HTTPS (but developers are not stupid, they sometime can't avoid using HTTP). This is a Chrome crusade based on heresy mainly with only faith based justification. Political or religious belief should not pollute the standard.
  2. To avoid MITM/phishing (that's more a reason why HTTPS is required, not webcrypto). Again, a developer knows that, when using HTTP, it's impossible to ensure the page displayed is the correct one. Yet, they are many case where we don't care (local network, secured internal network, etc...) or they are counter measures as explained above.
  3. It restrict the API to only "serious" users. Honestly, I hate this argument but I can understand it.
MattiasMartens commented 5 years ago

I'd like to add my voice to this chorus as well. Hiding the interface is pretty developer unfriendly. I can make do without it, but only by fetching a crypto library through a CDN (or copy-paste), which adds new potential points of failure (not to mention slowing down the page).

My use-case is just that I want to avoid sending out plaintext e-mail addresses in URLs over HTTP (which must then be decryptable on the other side). The context is not secure and can't be made secure. Not being able to access a standard library function because of the context in which it runs seems to violate the principle of least astonishment.

scabot commented 5 years ago

For us as well this limitation is unjustified. HTTPS is not a good default option for configurable enterprise intranet oriented IoT devices like IP cameras etc.

stevendesu commented 5 years ago

@X-Ryl669 put together a wonderful post detailing a plethora of reasons this was an incredibly stupid decision. I'd like to add to the list:

It seems like the only reason you decided to limit WebCrypto to HTTPS was because "Chrome did it first" -- that's bunk. Unless Chrome is now 100% of the World Wide Web Consortium and holds total sway over the future of web standards, they deserve a swift kick in the crotch and need to sit down.

Here's my use case that you're buggering with: I'm developing a video player. The video player will loads ads using the IMA SDK, and it will have analytics reporting playback statistics to AWS. Calls to AWS must be signed securely, and so I wish to use WebCrypto for SHA256 and HMAC generation. This player works when pushed to production where we have a valid SSL certificate and can use WebCrypto, but for local development it either fails because:

Now I'm forced to either generate an SSL certificate and run a web server just to test my player locally, push my code to production in order to test it (nope.jpg), or add some bulky and potentially insecure SHA256/HMAC library to my code (also-nope.jpg)

This asinine "requirement" (which makes the web slower, less secure, and less user friendly) has now turned my new developer onboarding steps from:

To:

Hammerfest commented 5 years ago

Running into this absurdity as well, internal use, cant HTTPS/SSL/ect, should have exceptions at the very least. No option = anti open

dukelec commented 4 years ago

+1

tbenst commented 4 years ago

I have a scientific application running offline that uses the web browser for rendering. I simply call crypto.subtle.digest to calculate a hash of function arguments that is used for memoizing an expensive function. Was confused why my app stopped working once I left localhost and found this issue. I can write my own hashing function, but it's insane to think that this standard doesn't trust developers to call a hash function in an insecure context. A hash function has many uses besides crypto.

patrickjrm commented 4 years ago

I'm using a third-party libary that itself relies on certain hash functions provided in window.crypto.subtle so I really need the global defined.

I was able to shim this in webpack for local development on an insecure host like http://my-virtualbox-vm like so:

npm install --save-dev @peculiar/webcrypto
// webpack.config.dev.js
var webpack = require('webpack');

module.exports = {
  // ...
  plugins: [
    new webpack.ProvidePlugin({
      'CryptoShim': '@peculiar/webcrypto',
    }),
  ]
}
// index.js (executes in browser context)
if(window.crypto.subtle === undefined){
    console.warn(`Shimming window.crypto.subtle using Nodejs library!`);
    let Crypto = new CryptoShim.Crypto();
    window.crypto.subtle = Crypto.subtle;
}

I hope this can help any other webpack users for local development.

That said, in my case, I am actually consuming a cross-domain API secured by TLS so it is pretty silly not to be able to generate hashes, signatures, etc., as required for API clients. In production, this client will be served also over TLS.

patrickjrm commented 4 years ago

Another option for individuals just working locally over HTTP is to enable the chrome://flags/#unsafely-treat-insecure-origin-as-secure flag for Chrome.

briandipalma commented 4 years ago

Can the digest functionality be made accessible? Even on a different API to distinguish it. I just want to create a digest of some text. There is nothing security critical about my use case, otherwise I have to store a far larger blob of text for comparisons.

briandipalma commented 4 years ago

In case anyone else is looking for a simple digest I'm trying https://github.com/indutny/hash.js and it seems to work well. For my purposes SHA-1 works just fine. It's a pity I'm not allowed access the perfectly serviceable native solution though.

StephenLynx commented 4 years ago

This is absurd. For what god camn reason shouldn't I be able to get the checksum of a file just because I'm not using https? What were you thinking? Why shouldn't I be able to use any feature at all unless I'm on https?

stevendesu commented 4 years ago

If anyone is looking for HMAC-SHA256 support (and not just a checksum or simple digest), I wrote the following for my project:

https://gist.github.com/stevendesu/2d52f7b5e1f1184af3b667c0b5e054b8

The code was designed for efficient minification, and intended to be loaded via CommonJS - but it can be pretty trivially wrapped to support other use cases. For instance:

var hmac = {};
(function (module) {
// ... paste code here ...
})(hmac);
// can utilize hmac here without requiring CommonJS

The module exports 3 methods:

Usage:

var hmac = require("./hmac");
var key = "mySecretKey";
var data = "myData";
var signature = hmac.hex(hmac.sign(key, data));
StephenLynx commented 4 years ago

Also, what's the deal with "chrome did so it's standard now"? Am I the only one that thinks a single corporation whims shouldn't dictate technical standards? If they want to do weird stuff and require https, that's their problem.

mwatson2 commented 4 years ago

This is absurd. For what god camn reason shouldn't I be able to get the checksum of a file just because I'm not using https? What were you thinking? Why shouldn't I be able to use any feature at all unless I'm on https?

This is covered in the comments, but admittedly quite some time ago.

If you are on HTTP you are vulnerable to a MITM attack which could replace the webcrypto API (or indeed any of your code) with an attackers version. So, then, all security bets are off. For example, the attacker's webcrypto could return the checksum you expect for a file, but the file could still be the attacker's version.

At the time of writing, there were large, well-known, supposedly reputable ISPs conducting MITM attacks on the Internet to modify the Javascript code on websites. So the argument that MITM attacks are hard or rare or only applicable in particular network scenarios didn't carry much weight.

There are trade-offs, of course, as noted above, as there could be applications concerned only about passive attacks and they could work over HTTP with Webcrypto if webcrypto was not restricted to secure contexts.

patrickjrm commented 4 years ago

@mwatson2 Out of curiosity, if malicious JavaScript has been executed in an insecure context, what is to stop someone from shimming window.crypto.subtle anyway as I had in this comment? It doesn't seem like removing the API accomplishes anything?

StephenLynx commented 4 years ago

@mwatson2 that's a reason to use https, not a reason to FORCE people to use https. Why should people be FORCED to use it?

mwatson2 commented 4 years ago

@StephenLynx The reason is that webcrypto over http is not functional - in the practical sense of being able to rely on the results - except in very limited circumstances. So it would be misleading for the standard to suggest that it was.

@patrickjrm Sure, an attacker performing a downshift attack from https to http could shim webcrypto. So the existence of the webcrypto API is not proof that the site has loaded over https. Nor is any other check that you do in JS that the attacker could spoof / disable. I don't see the relevance to the question of whether webcrypto should be available over http, though.

StephenLynx commented 4 years ago

@mwatson2 What does that even means? You can't rely on the result of ANYTHING that's from IO. Are you going to force XMLHttpRequest to also be only usable under https? And if it's so important, why no one touched on it until chrome started requiring https?

StephenLynx commented 4 years ago

And @mwatson2 is not for people to justify crypto being on http, is for people to justify why it shouldn't be. Imagine needing a justification for every single tool on every single context. That's the same as justifying a liberty. Is for the people denying the liberty to justify why it shouldn't be allowed. Otherwise it's a very authoritarian scenario. This is how I feel: this decision was fruit of pure authoritarianism. If people want to run unsecure websites that's their problem, not the standard.

MattiasMartens commented 4 years ago

If the standard for correctness of an API is that it cannot be corrupted by an attacker, what is the argument for enabling any API whatsoever, on HTTP or HTTPS?

HTTPS may protect against MITM attacks, but it does not protect against all forms of code injection. WebCrypto and other libraries are still vulnerable to attacks in an HTTPS environment, they're just vulnerable to a narrower range of attacks that are in principle easier to guard against.

What's more, all other libraries are just as vulnerable. Do the other libraries not carry significant risks too if they are corrupted? Is their correctness not also impossible to guarantee?

Is it within an API's jurisdiction to decide whether the caller is taking too great a risk by assuming that it is what it says it is? I personally do not think so.

I understand the need to balance between security and usability concerns, but this seems like an opaque, arbitrary decision that does not at all take the user's experience into account.

Remember that the error the user gets is that the specified API does not exist, not that it's correctness "cannot be guaranteed".

mwatson2 commented 4 years ago

These are not unreasonable points. I'm just describing how the issue was decided because that context is way way back up the thread.

Different considerations apply to a cryptography API and other APIs that don't purport to enable implementation of secure protocols. End users have a particular interest in site developers not being given security footguns and end users interests come above site developers in the priority of constituencies. That's not to say there are no security footguns in the web platform, but the existence of others would not be an argument for introducing another one.

Nevertheless, there are trade offs, as I said, as this is how this one was decided.

StephenLynx commented 4 years ago

No, it was decided because google decided on it and you decided that papa google knows best. People were OVERWHELMINGLY against it but it went forward anyway.

Speaking of which, who are you and who do you work for? I can't seem to find anything about you.

MattiasMartens commented 4 years ago

I've been following this issue for over a year. It's been quite saddening to see the complaints coming in over that time. I guess I hoped that the weight of the complaints would eventually add up to some kind of meaningful change.

@mwatson2 you say it's “decided” so I guess there's no reason to keep following the issue. I just wasn't aware that W3C worked that way.