[Tutor] Is it possible for a Python Program to send commands to the Python Interpreter?

Steven D'Aprano steve at pearwood.info
Sat Feb 20 01:07:26 CET 2010


On Sat, 20 Feb 2010 06:33:04 am Kent Johnson wrote:

> It sounds like you are looking for eval()
>
> (Standard warning - use eval() only on trusted data)


This is the tutor list, aimed at beginners to Python, many of whom are 
also beginners to programming as well. Even experienced programmers 
often get security very, very, very badly wrong. Do you think that a 
glib eight-word one-line "standard warning" really is sufficient?

James, if you are still reading, I should expand on Kent's warning.

The eval function, like exec, can be very dangerous in the wrong hands. 
There is a whole class of very, very common security bugs caused by 
functions like eval:

http://en.wikipedia.org/wiki/Code_injection

The way the bug works is that the programmer writes a function that 
takes some data, and directly or indirectly applies eval to it:

>>> def mylist(string):
...     # Convert a string to a list.
...     string = string.strip()
...     if string.startswith('[') and string.endswith(']'):
...         return eval(string)
...     else:
...         raise ValueError('not a list')
...
>>> mylist(" [1, 2, 3] ")
[1, 2, 3]


This seems pretty innocuous, but it contains a deadly land-mine.

This function then gets used in an application that uses strings 
produced by untrusted users. Say, it ends up in a web application, and 
the user types text into a field and the application ends up calling 
mylist on the contents of that field.

Then, some malicious user types this into the input form:

"[] or __import__('os').system('echo YOUR SYSTEM IS MINE') or [1,2,3]"

(only imagine something much worse than an echo command) and your web 
application does this:

>>> s = "[] or __import__('os').system('echo YOUR SYSTEM IS MINE') or 
[1,2,3]"
>>> mylist(s)
YOUR SYSTEM IS MINE
[1, 2, 3]

You have just had your web server compromised by a code injection bug.

(A web application is only one example of how you can get untrusted data 
into your app. It is the biggest risk though.)

Now, you might think that you can work around this by clever 
programming. Well, maybe, but sanitising strings so they are safe is a 
VERY difficult job. And naturally if you aren't aware they need to be 
sanitised, you won't do it.

My advice to anyone thinking they need to use eval or exec is:

(1) Don't do it.

(2) If you think you need to use them, you probably don't.

(3) If you really, really need to use them, then use the most 
restrictive environment possible. Instead of eval(s), use:

eval(s, {'__builtins__': None}, {})

which gives you some protection against naive attackers. The really 
clever ones will still break it.

(4) Sanitise your data. Don't use Javascript to sanitise it at the 
browser, because the Bad Guys know how to bypass your Javascript 
checks. If you're expecting (say) a list of integers, then there is no 
reason for the list to contain *any* alphabetic characters or 
underscores, and if there are any, reject the string and report an 
error:

def sanitise(string):
    safe = "1234567890[], \n\t"
    for c in string:
        if c not in safe:
            raise ValueError('unsafe string')

If your needs are more complicated, then sanitising the string becomes 
exponentially more difficult. It will probably be less work to write 
your own safe parser than to sanitise the input.

Have I scared you about using eval? If so, good. Don't let eval or exec 
anywhere near data provided by untrusted users, and don't confuse 
authentication with trust.


-- 
Steven D'Aprano


More information about the Tutor mailing list