whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.01k stars 2.62k forks source link

Extend WebSocket interface to access and use certificates #4420

Closed d3x0r closed 3 years ago

d3x0r commented 5 years ago

https://html.spec.whatwg.org/multipage/web-sockets.html#network

There is currently no method to access or specify any information about the underlying connection object via the standard interface; especially for wss (TLS) connections.

Most Node.JS websocket interfaces provide an additional parameter on the websocket constructor that is the options to pass to opening the socket. This allows specifying options to the underlying TLS socket such as certificate to identify the client, or an additional certificate root to validate the server.

And also, after connecting with websocket to a wss connection, there is no way to get any of the certificates (especially, and/or including) the root of the certificate chain that was used to validate the connection. If I could get this, I could additionally verify there isn't a TLS proxy in the middle. In a development/test environment, I can put a proxy in place that provides responses to DNS requests, have a custom root certificate from the proxy installed, and monitor all inbound/outbound connections. With the certificates that were used to verify the connection (all of which is public information), I can use that to sign a response to the server. And while it might be argued that the proxy could also replace the content of the script that does the signing; the script used is spliced together a series of random functions that the server specified... the browser could be compromised in such a way that the request for certificate information from the websocket connection will just return the 'proper' certificate; so perhaps it should just be the server's current identifying cert that is more volatile than the root..

In the first case, specifying an additional option object, I could specify

   var socket = new WebSocket( "wss:// url", ["protocols"], {ca: "..." } );

could also suffice

from https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options

ca | <string[]> | | <Buffer[]> Optionally override the trusted CA certificates. Default is to trust the well-known CAs curated by Mozilla. Mozilla's CAs are completely replaced when CAs are explicitly specified using this option. The value can be a string or Buffer, or an Array of strings and/or Buffers. Any string or Buffer can contain multiple PEM CAs concatenated together. The peer's certificate must be chainable to a CA trusted by the server for the connection to be authenticated. When using certificates that are not chainable to a well-known CA, the certificate's CA must be explicitly specified as a trusted or the connection will fail to authenticate. If the peer uses a certificate that doesn't match or chain to one of the default CAs, use the ca option to provide a CA certificate that the peer's certificate can match or chain to. For self-signed certificates, the certificate is its own CA, and must be provided. For PEM encoded certificates, supported types are "TRUSTED CERTIFICATE", "X509 CERTIFICATE", and "CERTIFICATE".

the cert option for client identification backwards might also be useful.

I appreciate that the Websocket API is so minimal.

