Archive for the ‘Python’ Category.

SSL in Python 2.6

Secure Sockets Layer (SSL) support in Python stdlib took a big leap forward with the introduction of the ssl module in Python 2.6.

Servers

The ssl module provides the means to build safe SSL servers just by using a stock installation of Python 2.6. There are still some pitfalls as usual, but it can be done by limiting compatibility.

The main problem with the ssl module in 2.6 in server use is that it is not possible to disable SSL version 2. This is important since SSL version 2 is insecure. And unfortuantely the most compatible mode selection with OpenSSL (on which Python’s SSL support is based) is to select the SSLv23 mode which on the server is compatible with all SSL and TLS versions supported by OpenSSL. OpenSSL does provide a way to select the SSLv23 mode and selectively turn off any SSL version, but this was not implemented in Python 2.6. It is available in at least M2Crypto, which is a 3rd party Python wrapper for OpenSSL that I maintain.

The 2.6 implementation will also not let you choose ciphers, so you are at the mercy of the protocol version you choose and the options that OpenSSL was compiled with. M2Crypto defaults to strong ciphers, and lets the application developers set any ciphers they wish.

With those important caveats, the server side SSL implementation looks reasonable, assuming you remember to provide the keyfile and certfile arguments to ssl.wrap_socket(). Especially if you can forgo some compatibility and limit your server to just TLSv1. (Transport Layer Security or TLS for short is the newer SSL.)

I wrote earlier how you can use M2Crypto to extract CA certificates from NSS project’s certdata.txt. You can also get free SSL server certificates that work in many (but not all) browsers from StartCom, for example. You still need to pay to cover all the mainstream browsers. In some use cases you may also be able to run your own CA and issue your own certificates but it is not for the faint of heart.

Here are sample servers written using stock Python 2.6 with SSL support compiled in, and M2Crypto 0.19:

Python 2.6 server:

import socket, ssl
bindsocket = socket.socket()
bindsocket.bind(('', 8000))
bindsocket.listen(5)
newsocket, fromaddr = bindsocket.accept()
c = ssl.wrap_socket(newsocket, server_side=True, certfile="servercert.pem",
                    keyfile="serverkey.pem", ssl_version=ssl.PROTOCOL_SSLv3)
print c.read()
c.write('200 OK\r\n\r\n')
c.close()
bindsocket.close()

M2Crypto 0.19.1 server:

from M2Crypto import SSL
ctx = SSL.Context('sslv3')
ctx.load_cert(certfile="servercert.pem", keyfile="serverkey.pem")
bindsocket = SSL.Connection(ctx)
bindsocket.bind(('', 8000))
bindsocket.listen(5)
c, fromaddr = bindsocket.accept()
print c.read()
c.write('200 OK\r\n\r\n')
c.close()
bindsocket.close()

The situation is not so good for the client side, although things have definitely improved.

Clients

The same SSLv2 issue and cipher issue is true for the client side as well. As a client developer, besides needing to specify ca_certs pointing to valid CA certificates for ssl.wrap_socket(), you will also need to remember to pass in cert_reqs=ssl.CERT_REQUIRED.

In addition, the client side needs to check that the certificate hostname (in the subjectAltName or commonName) matches the hostname that the client tried to connect to. If you don’t, you won’t know who you are talking to and your connection is subject to a man-in-the-middle attack. I was not able to convince Bill Janssen that this post connection check callback should be provided by the ssl module. I think there is value in doing a hostname check by default, and letting application developers override it if necessary. As it stands, you must code this check in the application code; you can get the peer certificate as a dictionary with getpeercert() method from the wrapped socket. Again, M2Crypto does this hostname check by default, but it can be overridden by the application if necessary. You might want to take a look at the M2Crypto checker code, although I am not convinced it is 100% correct per specifications (but it should err on the side of safety).

Below is a comparison of Python 2.6 and M2Crypto 0.19.1 clients.

Python 2.6 client:

from socket import socket
import ssl
s = socket()
c = ssl.wrap_socket(s, cert_reqs=ssl.CERT_REQUIRED,
                    ssl_version=ssl.PROTOCOL_SSLv3, ca_certs='ca.pem')
