Debugging SSL Applications in Python

about | archive


[ 2005-September-29 15:53 ]

When debugging network applications, my favourite tool is Ethereal. It is a network protocol analyzer that gives you plenty of detail about what is happening at all layers of the communications stack. Unfortunately, if you are developing an application that uses SSL, you can't see what data is being exchanged since it is encrypted. This is good since we want to prevent evesdropping, but is bad since we need to see what is happening in order to debug our applications. However, if your application is written in Python, its dynamic typing and dynamic function resolution can help. The following code replaces the standard library's socket.ssl function to return a custom object instead of an SSL object. This custom object intercepts the calls to read and write and prints the unencrypted data. It should be trivial to modify this code to do any sort of logging you might need. You could also do this same interception of the normal socket function if this logging is more convenient than using Ethereal.

This is exactly the kind of trick that makes dynamic languages more productive. I would not recommend replacing standard library functions as part of production code, but as a debugging and development tool, it is quite powerful.

class SSLDebugWrapper( object ):
	'''This object wraps a standard socket.ssl object in order to log the
	data passed in the read and write methods.'''

	def __init__( self, ssl ):
		'''Creates a new debug wrapper object that wraps the specified ssl socket.'''
		self.real = ssl

	def write( self, data ):
		'''Intercepts the data being sent out the socket.'''
		print data,
		
		self.real.write( data )

	def read( self, size=-1 ):
		'''Intercepts the data being received on the socket.'''
		data = self.real.read( size )
		print data,
		
		return data

	def __getattr__( self, name ):
		'''Redirects any other attributes to the underlying socket object.'''
		return getattr( self.real, name )

# Hang on to a reference to the actual socket.ssl function
realssl = socket.ssl

def ssldebug( sock, keyfile=None, certfile=None ):
	'''Creates an SSL socket object that logs all the data passing through it.'''
	return SSLDebugWrapper( realssl( sock, keyfile, certfile ) )

# Replace the socket library's ssl function
socket.ssl = ssldebug