views:

1238

answers:

6

If I am evaluating a Python string using eval(), and have a class like:

class Foo(object):
    a = 3
    def bar(self, x): return x + a

What are the security risks if I do not trust the string? In particular:

  1. Is eval(string, {"f": Foo()}, {}) unsafe? That is, can you reach os or sys or something unsafe from a Foo instance?
  2. Is eval(string, {}, {}) unsafe? That is, can I reach os or sys entirely from builtins like len and list?
  3. Is there a way to make builtins not present at all in the eval context?

There are some unsafe strings like "[0] * 100000000" I don't care about, because at worst they slow/stop the program. I am primarily concerned about protecting user data external to the program.

Obviously, eval(string) without custom dictionaries is unsafe in most cases.

+4  A: 

A normal call to eval is unsafe:

>>> eval("open('/tmp/tst', 'w')", {}, {})
<open file '/tmp/tst', mode 'w' at 0x7fe8703ce198>

But you can hide the builtin objects by passing a dict of globals that has __builtins__ explicitly set:

>>> eval("open('/tmp/tst', 'w')", {'__builtins__':[]}, {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'open' is not defined

I'm not sure if this prohibits all evil things one could do, but it surely is a step in the right direction...

sth
Oh geez, right. And now that I think about it, __import__ is there as well. Now to figure out if there's a way to find that stuff from any object...
Joe
+17  A: 

eval() will allow malicious data to compromise your entire system, kill your cat, eat your dog and make love to your wife.

There was recently a thread about how to do this kind of thing safely on the python-dev list, and the conclusions were:

  • It's really hard to do this properly.
  • It requires patches to the python interpreter to block many classes of attacks.
  • Don't do it unless you really want to.

Start here to read about the challenge: http://tav.espians.com/a-challenge-to-break-python-security.html

What situation do you want to use eval() in? Are you wanting a user to be able to execute arbitrary expressions? Or are you wanting to transfer data in some way? Perhaps it's possible to lock down the input in some way.

Jerub
+1: Who's the malicious person inserting the malicious code that you're eval()-ing? Find that person -- seriously. Most of 'eval is insecure' is hypothetical hand-waving about some evil person that no one can actually identify.
S.Lott
@S. Lott — Isn't one of the driving ideas behind security to *prevent* bad things from happening rather than afterwards hoping you can both identify the attacker and convince them not to do it again? Why discourage good practices?
Ben Blank
Any situation where a person on the greater internet to write untrusted code to run on your systems requires that the untrusted persons be unable to attack. "Who is this person" - if you wrote something insecure with eval() and I knew? It would be me to show you you shouldn't have done that.
Jerub
+2  A: 

You are probably better off turning the question around:

  1. What sort of expressions are you wanting to eval?
  2. Can you insure that only strings matching some narrowly defined syntax are eval()d?
  3. Then consider if that is safe.

For example, if you are wanting to let the user enter an algebraic expression for evaluation, consider limiting them to one letter variable names, numbers, and a specific set of operators and functions. Don't eval() strings containing anything else.

MarkusQ
Or, consider this: find the person who keeps putting malicious code into your eval expressions. Educate that person on the consequences of their anti-social behavior. Seriously. Who's putting the code in the eval expressions?
S.Lott
@S.Lott -- If he's writing cryptography programs it's either Carol or Eve, and for email handling its [email protected] but I don't know if we've ever identified who it is in the general case.
MarkusQ
I'd go one step ahead and ask *why* you want to do this at all.
Noufal Ibrahim
A: 

Note that even if you pass empty dictionaries to eval(), it's still possible to segfault (C)Python with some syntax tricks. For example, try this on your interpreter: eval("()"*8**5)

Benjamin Peterson
Can you post an example of those tricks?
phihag
Whoa, why did that cause a segfault?
jacob
It overflows the compiler's stack.
Benjamin Peterson
+5  A: 

You can get to os using builtin functions: __import__('os').

For python 2.6+, the ast module may help; in particular ast.literal_eval, although it depends on exactly what you want to eval.

John Fouhy
A: 

There is a very good article on the un-safety of eval() in Mark Pilgrim's Dive into Python tutorial.

Quoted from this article:

In the end, it is possible to safely evaluate untrusted Python expressions, for some definition of “safe” that turns out not to be terribly useful in real life. It’s fine if you’re just playing around, and it’s fine if you only ever pass it trusted input. But anything else is just asking for trouble.

Tim Pietzcker