c.connect(('www.google.com', 443))
# naive and incomplete check to see if cert matches host
cert = c.getpeercert()
if not cert or ('commonName', u'www.google.com') not in cert['subject'][4]:
    raise Exception('Danger!')
c.write('GET / \n')
c.close()

M2Crypto 0.19.1 client:

from M2Crypto import SSL
ctx = SSL.Context('sslv3')
# If you comment out the next 2 lines, the connection won't be secure
ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9)
if ctx.load_verify_locations('ca.pem') != 1: raise Exception('No CA certs')
c = SSL.Connection(ctx)
c.connect(('www.google.com', 443)) # automatically checks cert matches host
c.send('GET / \n')
c.close()

Certificate Revocations

Another aspect that affects especially clients, but also servers that have been set up to require peer certificates, is the issue of certificate revocation. The two standard ways of doing this are the Certificate Revocation Lists (CRL) and Online Certificate Status Protocol (OCSP), but the ssl module in Python 2.6 provides no support for these. Granted, they are not easy to deal with in any of the 3rd party libraries either, but at least they provide some APIs to deal with them. Python is not unique here, though, because many (most?) modern applications still do not do any revocation checks by default (if at all).

Other Libraries

Besides the stdlib’s ssl module and M2Crypto, there are other SSL options for the Python developers out there. You might want to take a look at pyOpenSSL, which is again being maintained. Or if you want a pure Python version which can use various crypto libraries for performance improvements, you might want to check out TLS Lite (although it hasn’t been updated in a while). An exiting new package you may want to pay attention to is python-nss, which is a Python wrapper for the Network Security Services (NSS) libraries from the Mozilla project. If there are other maintained Python SSL packages out there I would be interested in hearing about them.

M2Crypto 0.19.1 - OpenSSL without EC

M2Crypto 0.19 shipped with some code that expected OpenSSL with EC support. But since EC can be disabled, and at least Fedora Core ships with that disabled, things didn’t work so well in that case. Thanks to Miloslav Trmac who turned in a quick fix, we now have M2Crypto 0.19.1 that fixes this issue. No other major issues have surfaced in the week since 0.19 release, and since there have been at least 153 downloads according to PyPI, things look pretty stable.

Incidentally, if you were able to build 0.19, you should not need to get 0.19.1.

Office Resource Finder

Have you ever been looking for a colleague, printer, or just about any resource in an office and being frustrated there was no map showing you where to go? I have good news for you. I just released Solu, which is a “Self-service Office resource Locator and Updater”. Or “cubicle finder”. Or whatever you want to call it.

This project started as a perfect example of “scratch your own itch”. I was doing some research on how to do REST with Python, and remembered jj recommended Werkzeug at some point, so I took a look. Around the same time I was chatting with our new admin, and noticed the office blueprints on the IT cubicle wall. This lead the two us discussing how nice it would be if there was a map to help us new people around. I ended up saying I could program such a system in 20 hours.

So I ended doing a bit more than research, and studied Werkzeug and jQuery. And although it wasn’t strictly necessary I also spent quite a bit of time researching how to make an easy to deploy package of the application, and how to make it easy to try it out. In the end I spent almost exactly 20 hours on the first version, but I realized I could have done it in about half the time had I used Pylons, mostly because I already knew Pylons and also because the things were I struggled with Werkzeug were already provided by Pylons, or easily integrated into Pylons. After the initial version I’ve spent another 20 hours to fix bugs, write tests, and in general getting it to a stage I felt good to release. Even still it is missing some pieces I know how to do in Pylons, but still don’t know for sure how to deal with them in Werkzeug.

Since I developed this application partially on SpikeSource time, it was nice of them to let me Open Source it to everyone’s benefit.

Some of the things I really enjoyed about Werkzeug include:

  • Interactive Python debugger in the browser
  • Small enough to make the ramp up period quick
  • Good documentation
  • Enough features to provide almost everything I needed
  • Easy to swap template system from Jinja to Mako (since I didn’t want to spend the time to learn Jinja too)
  • Writing tests was easy
  • Easy to work with Unicode
  • Great tutorial which fit my needs almost perfectly

