{"id":453,"date":"2015-05-26T21:24:24","date_gmt":"2015-05-26T20:24:24","guid":{"rendered":"http:\/\/www.sevenwatt.com\/main\/?p=453"},"modified":"2016-01-10T14:17:03","modified_gmt":"2016-01-10T13:17:03","slug":"sslhttps-grade-a-python-simplehttpserver","status":"publish","type":"post","link":"https:\/\/www.sevenwatt.com\/main\/sslhttps-grade-a-python-simplehttpserver\/","title":{"rendered":"SSL\/HTTPS &#8211; Grade A+ python SimpleHTTPServer"},"content":{"rendered":"<p>After adding SSL to my <a href=\"\/\/www.sevenwatt.com\/main\/sslhttps-secure-web-and-websocket-server-in-python\/\" title=\"SSL\/HTTPS \u2013 Secure Web and WebSocket server in python\" target=\"_blank\">HTTPWebSocket server<\/a>, I found that originally it was not so secure due to the fact the the linux distribution I used did not get essential security updates. I used <a href=\"https:\/\/www.ssllabs.com\/ssltest\/analyze.html\" target=\"_blank\">Qualsys SSL Labs: https:\/\/www.ssllabs.com\/ssltest\/analyze.html<\/a> to analyze the security level of my server. Well, it started out with &#8216;grade E&#8217;. Finally, I ended up with &#8216;grade A+&#8217;. These are the steps to follow: <!--more--><\/p>\n<h3>OpenSSL<\/h3>\n<p>Use recent versions of SSL and Python. required is:<br \/>\n<code>openSSL &gt;= 1.0.1j<br \/>\nPython &gt;= 2.7.9<\/code><br \/>\nNormally this requirement is met for recent linux distributions. If not, <a href=\"\/\/www.sevenwatt.com\/main\/sslhttp-securing-the-odroid-xu\/\" target=\"_blank\">here<\/a> is how to build them from source.<br \/>\nNow we are at a grade A or B, depending on the certificate setup. Although not exactly with a 100% score.<\/p>\n<h3>Certificates<\/h3>\n<p>When using a certificate from an official CA, then some work must be done compared to other webservers. Python can only handle one certificate file, so the three or four different files (private key, server certificate, CA intermediate certificate and CA root certificate) need to be <a href=\"http:\/\/stackoverflow.com\/a\/18094458\" target=\"_blank\">concatenated<\/a>:<br \/>\n<code>cat my_site.crt intermediate-ca.crt root-ca.crt > server.pem<\/code><\/p>\n<h3>Protocols and Ciphers<\/h3>\n<p>Now we are at &#8216;grade A&#8217;, but not with perfect scores yet. That is for two reasons: By default TLSv1 is still enabled, and some weaker ciphers can still be used. To fix this, the ssl_wrapping of the socket connection in the server needs to be configured. The standard SSL wrapping setup in python like this<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nserver = ThreadedHTTPServer(('', port), SimpleHTTPServer)\r\nserver.socket = ssl.wrap_socket (server.socket, certfile='.\/server.pem', server_side=True)\r\n<\/pre>\n<p>Limiting to TLSv1.2 already improves the protocols to 100%, but leaves the ciphers at 90%:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nserver = ThreadedHTTPServer(('', port), SimpleHTTPServer)\r\nserver.socket = ssl.wrap_socket (server.socket, certfile='.\/server.pem', server_side=True, ssl_version=ssl.PROTOCOL_TLSv1_2)\r\n<\/pre>\n<p>Achieving 100% score requires to limit to both TLSv1.2, and ciphers with >=256 bits encryption. Configuration is a bit more elaborate and requires the definition of a ssl context (<code>ssl.SSLContext<\/code>). Only 3 ciphers are need:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nserver = ThreadedHTTPServer(('', port), SimpleHTTPServer)\r\nctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)\r\nctx.load_cert_chain(certfile=&quot;.\/server.pem&quot;)\r\nctx.options |= ssl.OP_NO_TLSv1\r\nctx.options |= ssl.OP_NO_TLSv1_1\r\nctx.options |= ssl.OP_CIPHER_SERVER_PREFERENCE\r\nctx.set_ciphers('ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA384 ECDHE-RSA-AES256-SHA')\r\nserver.socket = ctx.wrap_socket(server.socket, server_side=True)\r\n<\/pre>\n<p>Now we are at a &#8216;grade A&#8217; server with 100% scores.<\/p>\n<h3>bonus: grade A+<\/h3>\n<p>To achieve &#8216;grade A+&#8217;, all webserver responses to request need to have an additional header line to configure the life time of a renegotiation request for HTTP Strict Transport Security (HSTS). This can be achieved by a bit of a trick. It requires one to specialize <code>SimpleHTTPRequestHandler<\/code> and override the <code>end_headers()<\/code> method:<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nfrom SimpleHTTPServer import SimpleHTTPRequestHandler\r\nimport ssl\r\n\r\nclass GradeAplusSSLHandler(SimpleHTTPRequestHandler):\r\n    def end_headers(self):\r\n        self.send_header(&quot;Strict-Transport-Security&quot;, &quot;max-age=63072000; includeSubDomains&quot;)\r\n        SimpleHTTPRequestHandler.end_headers(self)\r\n<\/pre>\n<p>I real-life working example can be found in the web application <code>Plugwise-2-web.py<\/code> in the Plugwise-2-py repository:<br \/>\n<a href=\"https:\/\/github.com\/SevenW\/Plugwise-2-py\/blob\/master\/Plugwise-2-web.py#L320\" target=\"_blank\">https:\/\/github.com\/SevenW\/Plugwise-2-py\/blob\/master\/Plugwise-2-web.py<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>After adding SSL to my HTTPWebSocket server, I found that originally it was not so secure due to the fact the the linux distribution I used did not get essential security updates. I used Qualsys SSL Labs: https:\/\/www.ssllabs.com\/ssltest\/analyze.html to analyze the security level of my server. Well, it started out with &#8216;grade E&#8217;. Finally, I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":469,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[10],"tags":[42,40,31,39,41,35,34],"class_list":["post-453","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-other","tag-grade-a","tag-https","tag-python","tag-ssl","tag-ssllabs","tag-webserver","tag-websockets"],"_links":{"self":[{"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/posts\/453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/comments?post=453"}],"version-history":[{"count":15,"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/posts\/453\/revisions"}],"predecessor-version":[{"id":587,"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/posts\/453\/revisions\/587"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/media\/469"}],"wp:attachment":[{"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/media?parent=453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/categories?post=453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sevenwatt.com\/main\/wp-json\/wp\/v2\/tags?post=453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}