Skip to content

Instantly share code, notes, and snippets.

@SamuelChristie
Created November 2, 2015 16:08
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save SamuelChristie/13a2a29e74c189bcfd9b to your computer and use it in GitHub Desktop.
Save SamuelChristie/13a2a29e74c189bcfd9b to your computer and use it in GitHub Desktop.
Explanation of how to detect TLS 1.0 connections and, by way of custom headers, warn the user about the coming change to more modern TLS versions.

Gracefully Deprecating TLS 1.0

TL;DR: I explain how to detect TLS 1.0 connections and, by way of custom headers, warn the user about the coming change to more modern TLS versions.

  1. Background
  2. How you can prepare
  1. Final thoughts
  2. Contact
  3. References

Background

We need to prepare our users for the day when the secure servers we manage will stop supporting TLS 1.0. For those who operate an e-commerce site, PCI compliance requires that we disable TLS 1.0 support no later than June 30, 2016. Even if you do not currently operate an e-commerce server, you should still consider making this change to improve the overall security of your web application.

As a courtesy to our websites' visitors, we should display a warning that they will soon lose the ability to view the site if they are connecting with TLS 1.0. This will give sufficient time to upgrade browsers before they are met with a message informing them that the site cannot be loaded. Here is a sample warning:

As of January 12, 2016 your browser will no longer be able to display this website due to outdated encryption. Please upgrade your browser as soon as possible.

While only those using an affected browser should see the warning, it is not as simple as checking which browser and version they are using. For example, IE and Firefox both offered support for TLS 1.1 and 1.2 via a browser setting for several versions before those protocols were enabled by default. Additionally, the TLS-support matrix gets more complicated when mobile browsers and clients connecting via APIs are included in the mix.

How you can prepare

The best approach is to check which TLS version your client is currently using and pass that information to your application server via a custom header. Doing this correctly can be challenging, and it requires an understanding of where your users' encrypted connections are being terminated. It is most common for front-end servers like Nginx, Apache, HAProxy, f5, etc. to terminate the encryption instead of the application server itself. Since your front-end server is the last stop for the encrypted client-server exchange, you will to configure it to obtain the TLS protocol version and ensure that it is passed along to your application. This can be readily accomplished in most hosting configurations. The guidelines below address the most common front-end servers.

Nginx

Adding the following lines to the http section of your configuration file will add two new header entries to the incoming request. Your application server can then query for X-SSL-Protocol to obtain a string like "TLSv1" or "TLSv1.2" which you could then use to assess whether or not a warning should be displayed.

proxy_set_header      X-SSL-Protocol $ssl_protocol;
proxy_set_header      X-SSL-Cipher $ssl_cipher;

Sample JSP to read the new TLS version header:

String tlsHeader = request.getHeader("X-SSL-Protocol");

Sample ruby to read the new TLS version header (note that rails renames headers):

tls_version = request.headers['HTTP_X_SSL_PROTOCOL']

Apache

Apache's mod_ssl offers environmental variables which can provide details related to the current SSL/TLS connection. Adding the following lines to your conf file (be sure you are using mod_headers) will inject two new headers into the incoming request. You can then use X-SSL-Protocol in your Perl, PHP, Python, etc. to assess whether or not a warning should be displayed.

SSLOptions +StdEnvVars
RequestHeader set X-SSL-Protocol %{SSL_PROTOCOL}s
RequestHeader set X-SSL-Cipher %{SSL_CIPHER}s

Sample PHP to read the TLS version (as with rails, PHP/CGI also changes the header name):

$tlsversion = $_SERVER["HTTP_X_SSL_PROTOCOL"];

f5

You can create or update an f5 iRule with the SSL/TLS version and cipher details. Adding the following lines will inject two new request headers that will be passed to the backend. As with the Nginx and Apache examples, we can use the new X-SSL-Protocol header to determine if a warning message should be displayed to your user.

when HTTP_REQUEST {
  HTTP::header insert X-SSL-Protocol [SSL::cipher version]
  HTTP::header insert X-SSL-Cipher [SSL::cipher name]
}

HAProxy

If you are using HAProxy 1.5+ to terminate your TLS connections, then you can add the following lines to your configuration to set the custom headers:

http-request set-header X-SSL-Protocol %[ssl_fc_protocol]
http-request set-header X-SSL-Cipher %[ssl_fc_cipher]

IIS & .NET

Administrators of IIS understand that it handles SSL/TLS a little differently as compared to other servers. Microsoft uses their own library called Schannel to manage secure HTTP communications. There does not appear to be any default mechanism in IIS 8.5 and .NET 4.6 to indicate the TLS protocol version for a user's session.
Some options for IIS administrators wishing to transition away from TLS 1.0 include:

  • Installation of a load balancer / reverse proxy in front of IIS
  • Inclusion of JavaScript to contact a separate server for the purpose of verifying the maximum supported TLS version of the client
  • Enabling detailed Schannel logging and poll the log file to check for each user's connection

**Warning:** Some versions of SQL Server may experience problems after disabling TLS 1.0. Please be careful if you are running IIS side-by-side with SQL Server on the same system:
https://dba.stackexchange.com/questions/93127/sql-server-service-won-t-start-after-disabling-tls-1-0-and-ssl-3-0

Amazon ELB