Some of the things I found frustrating about Werkzeug:

  • No builtin mechanism to load application options from a file
  • No help to package the application
  • No cross-site request forgery (CSRF) protection out of the box
  • No internationalization/localization (i18n/l10n) support out of the box
  • No samples or recommendations on how to pass application and options to views and templates
  • No samples or recommendations on how to implement sessions

In the end I used ConfigObj to load the options from an ini file. This was a little trickier than I thought at first because I wanted the manage script to work so that I could run with Werkzeug’s server while testing, but I also wanted to retain WSGI deployment possibility. Packaging took some extra reading about setuptools. CSRF is still a little open, although I got pointers to zine.utils.forms and also found werkzeug.contrib.sessions which should make it possible in the way I was thinking about it. I don’t see CSRF as a huge issue at this point, though, given the data this application handles. I also got some pointers on how to implement get_app() function to get the application object from anywhere, so getting the options stored in the app became easy. I still have some open questions about localization, but those might go away once I actually try to do that.

There are some obvious improvements to Solu, like fixing the CSRF issues, i18n/l10n, multiple and resizeable maps, dealing with errors in a more user-friendly way, and overall making it pretty. I could see someone wanting to pull the information from company LDAP database or some such, and hooking this up with general employee database.

Since I spent so much time making it easy to see what the application is about by providing a website with screenshots, a demo site, easy installation and instructions on how to run your own application, I am interested in hearing how I did.

Released M2Crypto 0.19

I just pushed out M2Crypto 0.19. This ends the longest hiatus in releases (almost a year since 0.18.2) since I took over the project; apologies for the delays. I highlighted the best parts about 0.19 in an earlier post, so I won’t repeat them here. I need to make one clarification regarding Python 2.6 support: the optional timeout parameter added to many network modules is not yet supported in M2Crypto 0.19. I just noticed this too late for this release.

In preparation for 0.19 I did the first ever code coverage analysis of M2Crypto. I installed the latest coverage and nose, and run the M2Crypto unit tests. At first I got 72%. I then added some tests on trunk, and got the number to 75%. Then I added some docstrings and was surprised to note the figure jumped to 78%. Now I just need to write some more docstrings to break the magical 80% code coverage limit ;)

While nose and coverage were surprisingly easy to set up and run, finding out the specific lines of code that were not covered was not very user friendly. For that I installed figleaf. The workflow then became:

nosetests --with-coverage --cover-package=M2Crypto

which wrote the file .figleaf in the current directory. Then I run:

figleaf2html -d build/fig .figleaf

which produced HTML files in the build/fig directory. The HTML files showed the source code, formatted such that it was easy to see what was covered and what not. Basically non-covered lines were colored red.

Update: It seems I messed up the figleaf instructions. The above nosetests line will not produce .figleaf. I know of two ways to produce that. The first one is to add two more options to the nosetests command, which then becomes:

nosetests --with-coverage --cover-package=M2Crypto --with-figleafsections --figleaf-file=.figleaf

Unfortunately trying to process this with figleaf2html leads into:

Traceback (most recent call last):
  File "/usr/bin/figleaf2html", line 8, in <module>
    load_entry_point('figleaf==0.6.1', 'console_scripts', 'figleaf2html')()
  File "/usr/lib/python2.5/site-packages/figleaf-0.6.1-py2.5.egg/figleaf/annotate_html.py", line 256, in main
    coverage = figleaf.combine_coverage(coverage, d)
  File "/usr/lib/python2.5/site-packages/figleaf-0.6.1-py2.5.egg/figleaf/__init__.py", line 89, in combine_coverage
    keys.update(set(d2.keys()))
AttributeError: CodeTracer instance has no attribute 'keys'

The second way which actually works is to use figleaf directly:

figleaf --ignore-pylibs setup.py test -q

in the M2Crypto source tree. Then figleaf2html will work. The downside is that setup.py and test files are included in coverage.

Protocol Testing with Doctests

A couple of years ago I was asked to write some tests (and maybe test framework, I can’t remember) for Cosmo, the Chandler Server. I think the only tools we had used for testing until then were litmus and manual testing, maybe with some unit tests. The protocols to be tested include ticket, CMP etc. protocols.

