How do you set up a stack?

Alex Martelli aleaxit at yahoo.com
Wed May 9 05:48:03 EDT 2001


<s713221 at student.gu.edu.au> wrote in message
news:3AF98717.67080E1F at student.gu.edu.au...
    ...
> Eval will (should?) only evaluate legal pythonic numeric functions. This

>>> eval('__import__("os").system("echo yeah right")')
yeah right
0
>>>

Not sure what you mean by 'should' here, but it definitely doesn't
work that way in Python.  Guess what substrings could take the
place of that "echo yeah right" and possibly cause problems...:-).

> operations, you shouldn't have to worry too much about using eval.

DO worry any time you eval or exec code coming from a user
you don't trust 101%...:-).  Taking precautions is better
than worrying, of course -- you can prepare dictionaries, to
be passed to eval as its namespace-environments, that will
take away all the dangerous builtin, and as you're at it you
might as well inject the useful math operations from the
math module's namespace, etc.

> However, all of these are dealing with numbers. I'd be interested to see
> if someone did have an example of a malicious eval use. (In fact I'd be
> downright anxious to know of any eval security weaknesses. *grins*)

Hmmm, is my example above sufficient...?

> I try to eval a non-numerical statement.

Statements are not a worry for eval (only for exec), but that's not
the point -- destructive behavior can easily be had in expressions
that include function calls to suitable library functions.  So,
go through the list of builtins and only include those that are
truly needed for the expressions you want to eval (if any) -- I
expect '__import__' will not be there, for example:-).

>>> nobi={'__builtins__':None}
>>> eval('__import__("os").system("echo yeah right")',nobi)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<string>", line 0, in ?
NameError: There is no variable named '__import__'
>>>

Passing 'nobi' as the namespace for the eval removes all
built-ins, including in particular "import".  We may do
better...:

>>> import math
>>> nobi.update(math.__dict__)
>>> # check nothing was broken
...
>>> eval('__import__("os").system("echo yeah right")',nobi)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<string>", line 0, in ?
NameError: There is no variable named '__import__'
>>> # ok, now check math works as intended
...
>>> eval('sin(0.3)',nobi)
0.29552020666133955
>>>


Not too much work, is it?  And you may choose to do even
more, again without too much work, by examining all the
functions and variables the expression-in-string-form
may need, by splitting the eval step into a preliminary
compile() call, that gives you a code-object -- you
can check the names it uses and prompt the user for
variables you don't yet know about, e.g.:

>>> code=compile('sin(x)','<user-entered string>','eval')
>>> for name in code.co_names:
...     if not nobi.has_key(name):
...         value = raw_input("Variable %s:"%name)
...         nobi[name] = float(value)
...
Variable x:0.3
>>> eval(code,nobi)
0.29552020666133955
>>>


Alex






More information about the Python-list mailing list