Check ssl protocol, cipher & other properties in an asp.net mvc 4 application
Asked Answered
O

3

31

Because of compliance reasons we have to switch off the support of some ciphers and SSL2 on our webservers. This is not really a problem, but we would also like to inform them, after their successful login into the website, that we suggest switching on TLS 1.2 in their browser in case they are not already connecting to the server with TLS 1.2. So the question I have is:

How can I detect the protocol and cipher used in an https request to an ASP.net (MVC 4) application running in IIS?

I know that there are ways to log the SCHANNEL request to the event log and then read them out again, but this sounds very ugly to me.

And I have seen that the System.Net.Security.SslStream has the properties that I would need, e.g.: CipherAlgorithm, HashAlgorithm, KeyExchangeAlgorithm & SslProtocol, but I'm not sure where I can get these properties in my Controller Action in a mvc4 application.

Oxendine answered 29/7, 2014 at 5:57 Comment(0)
P
38

The bad news, as determined by ILSpy, is that there is no way to get to a System.Net.SslStream instance from anywhere inside ASP.NET. That class is used for direct programming against the network, for example by the WCF framework. The best you can do from ASP.NET (whether using System.Web or OWIN on top of IIS or HttpListener) is to get a server variable (see list of IIS server variables) for whether the connection is secured by whatever secure transport was negotiated with the client.

As far as deterministically reading data from the event log during a web request... that seems scary. But if you can make it work, please share the code. :)

Alternatively, you could try to implement your own Owin host (aka web server!) that uses SslStream underneath. Maybe. :P See this article for a thorough introduction to SslStream programming.

But since you're already able to turn off certain protocols on your server (as in this article, I assume)... You could set up your site on two different subdomains, e.g. www.example.com and secure.example.com, where the former is a vanilla web server and the latter is configured to only accept TLS 1.2 connections. Then you'd write some bootstrapping logic that gets served from www.example.com and attempts to make an AJAX request to secure.example.com/securityUpgradeCheck (possibly with a nicely styled spinner animation and "Please wait, attempting to secure this connection" text to impress your users :)). If that request succeeds, the user can be redirected to secure.example.com (probably permanently, since that user agent is then known to support TLS 1.2 unless for some reason the user changes their browser settings).

For added impact, order an EV SSL certificate for the secure domain so your users will notice the upgrade in security. :)

