7

OK. I know the experts have spoken and you should not ever use python's eval() on untrusted data, ever. I'm not smarter than the rest of the world, and shouldn't even try this. But! I'm going to, anyhow.

My basic problem is that I'm looking to write a little calculator evaluator program that'll take untrusted input, using a subset of python's syntax. I know: use ply or pyparsing and write a parser and there we go. Screwing around with passing globals and locals to eval() will not do the trick.

All the approaches I've seen (and been leery about) try to enumerate evil. Here, I'm trying to enumerate good -- get an AST, allow only a few node types, and then verify that any calls are to one of a set of whitelisted functions. Here's a mini-implementation (and a gist):

import ast
import math

SAFE_FX = {
    'exp': math.exp,
}

SAFE_NODES = set(
    (ast.Expression,
    ast.Num,
    ast.Call,
    ast.Name,
    ast.Load,
    ast.BinOp,
    ast.Add,
    ast.Sub,
    ast.Mult,
    ast.Div,)
)

class CleansingNodeVisitor(ast.NodeVisitor):
    def generic_visit(self, node):
        if type(node) not in SAFE_NODES:
            raise Exception("%s not in SAFE_NODES" % type(node))
        super(CleansingNodeVisitor, self).generic_visit(node)

    def visit_Call(self, call):
        if call.func.id not in SAFE_FX:
            raise Exception("Unknown function: %s" % call.func.id)

def my_safe_eval(s):
    tree = ast.parse(s, mode='eval')
    cnv = CleansingNodeVisitor()
    cnv.visit(tree)
    compiled = compile(tree, s, "eval")
    return(eval(compiled, SAFE_FX))

So, my_safe_eval('2*(4+exp(1.3))') works, while my_safe_eval('[].__class__') tricks and my_safe_eval('open("/something/evil")') is likewise forbidden -- without forbidding __builtins__ or __locals__ or anything.

I... I think this works. Am I mad?

Community
  • 1
  • 1
Nate
  • 4,561
  • 2
  • 34
  • 44
  • this is very similar to [JF Sebastians answer](http://stackoverflow.com/a/9558001/1020470) and this [question bySudoNhim](http://stackoverflow.com/questions/10661079/restricting-pythons-syntax-to-execute-user-code-safely-is-this-a-safe-approach) and [his own answer](http://stackoverflow.com/a/10654285/1020470) – Mark Mikofski Aug 07 '13 at 07:06
  • 2
    See also this: http://tomforb.es/breaking-out-of-secured-python-environments – Nate Sep 27 '13 at 19:47
  • It's only kind of another example, because I am in fact that same Nate Vack :) – Nate Oct 01 '13 at 21:33

2 Answers2

4

Zope has a thing called RestrictedPython, you may want to check it, at least to validate your approach or possibly reuse their code. It is configurable and re-usable.

Here's my other answer to a similar question.

Community
  • 1
  • 1
Sergey
  • 11,892
  • 2
  • 41
  • 52
  • RestrictedPython looks neat, but man, it's big. I really want to safely support only a tiny subset of python's syntax, and only in evaluating expressions and a limited set of function calls. I don't think a sandboxed python is really what I'm looking for. Can't totally say why. – Nate Sep 21 '12 at 03:51
  • Great, but how to deal with `while True:pass` for example? – USERNAME GOES HERE Nov 10 '20 at 14:00
  • Excellent answer! Zope was ahead of its times. – Nishant Aug 01 '22 at 13:02
2

Try asteval, seems like the thing you need. Otherwise there is this safe eval

Ajay
  • 161
  • 11
  • I am looking at [asteval](http://newville.github.io/asteval/) too and am very interested to hear peoples input. [@Nate](http://stackoverflow.com/users/12779/nate) did you benchmark your code against it? Have you tried all of the exploits on [ned batchelder's post](http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html)? So excited to see people taking a stab at this! – Mark Mikofski Aug 07 '13 at 03:10
  • asteval can't be exploited because it is extremely limited and it removes at the level of AST. I had to drop it for my projects because it is that limited. In place I used spydermonkey and pyv8.. but that was an overload in context switching between 2 different languages. So I ended up either with pypy or just removing eval capabilites replacing them with simple parser - best decision. Most of the times you don't need Turing completeness... – Ajay Nov 28 '16 at 18:23