Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two-way ssl authentication for flask

Tags:

I have already implemented SSL communication where client application verifies the identity of the SSL server application using flask. Now I want SSL server application to verify the identity of the SSL-client application. Is it possible with flask ? How do I verify client certificate ? During first handshake client is sending CSR and in response I am sending back certificate signed by Self Signed CA certificate.

But I am not yet clear how the client will be verified by server while next communication. Is there any callback for cert verification. Link on Google groups says its not possible to have ssl authentication on Flask. in order to do this one need to use webserver like apache,ngnix. Is this the only way to authenticate client ?

There is one more thing that I want to achieve that I need to identify each client based on their certificate. is that even possible with flask.

my question could be naive as I am not yet much familiar to flask

like image 611
nishi Avatar asked Apr 24 '14 07:04

nishi


1 Answers

Disclaimer

Before I start I would note @Emanuel Ey's comment. That you would want to consider if this was being done on a production or development server first. For example; if you are using Apache WebServer the HTTPS component can be done from Apache. The only thing you would do differently is pass through the certificate details as options and your server app would then verify the serial number within the app itself.

It is Possible

But it the way it is possible is not considered good programming practice. Unfortunately, it's not accessible from flask.request and not possible with the Flask package. However, Flask uses Werkzeug and it is possible by patching the werkzeug.serving package where will be writing your main Flask code. It is not recommended because you may want to update Flask or Werkzeug later and your patch might break and need to be re-factored. i.e. from 0.9 to 1.0.

This provides a solution without using a web server. But I would recommend the web server/environment variable combo. It is cleaner and comparatively good practice.

I have done some testing to see if this is easy to implement. I was able to confirm that this method can work using the latest development codebase 'Werkzeug-0.10_devdev_20141223-py2.7'.

You'll probably want to verify of the serial number (seed number) found in each certificate (and maybe even some other variables). As you may know, the serial is unique to each certificate and is determined during the certificate generation process by you on the server side. It helps to store this along with the clients record and certificate information (where appropriate) in order to verify client certificate serial number later on. Note: It may require alterations between hex and base 10 decimal.

Werkzeug dev_2014122

What I did was to add in the following options to the werkzeug.serving.BaseWSGIServer.__init__ call to wrap_socket().

Use these; server_side=True, ca_certs= '/etc/apache2/ssl/ca.pem', cert_reqs=ssl.CERT_REQUIRED

  • ca_certs: Use this to verify against, this is the CA cert used to generate the client certificates)
  • ssl.CERT_REQUIRED: require client certificate verification against ca_certs

Note: If the client certificate is does not pass initial verification you will not be able to fetch the client certificate. It will be None.

Then in my Flask test class I patched verify_request where

def verify_request(self, request, client_address):  cert = request.getpeercert(True) raw = decoder.decode(cert)[0] print "Serial Number of your certificate is: % " % str(raw[0][1]) # todo: do checks & if serial no is ok then return true return True  werkzeug.serving.BaseWSGIServer.verify_request = verify_request 

This proved it is possible but you'll probably want to investigate the request handlers of the HTTPServer class that the BaseWSGIServer inherits to find a better way to do a call back or override.

Werkzeug 0.9.X

If you are using Werkzeug 0.9.X I'm assuming you are using the import from OpenSSL import SSL. see code snippet here. I have not tested this.

Some of the calls you may be interested in for this version would be; - Context.set_verify(mode, callback) - Connection.get_peer_certificate()

Clarification

What I do not understand is your reference to sending a CSR during the first handshake. If this is your process of client certificate generation you may want to rethink how you do this in the context of your system and environment. If I could have some more information I could comment further..

Also, 'handshake' in an SSL/TLS context generally refers to the action of creating the secure connection in the first place using an existing certificate. Immediately after handshaking, loosely speaking, a connection is established.

like image 154
Ross Avatar answered Oct 18 '22 18:10

Ross