There also is a poor error specification if websocket has a critical error. There is no way to catch connection failures, for instance, or get any information about why the connection failed. There is an exception usually logged to the development console (I don't have an example handy), but there is no information even passed to the error or close handler. A failure to find a root certificate for instance, but also bad parameter in certificate (something firefox is giving me). I would like to know that there was a critical error that was thrown, to know not to just sit there and retry, but to change to a different page with information on how the user can resolve the issue.

Chrome, Edge and Firefox devtools even have very little information available about the certificates used for WSS (although HTTPS connection you can get the whole chains... even just clicking the info icon to the left of the URL in the address bar... )

domenic commented 5 years ago

In general browsers have not let clients control what CAs are in use; that is a decision made by the OS (and thus the user), possibly also involving browser security teams (IIRC Mozilla bundles its certs instead of using the OS's).

I don't think we'd change this, either for HTTPS or for WSS.

/cc @whatwg/security, @ricea.

d3x0r commented 5 years ago

In general browsers have not let clients control what CAs are in use; that is a decision made by the OS (and thus the user), possibly also involving browser security teams (IIRC Mozilla bundles its certs instead of using the OS's).

(that's only a response to 1/2 of the issue; however I would like to comment on that, not to drag it out or beat it to death)

It's not practical of course for everyone that wants to have a secure chain have their root certificates distributed; which sort of makes that a premium resource. 100,000 applications each with a (generous) 8K certificate would be 800,000,000; almost 1G for a bunch of roots that are mostly unused.

With a little assistance it is possible for some users to install root certificates extra on their system; (or in their application ala Firefox). It encourages users of android to increase the security access of their device even, so it's almost a good thing to require in that case.

That's exactly the decision I'm trying to make. It's relevant only for participating in my OS.
The gateways (front endpoints) are secured using public certificates.

I do try generally to leverage Hanlon's Razor Never attribute to malice that which is adequately explained by stupidity although I guess it doesn't really apply. It seems more like corporations have always been in control of browsers and https certficates such that they can monetize creation/signing of certificates, raising the bar above what a hobbiest might want; (there are of course lets encrypt that lets you interact for free for a limited number of certificate chains) which covers most 'hobbiest' allowing an entry point. But with a distributed mesh of services, each with their own service identifying certificates, it's not sufficient to rely on a third party; However, the certificate can be delivered over HTTPs/Wss over the existing public certificates, and allow access to the services in my operating system.

ricea commented 5 years ago
sleevi commented 5 years ago
  • I recall there was some discussion about providing certificate chain information on successful fetch for HTTPS in the past. I assume there is a good reason why this didn't happen. @sleevi https://github.com/sleevi, do you recall anything about this?

Yes. It is a privacy bug that would disclose information about the user’s network connectivity, allowing for fingerprinting or even identifying the user directly (e.g. if a traffic inspecting middlebox is used).

There is fundamentally no safe way to expose this to the Web Platform that does not, in some way, represent a new information side-channel. The removal of plugins was a positive and meaningful step to closing that hole, as such issues had been demonstrated with Flash and used adversarially.

  • Being able to specify alternate root certificates for a WebSocket connection is an interesting idea, but there are enough security issues that I don't think it will happen. For example, suppose an attacker tells you to connect to wss://accounts.google.com/, using their own certificate as the root certificate, and forges the DNS response so that accounts.google.com resolves to their server. The browser will send your Google cookies to their server. The attacker can now impersonate you perfectly.

Correct. Functionally, at best this represents a step equivalent to mixed content - as the user agent cannot meaningfully guarantee you are speaking to the intended server with a commonly understood level of security. At worst, however, this represents a way to undermine the security of users that can lead to compromise.

To the extent a UA has an obligation to ensure the meaningful protections of TLS are consistently applied, it is fundamentally incompatible to expose control of such information to the page. Such control is no different than the control afforded over the origin and attempting to load something over ws://, which UAs today actively block even though the API allows it.

d3x0r commented 5 years ago
  • I recall there was some discussion about providing certificate chain information on successful fetch for HTTPS in the past. I assume there is a good reason why this didn't happen. @sleevi https://github.com/sleevi, do you recall anything about this? Yes. It is a privacy bug that would disclose information about the user’s network connectivity, allowing for fingerprinting or even identifying the user directly (e.g. if a traffic inspecting middlebox is used). There is fundamentally no safe way to expose this to the Web Platform that does not, in some way, represent a new information side-channel. The removal of plugins was a positive and meaningful step to closing that hole, as such issues had been demonstrated with Flash and used adversarially.

The discovery of middlware box is exactly what I'm seeking, to be able to warn the user that their traffic is able to be monitored.

The information about the certificate chain used for the socket should all already be known by the server, and/or anyone connecting to that service, since the certificates (other than the root) are sent 'in the clear'; but even those are easily browsed.

It's not any information that can't be gleemed by just connecting to the server yourself... except when the connection has a deliberate MITM.

Re lack of meaningful error information on the open; I don't see how it would be useful to know (other than for the proper application) that some websocket connection failed because they don't have a root certificate? Or otherwise have obsolete certificates.
Fingerprint would be 'connection from client failed'

ricea commented 5 years ago

Re lack of meaningful error information on the open; I don't see how it would be useful to know (other than for the proper application) that some websocket connection failed because they don't have a root certificate? Or otherwise have obsolete certificates.

This gives away a treasure trove of information to a potential intruder, including:

When you're trying to break into a network that is behind NAT or Firewall, you're working in the dark. Every fragment of information about what's on the other side is valuable.

d3x0r commented 5 years ago

Re lack of meaningful error information on the open; I don't see how it would be useful to know (other than for the proper application) that some websocket connection failed because they don't have a root certificate? Or otherwise have obsolete certificates.

This gives away a treasure trove of information to a potential intruder, including:

  • The host resolves
  • There is a route to the IP
  • The host exists and is responding
  • There is a service running on that port, and it is responding

you get all of that just opening a websocket. WS:// port. (or not); and most of those errors are distinguishable based on time of failure anyway; so hiding the information just makes it harder to make useful decisions about the situation.

  • It speaks TLS

you have to already know that; it's up to the client to select that.

When you're trying to break into a network that is behind NAT or Firewall, you're working in the dark. Every fragment of information about what's on the other side is valuable.

sleevi commented 5 years ago

The discovery of middlware box is exactly what I'm seeking, to be able to warn the user that their traffic is able to be monitored.

Yes, and that's exactly the set of information that User Agents should not be exposing to the Server. If such a connection works, it is because the User configured it to work, and the User Agent seeks to facilitate the User's wishes (for example, that they installed such a locally trusted certificate). Further, given that such information can contain PII (e.g. the certificate used by the MITM box may be distinct for the user accessing the site), it's a complete non-starter to expose that information to the server.

If the connection fails, it's ambiguous as to whether it's due to adversarial attack or through User misconfiguration. Because it's far more likely to be User misconfiguration, in practice, revealing such information in the error case again fails the test of being in the User's interests.

Even if it chains to what the browser deems is a 'publicly trusted' anchor (for example, part of the default set of the UA) may still reveal more information than otherwise accessible today. For example, certificates are part of a directed cyclic graph; the path taken within that graph can vary between UA to UA and between versions of UAs, as trust stores change over time. A consequence of this is that even though the server may be aware of all possible paths in that graph from the server's certificate to possibly trusted certificates, revealing the specifically selected path discloses indirect information about the User Agent, such as the version of its trust store or the version of its path selection algorithm.

The consequence of all of this is that, in all cases, it reveals information about the user - small pieces, in the case of 'publicly trusted' certificates, to potentially large pieces in the case of MITM certs.

These problems have been well explored in the past; for example, in the careful discussion and deliberation about the set of information to expose to Network Error Logging or Resource Timing

So exposing the information about the certificates is actually something to explicitly prohibit, in the interest of the User and their privacy. Similarly, allowing control over how the TLS verification is performed functionally undermines the security assumptions and assurances that the user agent has and is able to provide, no different than loading content from a SecureContext enabled page via HTTP or WS.

I know it may seem unsatisfying, but there's no identified safe way that balances the Priority of Constituencies and allows that specific functionality. It's not for lack of trying or effort either - there are certainly use cases that 'could' be enabled, but the underlying functionality represents too much harm and risk to users to justify exposing.

d3x0r commented 5 years ago

https://stackoverflow.com/questions/6506068/can-a-ssl-certificate-be-signed-by-multiple-certificate-authorities (No) A couple other similar questions no... implementing x509 certificate generation using the openssl api,

    // set issuer name frome ca
    if (!X509_set_issuer_name(cert, X509_get_subject_name(x509))){
//or
subj = (ASN1_STRING *)X509_get_ext_d2i( x509, NID_subject_key_identifier, NULL, NULL );
            X509_add1_ext_i2d( cert, NID_authority_key_identifier, &kid, 0, X509V3_ADD_DEFAULT );

there's only one issuer name, so it would be hard to have multiple roots. or be cyclic.... Though I suppose one could mismatch them and form one chain one way and the other links to another...

while searching, I did see there's other standards for PKI that have extensions for multiple signers, but TLS doesn't use one of them.

Your case makes a lot of assumptions that the user has put the middleware there themselves and wants to keep its low-bit (quick-generating) keys secret; but the larger case is that the user in on eCorp, or eCountry network and has no clue; and the system image is maintained on eCorp servers and keys distributed outside of their knowledge. I suppose they would have signed some sort of form advising them that all of their activity will be monitored so they should already know?


and on error states, and lack-there of This will fail to connect, and assume that it's a user configuration error, as mentioned. But the steps it brings you do does not fix browser errors that remain that are beyond the users ability to configure; and there's no way to know that, hence the user is continually prompted to do the same thing they just did. https://www.chatment.com/

and unfortunately the result error result happens after exactly the same number of packets in both cases making timing not a distinguishing character for the errors. (firefox still hates the chain, and refuses to tell even the dev console why, but it's in the path of having found the correct root installed, or not is a difference of microseconds on most systems).

knowing 'failed to build chain' vs 'hated something in your chain' would help a lot.


clicked on resource timing and only glanced, as I was closing tabs I found the Network Error Logging draft; that would appear to satisfy the onerror issue... I could work with that...

sleevi commented 5 years ago

https://stackoverflow.com/questions/6506068/can-a-ssl-certificate-be-signed-by-multiple-certificate-authorities

(No) A couple other similar questions no... implementing x509 certificate generation using the openssl api,

// set issuer name frome ca if (!X509_set_issuer_name(cert, X509_get_subject_name(x509))){ //or subj = (ASN1_STRING *)X509_get_ext_d2i( x509, NID_subject_key_identifier, NULL, NULL ); X509_add1_ext_i2d( cert, NID_authority_key_identifier, &kid, 0, X509V3_ADD_DEFAULT );

there's only one issuer name, so it would be hard to have multiple roots. or be cyclic.... Though I suppose one could mismatch them and form one chain one way and the other links to another...

while searching, I did see there's other standards for PKI that have extensions for multiple signers, but TLS doesn't use one of them.

I’m sorry for not having provided clearer references. You are correct that a certificate has a single encoded issuer name and signature. However, there can be an unlimited number of certificates that represent possible paths in the graph to build.

RFC 4158 provides a number of clear illustrations that demonstrate how this works with TLS, and the complexity.

Your case makes a lot of assumptions that the user has put the middleware

there themselves and wants to keep its low-bit (quick-generating) keys secret;

No, this is not correct. The secret includes the existence of the certificate itself and it’s contents.


  • It could be possible to return information from the certificate without giving away the whole thing (the signature hash for instance, things like the above subject id hashes can be replicated easily, such that all the common-name information is identical to the source; other than they lack the specific key the real creator used, so the signature must be different);

No. This reveals they exist.

  • or something like a sha has of either the whole chain or parts, which, while identifying, are not reversable to specific information; and should be a simple equality check for a similar algorithm on the server,

No. This reveals they exist.

-

  • or an API to submit a bit of data to be signed and returned, similar to the sec key in the websocket header

This is based on a misunderstanding of the concern.

-

and on error states, and lack-there of This will fail to connect, and assume that it's a user configuration error, as mentioned. But the steps it brings you do does not fix browser errors that remain that are beyond the users ability to configure; and there's no way to know that, hence the user is continually prompted to do the same thing they just did. https://www.chatment.com/

https://www.chatment.com/

and unfortunately the result error result happens after exactly the same number of packets in both cases making timing not a distinguishing character for the errors. (firefox still hates the chain, and refuses to tell even the dev console why, but it's in the path of having found the correct root installed, or not is a difference of microseconds on most systems).

knowing 'failed to build chain' vs 'hated something in your chain' would help a lot.

I’m sorry, but this still reveals too much information. The error states existence is alone too much.

d3x0r commented 5 years ago
  • It could be possible to return information from the certificate without giving away the whole thing (the signature hash for instance, things like the above subject id hashes can be replicated easily, such that all the common-name information is identical to the source; other than they lack the specific key the real creator used, so the signature must be different); No. This reveals they exist.

They are known to exist. The connection was specified with wss.

  • or something like a sha has of either the whole chain or parts, which, while identifying, are not reversable to specific information; and should be a simple equality check for a similar algorithm on the server, No. This reveals they exist

They are known to exist. The connection was specified with wss. .

    • or an API to submit a bit of data to be signed and returned, similar to the sec key in the websocket header This is based on a misunderstanding of the concern. I’m sorry, but this still reveals too much information. The error states existence is alone too much.

and; sorry to have to repeat myself, but of course.... They are known to exist. The connection was specified with wss.

Edit: Add that it has to be OPEN state; otherwise your right, a probe that fials to negotiate could have cert fragments on the socket and be tested for in close before open.

sleevi commented 5 years ago

I’m sorry that it’s not clearer, but the principles of privacy and security that apply here are that arbitrary web pages should not be able to gain arbitrary information about the user’s network.

Exposing anything about the path the client took to the network - for example, the existence of a proxy or intermediary - is a privacy issue. This ranges from the existence of such an intermediary to information about it, which is why providing information about the TLS connection is problematic. There was significant discussion about whether it was even safe to reveal the protocol used to connect to a given server. More information - such as hashes or actual certificates - is even more deeply problematic. As I explained, at best, it’s a fingerprint, but more commonly, it reveals direct and unique identifying information about the user.

d3x0r commented 5 years ago

It's still my opinion that the user's ability to know they are insecure is greater than others being able to group them into a subset of users that are behind that middlware. It give no more than connecting to a website on IPV4 or IPV6 would give. IPv4 will probably be natted so it all comes from the same address, and IPV6 will be on the same subnet. It shows there is something different, but not what or how. The difference in digital siganture should be statistically indistinguisable for model A or model B or middlware vs the server's correct signature.


Edit: Having thought about various combinations of the above i can add that i would like to secure the server side also against bad actors, which end up identifiable. The purpose is validity and integrity of the connection. Someone masquarading on TOR with a custom middlware that logs to reverse engineer information they shouldn't really be accessing whether they agreed to the application terms of use or not would be 'identifiable'. An otherwise indifferent actor should prefer the connection with better security anyway; and re other products using this as an identification of them, again it goes back to the IP which won't be floating; and a middlware is probably a concentrator also.

sleevi commented 5 years ago

Given the discussion and expectations have shifted, due to this discussion, perhaps you could clearly summarize what the new functionality you’re advocating is, in the interest of clarity.

It sounds like some probabilistic check that 50/50 returns the correct answer as to whether or not a user is behind a middlebox, as that’s the only way to maintain privacy of the user. However, if that’s not correct, a fuller explanation may suffice.

As mentioned earlier, the principles of preserving the user’s privacy means the following information cannot be revealed:

Any of these would reveal information otherwise inaccessible to the Web Platform and assist in tracking users or denying service, functionality, or content that the User would otherwise be able to see.

d3x0r commented 5 years ago

Yes, this is a bit of X-Y problem that I came stating I wanted Y when what I need is X, while Y is merely a hypothetical idea of how X would be accomplished; so yes it has shifted somewhat.

Yes, 1 bit (true/false) whether there is middlware or not; and would be additional information to just the IP. so (3) from above was some method that gave back a value either with or without some other value applied... Something like performing a KangarooTwelve /sha3/sha2 on the signature of the leaf certicates digital signature would provide uniform result regardless of hashing used to create the certificate (which (c|w)ould have been more detail as to whether model A or B is used).

My goal is to make a best effort to validate the connection as non-tampered and secure. While TLS are touted as providing that, they can't, alone. And this is an approach from the top-down to discuss how this can be accomplished.

I have no attachment to any particular method.

The probability is highest that there is no issue.

I'm not unaware of any of the things that have been brought up. (sorry for the double negative)

A middleware-compatible browser might use a SOCKS-ish protocol with middlware and relay the proper answer for the websocket API to return; it's certainly not foolproof, but then user agent will probably identify that.

d3x0r commented 5 years ago

I'll drop this basically, soon, but I'll probably leave with the 'last word'. I understand the challenges; and maybe it's not even enough for food for thought... I could use an external connection validator program; and if they are unable to install and use the program, then assume the connection is compromised. I would have liked the tool to perform the test be available in this platform.

sleevi commented 5 years ago

My goal is to make a best effort to validate the connection as non-tampered

and secure. While TLS are touted as providing that, they can't, alone. And this is an approach from the top-down to discuss how this can be accomplished.

Thanks for the explanation! I think this may reveal a disconnect in goals, and was something I’d tried to capture earlier in the conversation, but may not have done a great job at it. The User is ultimately the one who determines whether the connection is secure, based on what they’ve instructed the User Agent to do.

Thus, if an intermediary is present, and it works, it could only have worked because the User’s machine or configuration indicates it should work.

If an intermediary is present, and it doesn’t work, it’s ambiguous to the UA whether it should have worked (indicating a user configuration issue) or should not have worked. Further, it cannot distinguish whether that intermediary is hostile (active MITM) or “benign” (e.g. misconfigured antivirus that does TLS inspection)

My understanding of the problem is that you’re looking for a signal in this latter case, in which things fail, but fail due to an intermediary - so that the web page can direct the user to reconfigure things. The challenge with such a design - which includes hashing of the signature and revealing one bit from it - is that it reveals the existence of an intermediary and the user’s network configuration, and many deliberate steps have been taken by UAs not to do that.

Understanding the motivations and use cases that sacrificing user privacy in this regard may help make a better case for the cost/benefit analysis for UAs, which could then help determine whether this is a design or problem space they would like to engage in. As it presently stands, ideas in this space have come up in the past, albeit with other motivations and use cases, and the trade-offs and principles involved have lead to it being more costly than beneficial.