7

If I have a dozen endpoints, and my WebAPI Service is configured for TLS 1.1 and TLS 1.2, how do I check each incoming endpoint request to see which version was negotiated?

So if a consumer of my endpoints currently only supports TLS 1.0 and TLS 1.1, they'll (obviously?) negotiate a TLS 1.1 handshake. But if a different consumer supports TLS 1.2 and TLS 1.3, they'll (obviously?) negotiate a TLS 1.2 handshake.

I want to track all of my consumers to see what handshakes are being negotiated. How do I do that per-request?

No Refunds No Returns
  • 8,092
  • 4
  • 32
  • 43
Suamere
  • 5,691
  • 2
  • 44
  • 58
  • I posted some informations on this matter here: [Which TLS version was negotiated?](https://stackoverflow.com/questions/48589590/which-tls-version-was-negotiated?answertab=active#tab-top). This, of course, applies to the client-side of the world, I'm not sure it can be useful here. But maybe you can find something interesting among the notes. – Jimi Nov 25 '18 at 00:58
  • @Jimi Great info. There were some key terms in there that might have lead to something, but additional searching didn't result in anything substantial. I'm sure I'll use it more in my research, but for now, it is very one-sided. :) – Suamere Nov 25 '18 at 01:39
  • 1
    According to [This similar Question and Answer](https://stackoverflow.com/questions/25009018/check-ssl-protocol-cipher-other-properties-in-an-asp-net-mvc-4-application), my question is pretty close to duplicate and the answer is **You Can't**. I hope somebody comes along and says otherwise, or maybe even in .NET CORE. But even that looks like a resounding No. Boooo. – Suamere Nov 25 '18 at 02:05
  • Well, you're right. This is something that is really missing in the managed implementations of the Network/Ssl streams on the higher layers (TCP knows everything). On both sides. Anyway, since I'm still keeping an eye out on this matter, I'm bookmarking your question and if something comes up I'll let you know. – Jimi Nov 25 '18 at 02:39
  • 1
    Not a great answer but ... if you can support multiple endpoints you can configure them separately. Why do you want to know which version of TLS is in use? Seems like you would document your security requirements and update them as necessary based on your business needs. – No Refunds No Returns Nov 26 '18 at 05:32
  • @NoRefundsNoReturns Logging, Education and Troubleshooting. I don't know of any business logic in the code that would change based on the information. But when dealing with a large number of integrations and consumers on the Front and Back Boundaries of my MicroService, I want that additional information. Specifically because Security Requirements are changing rapidly recently. – Suamere Nov 26 '18 at 14:02
  • @Jimi you may like the updated answer below. – No Refunds No Returns Nov 28 '18 at 04:23

1 Answers1

6

If you are using IIS it looks like you can add some extended logging to your IIS logs.

https://cloudblogs.microsoft.com/microsoftsecure/2017/09/07/new-iis-functionality-to-help-identify-weak-tls-usage/

plagiarizing ... er ... quoting for posterity:

To enable this new functionality, these four server variables need to be configured as the sources of the custom fields in IIS applicationHost.config. The custom logging can be configured on either server level or site level. Here is a sample site-level configuration:

<site name="Default Web Site" id="1" serverAutoStart="true">
 <application path="/">
 <virtualDirectory path="/" physicalPath="C:\inetpub\wwwroot" />
 </application>
 <bindings>
 <binding protocol="https" bindingInformation="*:443:" />
 </bindings>
 <logFile>
 <customFields>
 <clear />
<add logFieldName="crypt-protocol" sourceName="CRYPT_PROTOCOL" sourceType="ServerVariable" />
<add logFieldName="crypt-cipher" sourceName="CRYPT_CIPHER_ALG_ID" sourceType="ServerVariable" />
<add logFieldName="crypt-hash" sourceName="CRYPT_HASH_ALG_ID" sourceType="ServerVariable" />
<add logFieldName="crypt-keyexchange" sourceName="CRYPT_KEYEXCHANGE_ALG_ID" sourceType="ServerVariable" />
 </customFields>
 </logFile>
 </site>

Each SSL info field is a hexadecimal number that maps to either a secure protocol version or cipher suite algorithm. For an HTTP plain-text request, all four fields will be logged as ‘-‘.

Me again:

It looks like CRYPT_PROTOCOL can be 400 for TLS1.2, 40 for TLS 1.0, 10 for SSLv3 in the IIS Text logs.

From the examples, it looks like there might be ServerVariable values on each Request if you want to try to include in custom logs that are a bit easier to customize than the IIS log itself.

Great Question! and I may have a chance to use this answer m'self.


So ... it looks like you CAN get the ServerVariables from WebAPI but only in an unexpected way. See the snippet below. It seems if you enumerate the collection or call the Keys property all you get is some subset of variables. But if you explicitly request the CRYPT_* variables before any of those actions then you can indeed get them from your controller. I tried this on WebAPI 5.2.6 targeting .net 4.6.2 running under IIS as an Azure Classic Cloud Service. I suggest trying this and seeing if it works for you. If you have a more recent reference for Server Variables, please edit this answer and replace https://learn.microsoft.com/en-us/iis/web-dev-reference/server-variables with your link.

Below worked for me on date of writing for environment listed. It may change in the future. For production I would definitely move this into a helper method.

if (Request.Properties.TryGetValue("MS_HttpContext", out object context))
 {
 if (context is HttpContextWrapper wrapper)
  {
  var v = wrapper.Request?.ServerVariables;
  if (v != null)
   {
   var headers = response.Headers;
   const string CRYPT_PROTOCOL = nameof(CRYPT_PROTOCOL);
   try
    {
    headers.Add($"SV_{CRYPT_PROTOCOL}", $"[{v[CRYPT_PROTOCOL].Replace("\r", "0x0D").Replace("\n", "0x0A")}]");
    }
    catch (Exception ex)
    {
       headers.Add($"SV_{CRYPT_PROTOCOL}", ex.Message);
    }
    foreach (string key in v.AllKeys)
      {
      headers.Add($"SV_{key}", v[key].Replace("\r", "0x0D").Replace("\n", "0x0A"));
      }
     headers.Add($"SV_DONE", "All Server Variables Replaced");
     }
  }
No Refunds No Returns
  • 8,092
  • 4
  • 32
  • 43
  • Your answer definitely taught me something I don't know. I have to say that I purposely didn't mention Azure, because I use both Azure and Hosted Servers. And while I TRULY wanted an answer that could give me the information within ASP.NET Web API, having it in an IIS log isn't the worst thing. It just means I wouldn't have Real-Time access to this information during the call; Which may not matter anyway. So I like where this answer is going, and it basically answers the question, but it doesn't help me immediately, which makes me sad. Also, I love your conversational format, lol. – Suamere Nov 26 '18 at 19:08
  • I'm looking into whether or not the ServerVariables mentioned in the article are available in WebAPI. I was able to get to them locally but I doubt IIS Express is doing all the same things IIS is doing. Feel free to try it yourself. I'm just getting a service to log all of the ServerVariable values to see what shows up on a production server for an internet request. – No Refunds No Returns Nov 27 '18 at 00:32
  • Yes, this is interesting. I will need to test it (mostly, because I want to know whether I have those values - where do those numbers come from?). If @Suamere accepts this answer, I will link it when I update mine (TLS1.3 is now up and FW 4.7.2 made some changes to the SSL protocols management, in the HttpClient class for now). – Jimi Nov 28 '18 at 04:43
  • I'm going to test it over the next few days as I update some of my MicroServices. My current client I'm doing this for uses Azure AppServices, so that may affect things. If either @Jimi or myself say it works in either of our environments, I'll mark this as the answer. Either way, definitely an up-vote for an awesome discovery. – Suamere Nov 28 '18 at 15:02
  • @Jimi if you follow this link and look at the specific CRYPT_* server variables descriptions there are links to the IIS Enums and values. https://learn.microsoft.com/en-us/iis/web-dev-reference/server-variables – No Refunds No Returns Nov 28 '18 at 15:48
  • Yes, thanks, I had already found out. It's the same Security Package you can retrieve using the famous [QueryContextAttributes](https://msdn.microsoft.com/en-us/aa379340) of SChannel, through Secure32.dll. I wrote about that function and it's usage in the answer I linked. Very .Net-unfriendly. – Jimi Nov 28 '18 at 16:02
  • Any downvoters care to comment publicly? I'd love to correct this answer after all the time I spent yesterday verifying, testing, arguing with IIS PMs, etc. – No Refunds No Returns Nov 28 '18 at 16:23
  • Yep, subscribed. It would be useful to know what a downvote would imply, here. – Jimi Nov 28 '18 at 16:57
  • Consider Upvoting the github "backlog" for documenting this useful security feature. https://github.com/MicrosoftDocs/iis-docs/issues/212 – No Refunds No Returns Nov 28 '18 at 19:23
  • I executed through the code and did not notice any \n or \r characters. Also, how about just replacing them with empty so that a value to compare becomes available? – Amit Dec 07 '19 at 17:20
  • I'm not sure what scenarios result in the whitespace characters and there hasn't seemed to be any harm in having them present. If you want to use this code I suggest putting in your production service for a couple of weeks and log how many times they occur. That will help inform `your decision` in `your environment`. – No Refunds No Returns Dec 09 '19 at 21:34