The thought of expanding litmus, which was a tool written in C, did not sound too promising. Command line tool to test the protocol was also what we were mainly after at that point, so GUI tools were not needed. And it was going to be easier to automate command line tools with Tinderbox.

Around that time I had been really sold on doctests, so I figured by writing some helper functions the protocol tests for Cosmo could be done easily as Python doctests. Developing the “framework” was fast, I was testing within minutes, and probably the time spent on the helper functions still amounts to less than a day, total. I based the helper functions on httplib, but urllib2 or httplib2 would probably make the experience even easier. I named the tool “silmut” , which is an anagram of litmus, and also a word in the Finnish language, meaning “buds”.

In the end we collected each protocol’s tests into its own file. At the top we had some code to do common initialization, and after that followed the actual tests. Here’s an example of a test:

View account
 
    >>> r = request('GET', '%s/cmp/user/%s' % (path, user1), headers=authHeaders)
    >>> r.status # GET account ok
    200

I was actually surprised to note that these tests are still in the Cosmo sources, and apparently being maintained. Just goes to show you don’t always need to be too fancy to get the job done.

How to Replace Python’s socket.ssl with M2Crypto’s SSL Implementation

It seems like I started a mini-series about “hidden” M2Crypto tools and modules…

Python’s socket.ssl is not secure. If you need any real security you need to look for 3rd party packages (things will improve a little with Python 2.6).

Sometimes you are faced with a library that does SSL, but uses Python’s socket.ssl that you can’t easily replace. For this purpose I wrote a little helper module using M2Crypto. Basically you just need to import this socklib.py before you import the module that is using Python’s socket.ssl, and call socklib.setSSLContextFactory() with context factory that creates secure SSL contexts and your SSL usage just became secure.

The socklib.py implementation is for client side only. It would be easy to expand it for servers, though. It may also lack some features, but it filled the need I had so that is where I stopped. I wrote it for Python 2.5 and haven’t thought what would need to be changed for 2.6.

Root Certificates for Python Programs using Python

OpenSSL itself does not come with root certificates, which means that if you use OpenSSL for anything that requires those certificates (like SSL for example) you will need to get those certificates from somewhere else. This concerns most Pythonistas needing SSL since most Python programs use OpenSSL for SSL.

Most if not all Linux distributions include various sets of root certificates in OpenSSL-friendly formats. Windows also comes with root certificates, but to get access to them you would need to use the Windows-specific APIs.

The Curl project produced a crazy little script that can convert the certdata.txt from the NSS project (from Mozilla) into PEM format, suitable for OpenSSL. The Curl project also provided a converted certdata.txt file for download. Unfortunately the converted file was from a very old version of the certdata.txt file (when I first looked at it). I figured M2Crypto should have it’s own utility to do this conversions, so I ended up porting the script into Python. The dirty little certdata2pem.py script uses M2Crypto for certificate handling.

I used my script to get root certificates for Chandler.

Python Syntax Influencing New Languages

To this day C++ is the language I have programmed the longest in (although my Python experience is catching up fast), and at some point I even thought it would be the only programming language I would ever need and use. I actively stayed away from Python, mainly because I had heard about the forced indentation and having had bad experiences with Fortran before. But within two weeks after being forced to use Python I was sold. The Python syntax is definitely one of the attractions. So even though Python itself hasn’t (yet) taken over the world of programming languages, I am happy to see Python influencing new languages.

A while back jj pointed out Reia, which is a language for the Erlang virtual machine. The syntax looks a lot like Python’s, which almost makes me want to play with it. (The concepts of Erlang make it really attractive with the multicore architectures and all, but just reading the Wikipedia article on Erlang made my head hurt because of the syntax.)

Today I was reading about Delight, which is a Python-like syntax for the D programming language. (This is kind of ironic, because D is the nicer C++.) I can’t say I am sold on all of the ideas of Delight, but I do welcome any attempts to make other programming languages more Pythonic in syntax if nothing else.

Not that long time ago languages marketed themselves by having C-like syntax to make it easier to switch. I am wondering if Python is becoming the new C in that respect.