Is this a safe use of eval?

Ryan Kelly ryan at rfk.id.au
Thu Feb 24 04:26:02 EST 2011


On Thu, 2011-02-24 at 20:13 +1100, Ryan Kelly wrote:
> On Thu, 2011-02-24 at 10:48 +0200, Frank Millman wrote:
> > Hi all
> > 
> > I know that the use of 'eval' is discouraged because of the dangers of 
> > executing untrusted code.
> > 
> > Here is a variation that seems safe to me, but I could be missing something.
> > 
> > I have a class, and the class has one or more methods which accept various 
> > arguments and return a result.
> > 
> > I want to accept a method name and arguments in string form, and 'eval' it 
> > to get the result.
> > 
> > Assume I have an instance called my_inst, and a method called 'calc_area', 
> > with arguments w and h.
> > 
> > I then receive my_string  = 'calc_area(100, 200)'.
> > 
> > >>> result = eval('my_inst.{0}'.format(my_string))
> > 
> > This will only work if the string contains a valid method name with valid 
> > arguments.
> > 
> > Can anyone see anything wrong with this?
> 
> Yes.  A serious problem.
>
> 
> But actually it allows all sorts of nasty things.  Watch me open an
> arbitrary file on your system (assuming you have permission of course).
>
>     >>> testit('__class__.__mro__[-1].__subclasses__()[58]("/secret/file","w")')
>     Traceback (most recent call last):
>       File "<stdin>", line 1, in <module>
>       File "<stdin>", line 2, in testit
>       File "<string>", line 1, in <module>
>     IOError: [Errno 2] No such file or directory: '/secret/file'
>     >>>

Just to elaborate a little more.  Once you've got a reference to
'object' the game it pretty much over - you can walk its subclasses to
get to all kinds of nasty things like 'file'.

Since this was a newstyle class, getting to 'object' was easy - it's
always the class's final base class (i.e. __mro__[-1]).

You might think that using an old-style class would save you:

    >>> class MyClass:
    ...     pass
    ... 
    >>> inst = MyClass()
    >>> inst.__class__.__mro__[-1]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: class MyClass has no attribute '__mro__'


But there will almost always be a reference to a builtin type hanging
around somewhere.  Builtin types are all newstyle classes, and hence
have 'object' in their mro.  For example:

    >>> inst.__dict__.__class__.__mro__[-1]
    <type 'object'>
    >>> 

Or perhaps:

    >>> inst.__class__.__name__.__class__.__mro__[-1]
    <type 'object'>


It's pretty much impossible to prevent this kind of thing in python.

You might get away with it if you parse the string looking for
suspicious characters e.g. dots.  But that will limit your grammar and I
won't be surprised if there's still a way around it.

> So please, don't do this!  :-)

So what's the alternative?  As suggested by another poster, for simple
cases use the ast.literal_eval function.

For anything more complicated, use PyParsing to generate your own mini
language and interpret it yourself.  It's really pretty simple once you
get in the right head-space.  Try some of the links from this SO post if
you want to start down this path:

   http://stackoverflow.com/questions/1545403/math-expression-evaluation



  Cheers,

     Ryan


-- 
Ryan Kelly
http://www.rfk.id.au  |  This message is digitally signed. Please visit
ryan at rfk.id.au        |  http://www.rfk.id.au/ramblings/gpg/ for details

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: This is a digitally signed message part
URL: <http://mail.python.org/pipermail/python-list/attachments/20110224/86c54b7a/attachment.sig>


More information about the Python-list mailing list