UPDATE: I did some more digging, on the theoretical basis of writing a custom (native) ISAPI filter (or extension) to get at this information via the SChannel API. At first I was hopeful because I discovered a function HSE_REQ_GET_SSPI_INFO that would return an SSPI CtxtHandle structure, and which you could call from a custom ISAPI extension via the EXTENSION_CONTROL_BLOCK ServerSupportFunction function. That CtxtHandle structure, it turns out, represents an SChannel context and can get you a reference to a SECPKG_ATTR_CONNECTION_INFO attribute with which you can retrieve SSL connection-level information (the same information that's surfaced in the SslStream class in .NET, as far as I could tell). However, sadly, Microsoft anticipated that possibility and decided that this information is only available if you are using client certificates. The behavior is "by design."

There was one (native) SSPI function, QueryContextAttributes (Schannel), that I discovered during a long hunt through MSDN which may work. I haven't tried it, and it could simply fail for the same "by design" reason as the ISAPI API limitation linked to above. However, it may be worth a try. If you want to explore this route, here is an example of an ISAPI extension. Actually, with this approach you might be able to write an IIS module instead, using the newer IIS 7.0+ SDK.

But, assuming you don't have the luxury of requiring client certificates and that long shot doesn't work, that absolutely leaves only two options.

  1. Use a different web server (Apache, etc.), running on the same physical/virtual machine but on a different port, etc. (as per our discussion in the comments, since you can't spin up another machine). If you only want to give the client an informational message, then this approach, coupled with an AJAX request, might be sufficient. Yes, a different port could well be blocked by a firewall somewhere, but hey - it's only an optional informational message anyways.
  2. Rely on the semi-brittle approach with the System event log. Enable Schannel event logging and then write some event log querying code to try to correlate the request with the last-logged Schannel event. Note that you'll need to find a way to reliably correlate whatever gets put in the event log with the current HTTP request, so you might also need to write an ISAPI filter/extension or IIS module in this case to find the Schannel context handle (which is what I'm assuming the correlation would be based on).

By the way - is your load balancer configured to do any SSL interception? Because then this whole thing is moot anyways... Just a thought to consider.

UPDATE: Enabling Schannel logging netted this gem:

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Schannel" Guid="{1F678132-5938-4686-9FDC-C8FF68F15C85}" /> 
    <EventID>36880</EventID> 
    <Version>0</Version> 
    <Level>4</Level> 
    <Task>0</Task> 
    <Opcode>0</Opcode> 
    <Keywords>0x8000000000000000</Keywords> 
    <TimeCreated SystemTime="2014-08-13T02:59:35.431187600Z" /> 
    <EventRecordID>25943</EventRecordID> 
    <Correlation /> 
    <Execution ProcessID="928" ThreadID="12912" /> 
    <Channel>System</Channel> 
    <Computer>**********</Computer> 
    <Security UserID="S-1-5-18" /> 
  </System>
  <UserData>
    <EventXML xmlns:auto-ns3="http://schemas.microsoft.com/win/2004/08/events" xmlns="LSA_NS">
      <Type>client</Type> 
      <Protocol>TLS 1.2</Protocol> 
      <CipherSuite>0x3c</CipherSuite> 
      <ExchangeStrength>2048</ExchangeStrength> 
    </EventXML>
  </UserData>
</Event>

This can be read out directly from managed code. I think the UserID only corresponds to the IIS worker process SID, unfortunately, but assuming you can come up with some kind of correlation heuristic, you could set up a background thread to continually poll the event log and give you a list of recently established client handshakes (use a ConcurrentDictionary perhaps).

There. That's it. No more curious investigating for me. I'm done. :P

Prorate answered 2/8, 2014 at 1:16 Comment(14)
Thank you for your answer. I am using the IIS Crypto tool from Nartac to disable Ciphers and so on, but this results in the same registry keys as described in your link. Your idea with the ajax request to the secure site is not bad, but this would mean that I need 1 extra server just for probing if TLS 1.2 is enabled which I guess is a bit overkill. For compliance and other reasons I won't be able to build my own web server either. I'll look into further options and let you know if something comes up. Cheers!Oxendine
I was actually thinking you could just make that a virtual machine, potentially even hosted on your existing server. On the other hand, if you already have more than one web server, you could do a kind of load-balancing by client's TLS version. That would be a bit more practical since all the servers are still busy serving requests. But good luck! The only other alternative I can think of is using a different web server (Apache? etc.?) that has an inspectable secure transport layer.Prorate
We already have a proper load-balancer and and a pretty decent infrastructure behind the site. It's not just one server that serves the application to the client. I could probably add a specific apache webserver that runs all these validations and returns a little json to the page which I then can use to show the message. But I already know now, that our IT department will not be too happy to maintain an apache webserver and bringing it through the compliance checks is just a nightmare... I was hoping it could be done with a standard IIS installation.Oxendine
Now there's an idea - can you configure a rule in your load-balancer to direct different clients to subfarms based on the negotiated TLS version? Admittedly a long shot.Prorate
But actually, my idea is valid if you have a load-balanced infrastructure with multiple IIS servers as you described. So this does theoretically work. What I'm suggesting is that you subdivide your existing server farm into one subset that serves TLS v1.2+ users (under a 'secure.example.com' subdomain), and the remaining servers that serve all other users (under the standard 'www.example.com' subdomain that users hit first). You'll then have two load-balanced server farms, serving different subdomains, and the one subdomain is restricted to TLS v1.2+.Prorate
Here's an example of multiple client SSL profiles on the F5 Q&A website that suggests such a thing may be possible to do on the load balancer: devcentral.f5.com/questions/… (A side benefit is that your IT people will usually be delighted to get a chance to exploit the load balancer's advanced features, in my experience. :))Prorate
I'll have a chat with our IT team lead. This all might lead to an usable solution, but unfortunately my original questions just seems to have no answer. I hope something comes up in the next two days, otherwise I'm going to increase the bounty. Nevertheless, thank you for all the effort!Oxendine
@JamesBlond, I did some more digging that hopefully clarified the issue somewhat. Officially Microsoft says that you cannot get at this information from within IIS by design, unless you are using client certificates. There may be a loophole in the API that exposes this anyways, but I'm not convinced it will work. Anyways, see my updated answer.Prorate
Thank you Lars for all your effort. If no one comes up with something better I'll definitely award you the bounty. Cheers!Oxendine
+1'd on the sheer level of research and experimentation!Howbeit
Thanks. :) Hope you find a solution that works for you!Prorate
You mention that you can use a server variable to check "whether the connection is secured by whatever secure transport was negotiated" I'm just looking to see if the transport is using TLS1.2. Can this be done from a web application? The link you provide doesn't really mention if this is provided to the application.Riocard
Hi @Robba, no, unfortunately the IIS API will only tell a web application if the connection is secure; it will not tell the application which secure transport was negotiated. The rest of my answer gives the long version for how you can work around it by using a separate "canary" server that you've preconfigured to only allow TLS1.2+, and have the client make requests to that to see if they succeed.Prorate
@Riocard see my recent answer linking to #53462135 that explains how this is possible in IIS 8.5 and higher.Geyer
P
7

Reading this with interest as we have exactly the same problem.

It occurred to me that there must be a public web service available to query this type of information and I did a little research and found this: https://www.howsmyssl.com/s/api.html

The API is here

This API can be called from client side Javascript and it returns standard JSON which can be easily parsed and a suitable alert shown to your users.

You can also send the information to your own web service for logging purposes and try to combine the information with your existing IIS logs.

The only issue you face then is relying on a third party. This can be mitigated by standing up your own server and hosting the code which is freely available on GitHub.

We are going to be working on this solution so once I have some code in place I will update this answer.

Thanks again to Lars for the comprehensive answer above. I would have added this as a comment to that post however I thought it worth creating a separate answer so people can find it easier.

Paddock answered 20/8, 2015 at 12:0 Comment(5)
Brilliant, thank you, I could have used that a year ago :-)Oxendine
What solution did you end up implementing James?Paddock
A not very technical one... We informed all our customers by email about the changes and that they can check their browsers on the SSL Labs website and what they have to do to get up to date: ssllabs.com/ssltest/viewMyClient.htmlOxendine
@Paddock See my recent answer linking to #53462135 for a documented solution.Geyer
Hey @Paddock Did you get chance to make out code to detect client browser's TLS version?Genip
G
0

How do you check the Negotiated TLS Handshake from the Server? has the way to get this under IIS 8.5 and higher in ASP.Net MVC / WebAPI. At least it worked for me and a couple of others when I answered it yesterday.

Geyer answered 28/11, 2018 at 16:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.