dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.02k stars 4.67k forks source link

Support SSLKEYLOGFILE in SslStream #37915

Closed bgrainger closed 5 months ago

bgrainger commented 4 years ago

SSLKEYLOGFILE is a feature provided by Chrome and Firefox that logs the pre-master secret to a file (specified by the SSLKEYLOGFILE environment variable) during TLS negotiation. The format of the file is documented here: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.

This allows encrypted data in a packet capture to be decrypted by Wireshark (without having to know the server's private key): https://wiki.wireshark.org/TLS#Using_the_.28Pre.29-Master-Secret.

As HTTPS and HTTP/2 become more popular, it will be increasingly more useful to be able to decrypt packet captures for network analysis; as per http://gary-nebbett.blogspot.com/2018/06/tracing-https-traffic-on-microsoft.html. (This use case also seems to be implied by https://github.com/dotnet/runtime/issues/35369.)

The ability to use a network trace analysis tool is especially useful when HTTP/2 is in use because the binary encoding of HTTP/2 can easily be decoded and nicely presented by such tools.

It would be great if TLS negotiation performed by SslStream could also log these secrets so that tools like Wireshark could be used to decrypt packet captures involving .NET clients. (Note that I'm not necessarily asking for the SSLKEYLOGFILE environment variable to be specifically supported, but just some opt-in way of dumping the same data; SSLKEYLOGFILE seemed like the most concise way to describe the feature.)

Besides HTTPS, another use case would be TLS negotiation in a different protocol; for example, the MySQL protocol upgrades from plain text to TLS, and being able to decrypt the packets in Wireshark would make diagnosing issues easier: https://github.com/mysql-net/MySqlConnector/issues/780#issuecomment-644259320

One potential problem is that Schannel doesn't support exporting this information, which could make it difficult to implement this feature on all platforms: https://social.technet.microsoft.com/Forums/en-US/4041d78a-21bb-44fd-9a96-6579ea8129d1/obtaining-sslkeylogfilelike-data-from-edge-et-al-schannel-clients?forum=messageanalyzer

ghost commented 4 years ago

Tagging subscribers to this area: @dotnet/ncl Notify danmosemsft if you want to be subscribed.

karelz commented 4 years ago

Triage: Useful for diagnostics to be able to view the traffic unencrypted.

booyaa commented 3 years ago

Just as an aside, I foolishly assumed that given libcurl is a dependency when bootstrapping .net core in Linux, that you could export SSLKEYLOGFILE as an environment variable (see docs). Sadly, I can confirm this does not generate a key log file. So this feature would be very helpful.

Update: apparently SSLKEYLOGFILE started working overnight. Using Azure App Service, custom Docker image based off mcr.microsoft.com/dotnet/core/aspnet:3.1.8-alpine3.12 stopped working again... after a restart

wfurt commented 3 years ago

While SSLKEYLOGFILE may be problematic because of forward secrecy and platform dependency, I was experimenting with alternative approach. https://github.com/wfurt/PcapStream gives you option to create decrypted captures with HttpClient, Kestrel and perhaps other scenarios. It is in early stage but any feedback would be appreciated.

The MSQL use case is still problematic AFAIK as the client frames encrypted bits to TDS. I don't know Wireshark can deal with that. I did not have luck.

wpbrown commented 3 years ago

Native support in the runtime would be great. In the meantime, I've created a solution for Linux with OpenSSL 1.1.1+ (i.e. Ubuntu 18.04 up on amd64 or 20.04 all archs) that works by wrapping SSL_new and dlsym. A basic LD_PRELOAD substitution doesn't work because the .NET runtime provides an open handle to dlsym, so dlsym needs to be wrapped as well.

upsampled commented 1 year ago

I understand this is a sensitive topic, but as TLSs is a common interface between organizations this feature is used to resolve disputes. IE: "See we are sending X but your are responding with Y and we expect Z. You can reproduce this collection procedure at your end as well to verify."

The idea that both parties can collect a common pcap, decrypt with their own SSLKEYLOGFILE, and reach consensus is commonplace.

raffaeler commented 1 year ago

Is there any ongoing work on this?

wfurt commented 1 year ago

not really. We agreed to support it in DEBUG build in way similar to #83001. But the urgency is lower IMHO as there are other alternatives.

raffaeler commented 1 year ago

@wfurt I am very concerned about letting this option available only for debug builds. There is a very high risk of seeing debug code being deployed in production which is problematic for other reasons. We already have secrets in the environment variables and this new variable has the same risk level.

Also, the provided workarounds are briliant but do not provide a solution for all the cases and require rebuilding which is something that can be hard to ask in large organizations.

wfurt commented 1 year ago

When I said DEBUG I mean debug build for runtime. That is not published to usual feeds so there should be no confusion IMHO. Of course, somebody can work hard and mess it up anyway but it seems like this is what browsers do so we would be on par. (for Linux)

The rebuild is somewhat intentional. To limit risk you mentioned we down't want to make it too simple. You can always build it always and have App specific overrides to enable it - if that meets policy of give organization.

raffaeler commented 1 year ago

@wfurt just to be sure:

To limit risk you mentioned we down't want to make it too simple

I understand the concerns, but if I think to the container workloads, should the environment variables be defeated, security is already gone.

wfurt commented 1 year ago

The PR is just for Quic, SslStream change can mimic that for TLS in future. And there are no debug binary bits available for download AFAIK. (but you can grab them for example from PR runs)

So the goal is to make it possible for somebody who is determined and knows what to do. Ease of access and wide availability is not priority at the moment.

raffaeler commented 1 year ago

Got it, thank you

hannadeng commented 1 year ago

Is there any update on this?

This feature is also very meaningful for gRPC on .NET. In most enterprise scenario, the gRPC traffic should be encrypted by TLS. A method to decrypt the TLS traffic is necessary for analyzing the streaming data. Whilst according to WireShark Wiki , currently C#/dotnet does not support TLS session secret key from either client or server side, while other languages like Java/Golang do support it.

It's better for .NET Core to support an easier way of decrypting HTTPS traffic, like exporting the TLS session secret key or any other method.

raffaeler commented 1 year ago

@hannadeng I don't think that this would be helpful in your case (gRPC). Unfortunately this debug feature is QUIC only and only works if you get the runtime from a different feed. For me it is a no-go.

hannadeng commented 1 year ago

83001.

Thanks @raffaeler . Maybe my question is not quite clear. Let me explain more. I understand https://github.com/dotnet/runtime/pull/83001. is only for QUIC and will not be applicable in my case (gRPC - HTTP2 protocol). Whilst I thought the original issue requested by bgrainger is a kind of general request for supporting decrypting TLS, including HTTP/2 protocol. Appreciate @JamesNK could help input more insights.

raffaeler commented 1 year ago

@hannadeng I am totally with you. I would love to have the opportunity to monitor the SSL convesation with the required level of precautions needed to avoid misues. Anyway in this thread I was said that they won't implement the decryption outside QUIC protocol. You, me and many others are stuck, that's it.

hannadeng commented 1 year ago

@hannadeng I am totally with you. I would love to have the opportunity to monitor the SSL convesation with the required level of precautions needed to avoid misues. Anyway in this thread I was said that they won't implement the decryption outside QUIC protocol. You, me and many others are stuck, that's it.

Got it. Thanks again!

wfurt commented 1 year ago

When https://github.com/dotnet/runtime/pull/83001 is done I plan to do it for SslStream as well - Debug build on Linux only. But I don't have much time for either one at the moment.

If there a reason why https://github.com/wfurt/PcapStream does not work for you @hannadeng ? It should give you ability to decode and debug HTTP/2 and gRPC.

hannadeng commented 1 year ago

When #83001 is done I plan to do it for SslStream as well - Debug build on Linux only. But I don't have much time for either one at the moment.

If there a reason why https://github.com/wfurt/PcapStream does not work for you @hannadeng ? It should give you ability to decode and debug HTTP/2 and gRPC. Thank you @wfurt , let me try this.

StiliyanKushev commented 1 year ago

Bump.

Cod3Bud commented 1 year ago

Please get this feature asap. It will be very helpful in debugging.

karelz commented 1 year ago

There is too much on our plate at the moment for 8.0 and only a little bit time left (1 month for fixing all known bugs + features should be finished by Monday), so the feature will most likely not make it in 8.0 - hence the milestone Future. In the meantime, can you use the workaround / PoC which @wfurt mentioned and asked about? https://github.com/wfurt/PcapStream

ManickaP commented 10 months ago

Tentatively re-opening to discuss the requirement for DEBUG build (as this no longer applies to browsers - Firefox, Chromiun, nor CLI tools - cUrl) And to make sure we implement this for Windows as well, see #94843

raffaeler commented 10 months ago

I would like to know if there is any chance to discuss supporting SSLKEYLOGFILE in release builds of the CLR, using an environment variable to enable easier diagnostics. To support this request:

If this is out of discussion for .NET 9, please make a statement so that we avoid 'noise' in this comments. Thanks!

ManickaP commented 10 months ago

I would like to know if there is any chance to discuss supporting SSLKEYLOGFILE in release builds of the CLR, using an environment variable to enable easier diagnostics.

That's the intent here.

If this is out of discussion for .NET 9, please make a statement so that we avoid 'noise' in this comments.

No, I'd like to solve this in .NET 9.

And thank you for describing your scenario, that will help make my case.

nathan130200 commented 6 months ago

Any updates?

ManickaP commented 6 months ago

Thanks for the remainder, I just kicked off an internal discussion about this.

upsampled commented 5 months ago

Can you elaborate on why the SslStream cannot emit the Traffic or Handshake secret?

OS SslStream QuicConnection
Windows Not possible Yes
wfurt commented 5 months ago

There is no API on Windows to extract the secrets @upsampled . SslStream does not implement TLS - it just wraps underlying OS functions and therefore it is dependent on the provided capabilities.

wfurt commented 5 months ago

If Windows client is your only one option thee are other ways how to peek inside - something like Fiddler or PcapStream should still work.

rzikm commented 5 months ago

Can you elaborate on why the SslStream cannot emit the Traffic or Handshake secret?

The underlying OS library (Schannel) does not give out secrets to the application. In fact, the secrets are not kept in the app process memory at all, but instead reside in lsass.exe process and there is IPC between the two under the hood. https://b.poc.fun/decrypting-schannel-tls-part-1/ may be an interesting read (although the method described may not be practical).

Schannel added support for exporting TLS 1.3 secrets specifically in order to support implementing QUIC (QUIC requires access to encryption secrets because it interleaves TLS + other protocol data + application data and applies custom encryption using the secrets derived by TLS layer). That support is intentionally limited to very specific use case where Schannel gives out unencrypted TLS messages and encryption secrets separately. IIRC, this mode does not work for TLS 1.2.

We could theoretically use that mode and apply TLS framing+encryption in .NET, but that has few problems:

All-in-all, the implementation cost far outweighs possible benefits and it might be better to use other tools like wfurt mentioned.

upsampled commented 5 months ago

Schannel added support for exporting TLS 1.3 secrets specifically in order to support implementing QUIC

Are those same exporting mechanisms available for TLSv13 over TCP? I am assuming this is to support 0-RTT or the PSK-binder mechanisms in TLSv13 and both also are possible over TCP.

I guess I am asking if the below is true

OS SslStream (<=TLSv12) SslStream (TLSv13) QuicConnection (TLSv13)
Windows Not possible Possible Yes
rzikm commented 5 months ago

Are those same exporting mechanisms available for TLSv13 over TCP?

No, let me clarify. The secrets are exported only if the TLS version is 1.3 and it is operating in "recordless mode" (where it strips outer encryption and provides unencrypted TLS messages).

SChannel does not know about TCP, it is simply exchanging data via buffers and sending data is up to other code.

Putting the stripped encryption back in .NET code is the complex and problematic part which is the main reason we won't invest into that, for reasons listed in my previous comment.

nathan130200 commented 5 months ago

Can you elaborate on why the SslStream cannot emit the Traffic or Handshake secret?

OS SslStream QuicConnection Windows Not possible Yes

In windows maybe you can consider using some OpenSSL wrapper instead of SslStream. Like OpenSSL Net does

wfurt commented 5 months ago

Can you elaborate on why the SslStream cannot emit the Traffic or Handshake secret? OS SslStream QuicConnection Windows Not possible Yes

In windows maybe you can consider using some OpenSSL wrapper instead of SslStream. Like OpenSSL Net does

we could. But that has massive implications for certifications, performance, security and integration with OS functions like Cert store. As I mentioned above, there are other ways how to solve debugging TLS traffic @nathan130200

nathan130200 commented 5 months ago

It makes sense, and it ends up becoming unfeasible to do this at the moment, especially when we talk about performance.

jimm98y commented 5 months ago

If Windows client is your only one option there are other ways how to peek inside - something like Fiddler or PcapStream should still work.

PcapStream looks nice, but it is only usable if you are the app developer (not always the case, you might want to debug a third-party app/component).

Fiddler is only usable if the app supports proxies and does not implement SSL pinning or similar mechanisms. It does not help with non-HTTP connections as far as I know.

Imagine you are an app developer using a third-party component that implements some protocol (e.g. BACnet SC, SNMPv3, SRTP...) and you'd want to debug an issue in the communication in between your app using that component and an actual device. For this example let's consider a scenario when the third-party component does not expose the underlying streams to the developer directly, meaning we have to go through some API and we cannot use PcapStream. If the component is using OpenSSL (with keylog for debugging), then it's quite easy: you configure the environment which you fully control to dump the keys, point Wireshark to that file and you'll see the decrypted traffic -> happy debugging. However, if that third-party library is not using OpenSSL, then you're out of luck. Is the issue you are trying to debug in your code? Is it in the third-party component? Is it on the device? What's happening on the wire?

I think there should be some easy opt-in mechanism like the one in OpenSSL to allow developers to peek inside the TLS encrypted stream and see the decrypted traffic, provided they fully control at least one side of the connection. But I understand from the previous comments that maybe it's not an issue of .NET, but Windows API in general. Is my understanding correct?