Azure App Service: Understanding TLS Mutual Authentication with Web App
I have published 2 posts in the past on Client Certificate authentication.
Client Certificate Authentication (Part 1)
Client Certificate Authentication (Part 2)
In order to enable Client Certificate authentication on azure web app, we need to flip the clientCertEnabled
property to true
. Please refer the following article on how to enable TLS mutual auth for an Azure Web App:
How To Configure TLS Mutual Authentication for Web App
HOW IT WORKS
The best way to understand this is to capture a network trace by issuing a HTTPS request to the Azure Web app. You will notice that the certificate request is not part of the Server Hello. This is because the server supports Secure Renegotiation and it tends to send this as a part of the Encrypted Handshake Message to the client. Refer my earlier post here.
To demonstrate this, I have enabled client certificate Authentication on one of the staging slots of my web app. Here is the link: https://clientcertauth-demo.azurewebsites.net
I have written a sample code, which accepts any certificate that is provided to it. It reads the client certificate and displays the results in a tabular format.
I browsed this web app and captured a network trace using WireShark. Here is a screenshot of this trace:
So, how do we confirm if the server is requesting for client certificate. You can use one of the following methods:
- Enable SSL traffic decryption in WireShark.
I found a blog post on how to do this and tested it myself. Here is the link: https://jimshaver.net/2015/02/11/decrypting-tls-browser-traffic-with-wireshark-the-easy-way/
Here is a screenshot of the Decrypted message: The server is sending another Server Hello which contains the Certificate Request message.
- Using cURL.exe
You can refer to my previous post on client cert here to use curl. FYI, cURL is included by default on all the Azure App Service instances. Path is D:\Program Files (x86)\Git\usr\bin. This means you can do run cURL inside the command prompt window in the Kudu console.Open the following URL in a browser: https://<sitename>.scm.azurewebsites.net/DebugConsole (replace <sitename> with the name of the web app)
Execute the following command:
curl -v https://<hostname>Review the verbose output and search for “Request CERT (13) ”.
Following is a brief output of the command from the Kudu console of the web app. The Server is requesting for the client certificate in the second Server Hello message. See the text highlighted in yellow.
D:\home>curl -v https://clientcertauth-demo.azurewebsites.net* STATE: INIT => CONNECT handle 0x80073200; line 1103 (connection #-5000) * Rebuilt URL to: https://clientcertauth-demo.azurewebsites.net/* Added connection 0. The cache now contains 1 members* Trying 111.221.95.27...* STATE: CONNECT => WAITCONNECT handle 0x80073200; line 1156 (connection #0) % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to clientcertauth-demo.azurewebsites.net (111.221.95.27) port 443 (#0)* STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x80073200; line 1253 (connection #0) * ALPN, offering http/1.1* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH* successfully set certificate verify locations:* CAfile: /usr/ssl/certs/ca-bundle.crt CApath: none* TLSv1.2 (OUT), TLS header, Certificate Status (22):} [5 bytes data]* TLSv1.2 (OUT), TLS handshake, Client hello (1):} [512 bytes data]* STATE: SENDPROTOCONNECT => PROTOCONNECT handle 0x80073200; line 1267 (connection #0) <!DOCTYPE html><html>
<--------------------------Trimmed HTML Response -------------------------->
</html>{ [5 bytes data]* TLSv1.2 (IN), TLS handshake, Server hello (2):{ [81 bytes data]* TLSv1.2 (IN), TLS handshake, Certificate (11):{ [3245 bytes data]* TLSv1.2 (IN), TLS handshake, Server key exchange (12):{ [333 bytes data]* TLSv1.2 (IN), TLS handshake, Server finished (14):{ [4 bytes data]* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):} [70 bytes data]* TLSv1.2 (OUT), TLS change cipher, Client hello (1):} [1 bytes data]* TLSv1.2 (OUT), TLS handshake, Finished (20):} [16 bytes data]* TLSv1.2 (IN), TLS change cipher, Client hello (1):{ [1 bytes data]* TLSv1.2 (IN), TLS handshake, Finished (20):{ [16 bytes data]* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-SHA384* ALPN, server did not agree to a protocol* Server certificate:* subject: CN=*.azurewebsites.net* start date: Sep 28 21:45:23 2016 GMT* expire date: May 7 17:03:30 2018 GMT* subjectAltName: clientcertauth-demo.azurewebsites.net matched* issuer: C=US; ST=Washington; L=Redmond; O=Microsoft Corporation; OU=Microsoft IT; CN=Microsoft IT SSL SHA2* SSL certificate verify ok.* STATE: PROTOCONNECT => DO handle 0x80073200; line 1288 (connection #0) } [5 bytes data]> GET / HTTP/1.1> Host: clientcertauth-demo.azurewebsites.net> User-Agent: curl/7.47.1> Accept: */*> * STATE: DO => DO_DONE handle 0x80073200; line 1350 (connection #0) * STATE: DO_DONE => WAITPERFORM handle 0x80073200; line 1477 (connection #0) * STATE: WAITPERFORM => PERFORM handle 0x80073200; line 1487 (connection #0) { [5 bytes data]* TLSv1.2 (IN), TLS handshake, Hello request (0):{ [4 bytes data]* TLSv1.2 (OUT), TLS handshake, Client hello (1):} [512 bytes data]* TLSv1.2 (IN), TLS handshake, Server hello (2):{ [105 bytes data]* TLSv1.2 (IN), TLS handshake, Certificate (11):{ [3245 bytes data]* TLSv1.2 (IN), TLS handshake, Server key exchange (12):{ [333 bytes data] * TLSv1.2 (IN), TLS handshake, Request CERT (13): { [30 bytes data]* TLSv1.2 (IN), TLS handshake, Server finished (14):{ [4 bytes data]* TLSv1.2 (OUT), TLS handshake, Certificate (11):} [7 bytes data]* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):} [70 bytes data]* TLSv1.2 (OUT), TLS change cipher, Client hello (1):} [1 bytes data]* TLSv1.2 (OUT), TLS handshake, Finished (20):} [16 bytes data]* TLSv1.2 (IN), TLS change cipher, Client hello (1):{ [1 bytes data]* TLSv1.2 (IN), TLS handshake, Finished (20):{ [16 bytes data]* HTTP 1.1 or later with persistent connection, pipelining supported< HTTP/1.1 403 Forbidden< Content-Type: text/html* Server Microsoft-IIS/8.0 is not blacklisted< Server: Microsoft-IIS/8.0< Date: Fri, 09 Jun 2017 19:19:37 GMT< Connection: close< Content-Length: 2399
<--------------------------Trimmed -------------------------->
Hope this clarifies few doubts on how TLS Mutual Auth works on Azure App Service.
RESTRICTIONS
Currently, TLS Mutual Authentication is supported for web apps running in Basic or higher pricing tiers only. You will receive the following error if you try to enable it for a web app running in FREE/SHARED tier.
"Cannot enable client certificate for a site '{0}' because current site mode does not allow it." When the
clientCertEnabled
property is set totrue
the web app mandates the client to provide a client certificate. If the client fails to provide a client certificate they will receive a 403 error. Those familiar with IIS, this is equivalent to setting the sslFlags property (inside <access> tags) in IIS to include “SslRequireCert
”.<location path="Kaushal"> <system.webServer> <security> <access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" /> </security> </system.webServer></location>
As of now, Azure App Service supports Secure Renegotiation and doesn’t entertain ReNego. If there are TLS clients, that are not compliant as per the RFC 5746 and depend on Re-Negotiation, then they cannot connect to Azure App Service.
Comments
- Anonymous
June 12, 2017
Thanks for this post , I solved my problems ! - Anonymous
June 20, 2017
were I've been stumped is I have an azure app service that I want to talk to an azure sql using mutual authentication, is that possible? In know this is not the place to ask but ...- Anonymous
June 26, 2017
@Tony, I don't think SQL Azure supports TLS Mutual authentication. See this: Securing SQL Azure You may want to raise an ask for this feature here: https://feedback.azure.com/forums/217321-sql-database
- Anonymous
- Anonymous
November 15, 2017
Hi Thanks for this post.Though I have a question that I really need answer to. Why is it that during the server hello, the list of CA is empty? I need to specify the CAs so that the client my server is talking to would be able to send the right certificate back.Hopefully you will be able to see this soon. Thanks again.- Anonymous
November 29, 2017
@John, There are few reasons for this. 1. If there are too many certificates in the Root CA store, then the CA list becomes large and this causes the SSL handshake to fail. 2. Also some platforms expect Intermediate CA certs in the list and not Root CA's. In this case again the SSL handshake will fail.To have compatibility with systems of all types it is better to not send the list. However, the down side is that the filtering now happens on the client side, who is free to send any certificate that it deems appropriate. HTH.
- Anonymous