Amazon does not appear to support setting custom headers for this purpose beyond X-Forwarded-Proto which simply indicates whether the connection is using HTTP or HTTPS. That, unfortunately, will not provide the detail needed to warn users of older protocol versions.

Final thoughts

If you plan to update your secure server's configuration soon, it is worth double-checking all of your TLS settings (a good thing to do periodically). There are two resources I recommend to help with this process:

  • The Mozilla SSL Configuration Generator
    https://mozilla.github.io/server-side-tls/ssl-config-generator/
    A free tool created by the Mozilla Foundation to assist server administrators with their SSL/TLS configuration settings. It supports Apache, Nginx, HAProxy, and Amazon ELB. To disable support for TLS 1.0, you simply have to select the "Modern" profile. The "Intermediate" profile is largely the same, but it includes support for TLS 1.0.
  • Qualys SSL Labs Server Test
    https://www.ssllabs.com/ssltest/
    This is a free tool that will form encrypted connections to your website and check for any known SSL/TLS vulnerabilities. It also presents an overall grade for the strength of encryption on your website. This is an excellent utility that you can use periodically to ensure that you are current with the latest best practices.

References

PCI Compliance

All US e-commerce websites must be PCI DSS compliant. Starting with PCI DSS v3.1, TLS version 1.0 is no longer considered acceptable and must be phased out no later than June 30, 2016. All e-commerce sites should be routinely audited as part of the compliance process to maintain an active merchant account. Some scanning tools (like TrustWave) will flag sites which still make use of TLS 1.0.
https://www.pcisecuritystandards.org/documents/PCI_DSS_v3-1.pdf

Federal Guidelines

According to NIST Special Publication 800-52 Revision 1, federal agencies had until January 1, 2015 to develop migration plans to shift to TLS 1.2 and stop support for TLS 1.0 (which they no longer consider secure).
http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r1.pdf

Browsers

TLS 1.1 and 1.2 support has been available in all major browsers for some time now. For reference, the earliest versions of each browser that, by default, offered support for TLS 1.1 and 1.2 are:

  • Chrome 22 / 30 (TLS 1.1 / 1.2) - released 2012-09-25 and 2013-10-01, respectively
  • Firefox 27 - released 2014-02-04
  • IE 11 - released 2013-11-07 (Windows 7)
  • Safari 7 - released 2013-10-22

https://en.wikipedia.org/wiki/Template:TLS/SSL_support_history_of_web_browsers

Internet Explorer and Microsoft Security Updates

As of January 12, 2016, Microsoft will no longer provide security updates for any version of IE other than the most recent one available for their currently-supported operating systems. If, for example, a user is on Windows 7 and is using IE 10, they will no longer receive security updates for their browser. The only option will be for them to upgrade to IE 11.
https://support.microsoft.com/en-us/lifecycle/search?sort=PN&alpha=internet%20explorer
https://support.microsoft.com/gp/Microsoft-Internet-Explorer

IETF Summary

The Internet Engineering Task Force provides a good overview of known attacks on TLS. Feel free to review this for specifics on the weaknesses of TLS 1.0.
https://tools.ietf.org/html/rfc7457

OWASP TLS Cheat Sheet

The Open Web Application Security Project has many great resources to help website administrators increase the security of their platform. If you have never visited their site before and you operate a secure web application, it is definitely worth reading:
https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet

@jonwilkinson
Copy link

Excellent writeup, wish I had found it sooner :)
Another solution for Amazon ELB (until they provide a better one) is to create another nginx(or apache, etc) server behind a new ELB. The new ELB should use the more secure protocols and ciphers just like it will in June 2016. Drop an html page or a json file on that nginx server and add CORS headers. Now from your main site (where you want to warn users of the upcoming change) you can attempt an ajax request to the json file behind the new secure ELB. If the ajax call fails, warn the client about insufficient security.
There are probably other workarounds too...

On a related note, this thread
https://forums.aws.amazon.com/thread.jspa?messageID=667421
discusses a potential issue with SSL/TLS1.0 still being used between CloudFront and its origin (i.e., S3) and not configurable at this point.

@jasondbecker
Copy link

jasondbecker commented Apr 22, 2016

The PCI Security Standards Council has extended the deadline to migrate away from TLS 1.0 to June 30, 2018 although they state that "waiting is not recommended" and "that anyone using SSL and early TLS risks being breached".

@motolola
Copy link

Experimenting (before proposing a solution to my company) this on my local machine, an Apache installation through MAMP, the two variables return a null value, is that my local connection doesn't activate any SSL/TLS connection. I am a bit amateur here, but any help will be great

@mihirkagrana
Copy link

Thank you for this nice article.

Those who are using Incapsula firewall, may find an easier solution. Incapsula provides a configuration called "INCAP-TLS-VERSION". If you turn it on, it will send additional header with client browser's TLS version. It can be used to alert users.

@farolfo
Copy link

farolfo commented Feb 5, 2018

FYI Amazon ELBs now do support TLS versions configuration :D

@dgpro
Copy link

dgpro commented Jun 5, 2018

Just a note that proxy_set_header for nginx works only if you use proxy_pass, but for fastcgi_pass to php you should use

fastcgi_param HTTP_X_SSL_PROTOCOL $ssl_protocol;
fastcgi_param HTTP_X_SSL_CIPHER $ssl_cipher;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment