This recipe describes how to set up a simple HTTP server supporting SSL secure communications. It extends the SimpleHTTPServer standard module to support the SSL protocol. With this recipe, only the server is authenticated while the client remains unauthenticated (i.e. the server will not request a client certificate). Thus, the client (typically the browser) will be able to verify the server identity and secure its communications with the server.
This recipe requires you already know the basis of SSL and how to set up OpenSSL. This recipe is mostly derived from the examples provided with the pyOpenSSL package.
In order to apply this recipe, follow these few steps:
- Install the OpenSSL package in order to generate key and certificate. Note: you probably already have this package installed if you are under Linux, or *BSD.
- Install the pyOpenSSL package, it is an OpenSSL library binding. You'll need to import this module for accessing OpenSSL's components.
- Generate a self-signed certificate compounded of a certificate and a private key for your server with the following command (it outputs them both in a single file named server.pem):
openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
- Assuming you saved this recipe in SimpleSecureHTTPServer.py, start the server (with the appropriate rights):
- Finally, browse to https://localhost, or https://localhost:port if your server listens a different port than 443.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
''' SimpleSecureHTTPServer.py - simple HTTP server supporting SSL. - replace fpem with the location of your .pem server file. - the default port is 443. usage: python SimpleSecureHTTPServer.py ''' import socket, os from SocketServer import BaseServer from BaseHTTPServer import HTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler from OpenSSL import SSL class SecureHTTPServer(HTTPServer): def __init__(self, server_address, HandlerClass): BaseServer.__init__(self, server_address, HandlerClass) ctx = SSL.Context(SSL.SSLv23_METHOD) #server.pem's location (containing the server private key and #the server certificate). fpem = '/path/server.pem' ctx.use_privatekey_file (fpem) ctx.use_certificate_file(fpem) self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type)) self.server_bind() self.server_activate() class SecureHTTPRequestHandler(SimpleHTTPRequestHandler): def setup(self): self.connection = self.request self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) def test(HandlerClass = SecureHTTPRequestHandler, ServerClass = SecureHTTPServer): server_address = ('', 443) # (address, port) httpd = ServerClass(server_address, HandlerClass) sa = httpd.socket.getsockname() print "Serving HTTPS on", sa, "port", sa, "..." httpd.serve_forever() if __name__ == '__main__': test()
Excellent ! I've always thought setting up a SSL server was only for the experts in cryptography, so finding this short recipe, very well explained, is a very nice surprise. I've followed your explanations and everything worked, except I had to google around to find a Windows binary for OpenSSL for Python 2.4. I found it here : http://webcleaner.sourceforge.net/pyOpenSSL-0.6.win32-py2.4.exe.
Merci beaucoup !
Great work man, I've been itching for something like this for ages now. I was just wondering if you had seen this error from your application:
File "/home/erik.lat/Downloads/recipe-442473-1.py", line 44, in test httpd.serve_forever() File "/usr/lib64/python2.7/SocketServer.py", line 227, in serve_forever self._handle_request_noblock() File "/usr/lib64/python2.7/SocketServer.py", line 287, in _handle_request_noblock self.shutdown_request(request) File "/usr/lib64/python2.7/SocketServer.py", line 459, in shutdown_request request.shutdown(socket.SHUT_WR) TypeError: shutdown() takes exactly 0 arguments (1 given)
I'm running this on Fedora 14 with Python 2.7-8.1 on x86_64.
Answered my own question: https://bugs.launchpad.net/pyopenssl/+bug/686804
This bug is irrelevant.
To fix the shutdown errors, add the following at line 29:
def shutdown_request(self,request): request.shutdown()
@Erik Lat: If a work around to this bug is absolutely then you can modify %PYTHON_PATH%/lib/socket.py:303 and add the following code.
@Erik Lat: Even better. I wrote a workaround and posted a comment on the bug.
How would you add threading to the Server?
I made a python 3.3 version of this at https://gist.github.com/ubershmekel/6194556
Hello, I'm currently studying information security and I'm looking for away to communicate server-client using public key to send packages. I've seen the server side and how it works but, can I use the same to cypher from the clients to the server?
Thanks for any help provided.
Dear Sebastien, I'm trying to use your code in python3 with no results. This is my SecureHTTPServer class:
When I run it, I get no errors. but when I connect with my browser then my do_GET(self) function inside the SimpleHTTPRequestHandler is never called.
Hey i have added def do_POST() inside class SecureHTTPRequestHandler(SimpleHTTPRequestHandler):, here do_POST() will receive POST request from our application on https and it will respond back with 201. But when POST request is received im seeing 400 bad request, below is the snippet.
Serving HTTPS on 0.0.0.0 port 2091 ... 10.3.2.45 - - [16/May/2016 20:50:16] code 400, message Bad request syntax ('\x16\x03\x01\x00=\x01\x00\x009\x03\x01W:3\x98c\x06\xeb\xd9\xbb\xbd\xbf\x9a\x85-\x17\x9a\xef\xdeD\xb3\xc2k\x0f5\x9b\x84:\xc1\xa4r\xc5\x11\x00\x00\x12\x009\x008\x005\x003\x002\x00/\x00\x16\x00\x13\x00') 10.3.2.45 - - [16/May/2016 20:50:16] "=9W:3▒c▒ٻ▒▒▒▒-▒▒▒D▒▒k5▒▒:▒r▒98532/" 400 -
finished one request
i have freshly generated server.pem using above command, but still getting this above error.
I have decoded the above POST in the wireshark and it looks like as below:
..<head> <title>Error response</title> </head> <body> <h1>Error response</h1> <p>Error code 400. <p>Message: Bad request syntax ('\x16\x03\x01\x00=\x01\x00\x009\x03\x01W6L\xd9.\xe5a\xcdF\x80Vf\xef&Y\xeds\x9f\x17FW\xd4\xff\x1fW\x1a\xa98\xbdO5\x13\x00\x00\x12\x009\x008\x005\x003\x002\x00/\x00\x16\x00\x13\x00'). <p>Error code explanation: 400 = Bad request syntax or unsupported method. </body>
Please let me know how to fix this issue, so that i will see the exact POST request as sent by our application. This works fine for HTTP, but not for HTTPS.