[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