Two-way ssl authentication for flask Two-way ssl authentication for flask flask flask

Two-way ssl authentication for flask


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_requestwhere

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 truereturn Truewerkzeug.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.