Security of Python's Eval() on Untrusted Strings

Security of Python's eval() on untrusted strings?

You cannot secure eval with a blacklist approach like this. See Eval really is dangerous for examples of input that will segfault the CPython interpreter, give access to any class you like, and so on.

Is there a way to secure strings for Python's eval?

Here you have a working "exploit" with your restrictions in place - only contains lower case ascii chars or any of the signs +-*/() .
It relies on a 2nd eval layer.

def mask_code( python_code ):
s="+".join(["chr("+str(ord(i))+")" for i in python_code])
return "eval("+s+")"

bad_code='''__import__("os").getcwd()'''
masked= mask_code( bad_code )
print masked
print eval(bad_code)

output:

eval(chr(111)+chr(115)+chr(46)+chr(103)+chr(101)+chr(116)+chr(99)+chr(119)+chr(100)+chr(40)+chr(41))
/home/user

This is a very trivial "exploit". I'm sure there's countless others, even with further character restrictions.
It bears repeating that one should always use a parser or ast.literal_eval(). Only by parsing the tokens can one be sure the string is safe to evaluate. Anything else is betting against the house.

Safety of Python 'eval' For List Deserialization

It is indeed dangerous and the safest alternative is ast.literal_eval (see the ast module in the standard library). You can of course build and alter an ast to provide e.g. evaluation of variables and the like before you eval the resulting AST (when it's down to literals).

The possible exploit of eval starts with any object it can get its hands on (say True here) and going via .__class_ to its type object, etc. up to object, then gets its subclasses... basically it can get to ANY object type and wreck havoc. I can be more specific but I'd rather not do it in a public forum (the exploit is well known, but considering how many people still ignore it, revealing it to wannabe script kiddies could make things worse... just avoid eval on unsanitized user input and live happily ever after!-).

How safe is expression evaluation using eval?

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...

Why is using 'eval' a bad practice?

Yes, using eval is a bad practice. Just to name a few reasons:

  1. There is almost always a better way to do it
  2. Very dangerous and insecure
  3. Makes debugging difficult
  4. Slow

In your case you can use setattr instead:

class Song:
"""The class to store the details of each song"""
attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
def __init__(self):
for att in self.attsToStore:
setattr(self, att.lower(), None)
def setDetail(self, key, val):
if key in self.attsToStore:
setattr(self, key.lower(), val)

There are some cases where you have to use eval or exec. But they are rare. Using eval in your case is a bad practice for sure. I'm emphasizing on bad practice because eval and exec are frequently used in the wrong place.

Replying to the comments:

It looks like some disagree that eval is 'very dangerous and insecure' in the OP case. That might be true for this specific case but not in general. The question was general and the reasons I listed are true for the general case as well.

Math Eval String in DataFrame Python

Although there are security concerns, you can use eval to evaluate each element using a lambda expression.

df = pd.DataFrame({'X': ['25+4', '25+5', '15+3', '20+2', '20+3']})

>>> df
X
0 25+4
1 25+5
2 15+3
3 20+2
4 20+3

>>> df.X.apply(lambda x: eval(x))
0 29
1 30
2 18
3 22
4 23
Name: X, dtype: int64

For a description of security concerns, see:

  • Security of Python's eval() on untrusted strings?

  • https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html

  • Using python's eval() vs. ast.literal_eval()?

Change string to list datatype

The string you have is valid json, so you can just parse it:

import json

x = '["a","b"]'

l = json.loads(x)

print(l)
# ['a', 'b']

print(type(l))
# <class 'list'>


Related Topics



Leave a reply



Submit