Learning From Mistakes12/13/2000
Shortly after I wrote my news article on the Python wiki program MoinMoin, Jürgen Hermann announced a new version with this notice:
This is a security update, which explains the short release cycle. It replaces some
__import__(), which is much safer (or actually, safe in contrast to totally unsafe). IF YOU HAVE INSTALLED RELEASE 0.5 OR 0.6, UPGRADE NOW!
Because of the short release cycle, the code couldn't be very different between 0.6 and 0.7. That would make changes easy to spot. I love learning opportunities. I got a copy of 0.6 to compare with 0.7. There are several places in MoinMoin where Hermann wanted to use the same code but choose between different libraries of functions to be used by that code. This is called a plug-in. Hermann uses plug-ins to select formatters based on mime-type, or to select a particular parser or extension macro set based on the type of information submitted from a form. The name of the plug-in module to use is taken from information passed by CGI.
For example, here is the code used to import a formatter:
exec("from formatter.%s import Formatter" % (string.replace(mimetype, '/', '_'),))
mimetype is taken from information passed to the program from an untrustworthy outside source. This may not seem like a big security leak, but any leak is a dangerous thing. Someone could monkey with the string passed to mimetype. The
exec function will execute whatever code is passed to it. Using
exec, you might unwittingly execute a dangerous block of code. Here is how Hermann fixed it. He replaced these
exec strings with this utility function
def importName(modulename, name): """ Import a named object from a module in the context of this function, i.e. use fully qualified module paths. Return None on failure. """ try: module = __import__(modulename, globals(), locals(), [name]) except ImportError: return None return vars(module)[name]
__import__ function only imports the specified code. If the mimetype (passed to this function as
modulename) were manipulated now, you would only get an import error, not a surprise intruder.
It's errors like these that the taint mode in Perl was designed to catch. In this paranoid mode, information received from outside the program, like the string assigned to mimetype in this case, is rejected as tainted unless it is first checked by a regular expression. It throws in an extra step designed to make you stop and think before leaving the barn door open.
While Python doesn't have an equivalent to taint mode, it does have a module to help you restrict what might be dangerous code, the restricted execution or RExec module. The RExec module is not needed in this instance because Hermann only needs to import installed code, not evaluate user supplied code. For this purpose, the
__import__ function does nicely. If, however, you work with a lot of CGI, you should study up on RExec.
Stephen Figgins administrates Linux servers for Sunflower Broadband, a cable company.
Read more Python News columns.
Discuss this article in the O'Reilly Network Python Forum.
Return to the Python DevCenter.