views:

156

answers:

2

I am building a website where I have a need that user should be able to evaluate some expression based from the value in DB tables, instead of using tools like pyparsing etc, I am thinking of using python itself, and have come up with a solution which is sufficient for my purpose. I am basically using eval to evaluate the expression and passing globals dict with empty __builtins__ so that nothing can be accessed and a locals dict for values from DB, if user will need some functions I can pass those too e.g.

import datetime
def today():
    return datetime.datetime.now()

expression = """ first_name.lower() == "anurag" and today().year == 2010 """

print eval(expression, {'__builtins__':{}}, {'first_name':'Anurag', 'today':today})

So my question is how safe it would be , I have three criteria

  1. Can user access current state of my program or table etc someshow?
  2. Can user have access to os level calls?
  3. Can user halt my system by looping or using much memory e.g. by doing range(10*8), in some cases he can e.g 100**1000 etc so 3 is not so much of a problem. i may check such op with tokenize and anyway I will be using GAE so it is not not much of concern.

Edit: IMO this is not the duplicate of Q:661084 because where it ends this one starts, I want to know even with __builtins__ blocked, can user do bad things?

+3  A: 

Certainly it's possible to consume all available memory or create an infinite loop even without the builtins. There are many ways to do it such as 'a'*999999*999999 or to make an infinite loop:

>>> print eval('[[x.append(a) for a in x] for x in [[0]]]',
...             {'__builtins__':{}}, {'first_name':'Anurag', 'today':today})

As for 1) and 2), I'm not sure but it looks risky. Here is one thing that I tried that I thought would work, but it seems that someone else already considered that line of attack and blocked it:

>>> import datetime
>>> def today():
>>>     return datetime.datetime.now()
>>>
>>> print eval('today.func_globals', {'__builtins__':{}}, {'first_name':'Anurag', 'today':today})
RuntimeError: restricted attribute

I was half expecting to get this instead:

{'__builtins__': <module '__builtin__' (built-in)>, ...

So I think it's probably a bad idea. You only need one tiny hole and you give access to your entire system. Have you considered other methods that don't use eval? What is wrong with them?

Mark Byers
yes setting __builtins__ variable, restricted execution mode kicks in :)
Anurag Uniyal
+1 for 'restricted attribute' I wasn't knowing that, looks like it is due to old reexec module, I wanted to use eval because if I can why not?
Anurag Uniyal
+4  A: 

It's completely unsafe to use eval, even with built-ins emptied and blocked -- the attacker can start with a literal, get its __class__, etc, etc, up to object, its __subclasses__, and so forth... basically, Python introspection is just too strong to stand up to a skilled, determined attacker.

ast.literal_eval is safe, if you can live by its limitations...

Alex Martelli
ok I believe you :) but some counter-example would be fun, anyway I will try PyParsing then.
Anurag Uniyal
The hint that you can get to *any* type or class (by moving up to `object` e.g. via `__class__` and `__mro__` or `__bases__`, then down via `__subclasses__`, ...) should be sufficient as "counterexample": let malicious hackers who want to play destructive pranks at least work a minute to build their solutions...!-)
Alex Martelli