programing

python's eval() vs. ast.literal_eval() 사용

prostudy 2022. 9. 14. 21:40
반응형

python's eval() vs. ast.literal_eval() 사용

어떤 코드에 문제가 있어서eval()생각할 수 있는 해결책으로 떠올랐습니다.이제 나는 한 번도 사용할 필요가 없었다.eval()하지만, 나는 그것이 야기할 수 있는 잠재적인 위험에 대한 많은 정보를 접했다.하지만, 저는 그것을 사용하는 것에 대해 매우 조심하고 있습니다.

사용자로부터 입력을 받고 있는 상황입니다.

datamap = input('Provide some data here: ')

어디에datamap사전이어야 합니다.여기저기 찾아보니eval()해결할 수 있을 거야데이터를 사용하기 전에 입력의 종류를 확인할 수 있을 것 같아서 보안상의 대책을 강구할 수 있을 것 같습니다.

datamap = eval(input('Provide some data here: ')
if not isinstance(datamap, dict):
    return

나는 그 문서들을 읽었지만 이것이 안전한지 아닌지 아직 확실하지 않다.데이터가 입력되는 즉시 또는 입력된 후에 평가합니까?datamap변수는 호출됩니까?

이요?ast모듈.literal_eval()안전한 유일한 방법인가?

datamap = eval(input('Provide some data here: '))안전하지 않다고 판단하기 전에 실제로 코드를 평가한다는 의미입니다.함수가 호출되는 즉시 코드를 평가합니다.의 위험성도 참조해 주세요.

ast.literal_eval 입력이 유효한 Python 데이터형이 아닌 경우 예외를 발생시키므로 입력이 올바르지 않은 경우 코드가 실행되지 않습니다.

사용하다ast.literal_eval필요할 때 언제든지.보통 리터럴 Python 문을 평가해서는 안 됩니다.

ast.literal_eval() Python 구문 중 일부만 유효하다고 간주합니다.

제공된 문자열 또는 노드는 문자열, 바이트, 숫자, 튜플, 목록, 딕트, 세트, 부울란 및 Python 리터럴 구조로만 구성될 수 있습니다.None.

패스__import__('os').system('rm -rf /a-path-you-really-care-about')안으로ast.literal_eval()에러가 발생하지만 파일은 삭제됩니다.

사용자가 일반 사전을 입력할 수 있도록 하는 것처럼 보이므로ast.literal_eval()원하는 대로 안전하게 할 수 있습니다.

eval: 이것은 매우 강력하지만 신뢰할 수 없는 입력에서 평가하는 문자열을 받아들이는 경우에도 매우 위험합니다.평가 대상 문자열이 "os.system('rm -rf /')"이라고 가정하면 컴퓨터상의 모든 파일이 삭제됩니다.

ast.literal_eval: 표현식 노드 또는 Python 리터럴 또는 컨테이너 디스플레이를 포함하는 문자열을 안전하게 평가합니다.제공된 문자열 또는 노드는 문자열, 바이트, 숫자, 튜플, 목록, 딕트, 세트, 부울란, 없음, 바이트 및 집합의 Python 리터럴 구조로만 구성될 수 있습니다.

문::

eval(expression, globals=None, locals=None)
import ast
ast.literal_eval(node_or_string)

예제:

# python 2.x - doesn't accept operators in string format
import ast
ast.literal_eval('[1, 2, 3]')  # output: [1, 2, 3]
ast.literal_eval('1+1') # output: ValueError: malformed string


# python 3.0 -3.6
import ast
ast.literal_eval("1+1") # output : 2
ast.literal_eval("{'a': 2, 'b': 3, 3:'xyz'}") # output : {'a': 2, 'b': 3, 3:'xyz'}
# type dictionary
ast.literal_eval("",{}) # output : Syntax Error required only one parameter
ast.literal_eval("__import__('os').system('rm -rf /')") # output : error

eval("__import__('os').system('rm -rf /')") 
# output : start deleting all the files on your computer.
# restricting using global and local variables
eval("__import__('os').system('rm -rf /')",{'__builtins__':{}},{})
# output : Error due to blocked imports by passing  '__builtins__':{} in global

# But still eval is not safe. we can access and break the code as given below
s = """
(lambda fc=(
lambda n: [
    c for c in 
        ().__class__.__bases__[0].__subclasses__() 
        if c.__name__ == n
    ][0]
):
fc("function")(
    fc("code")(
        0,0,0,0,"KABOOM",(),(),(),"","",0,""
    ),{}
)()
)()
"""
eval(s, {'__builtins__':{}})

코드 기기 in in in in in in().__class__.__bases__[0]단지 그 자체일 뿐이다.이제 모든 서브클래스를 인스턴스화했습니다.여기서 메인enter code here여기서 n이라는 이름의 클래스를 찾는 것이 목적입니다.

는 할 가 있다.code 및 "" " " " "function이치노은 '보다 낫다'와 방법입니다.CPython오브젝트 서브클래스에 접속하여 시스템을 접속합니다.

python 3.7 ast.literal_eval()부터는 엄격해졌습니다.임의 숫자의 덧셈과 뺄셈은 더 이상 허용되지 않습니다.링크

Python은 평가에 열심입니다.eval(input(...))3)가 (Python 3)에 eval나중에 데이터를 어떻게 처리하든 상관없습니다.그러므로, 이것은 안전하지 않다. 특히 당신이eval사용자 입력

ast.literal_eval.


예를 들어, 프롬프트에 이것을 입력하는 것은 매우 나쁠 수 있습니다.

__import__('os').system('rm -rf /a-path-you-really-care-about')

최근의 Python3 에서는 단순한 문자열을 해석하는 것이 아니라 ast.parse() 메서드를 사용하여 AST를 작성하고 해석해야 합니다.

이것은 간단한 산술식을 안전하게 평가하기 위해 Python 3.6+에서 ast.parse()를 올바르게 사용하는 완전한 예입니다.

import ast, operator, math
import logging

logger = logging.getLogger(__file__)

def safe_eval(s):

    def checkmath(x, *args):
        if x not in [x for x in dir(math) if not "__" in x]:
            raise SyntaxError(f"Unknown func {x}()")
        fun = getattr(math, x)
        return fun(*args)

    binOps = {
        ast.Add: operator.add,
        ast.Sub: operator.sub,
        ast.Mult: operator.mul,
        ast.Div: operator.truediv,
        ast.Mod: operator.mod,
        ast.Pow: operator.pow,
        ast.Call: checkmath,
        ast.BinOp: ast.BinOp,
    }

    unOps = {
        ast.USub: operator.neg,
        ast.UAdd: operator.pos,
        ast.UnaryOp: ast.UnaryOp,
    }

    ops = tuple(binOps) + tuple(unOps)

    tree = ast.parse(s, mode='eval')

    def _eval(node):
        if isinstance(node, ast.Expression):
            logger.debug("Expr")
            return _eval(node.body)
        elif isinstance(node, ast.Str):
            logger.debug("Str")
            return node.s
        elif isinstance(node, ast.Num):
            logger.debug("Num")
            return node.value
        elif isinstance(node, ast.Constant):
            logger.info("Const")
            return node.value
        elif isinstance(node, ast.BinOp):
            logger.debug("BinOp")
            if isinstance(node.left, ops):
                left = _eval(node.left)
            else:
                left = node.left.value
            if isinstance(node.right, ops):
                right = _eval(node.right)
            else:
                right = node.right.value
            return binOps[type(node.op)](left, right)
        elif isinstance(node, ast.UnaryOp):
            logger.debug("UpOp")
            if isinstance(node.operand, ops):
                operand = _eval(node.operand)
            else:
                operand = node.operand.value
            return unOps[type(node.op)](operand)
        elif isinstance(node, ast.Call):
            args = [_eval(x) for x in node.args]
            r = checkmath(node.func.id, *args)
            return r
        else:
            raise SyntaxError(f"Bad syntax, {type(node)}")

    return _eval(tree)


if __name__ == "__main__":
    logger.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    logger.addHandler(ch)
    assert safe_eval("1+1") == 2
    assert safe_eval("1+-5") == -4
    assert safe_eval("-1") == -1
    assert safe_eval("-+1") == -1
    assert safe_eval("(100*10)+6") == 1006
    assert safe_eval("100*(10+6)") == 1600
    assert safe_eval("2**4") == 2**4
    assert safe_eval("sqrt(16)+1") == math.sqrt(16) + 1
    assert safe_eval("1.2345 * 10") == 1.2345 * 10

    print("Tests pass")

사전만 더 수 .json.loadsjson dicts son 、 json dicts 、 json dicts 에 son 、 json dicts 。데이터만 할 수 , 는 "의 입니다.literal_eval.

나는 에 갇혔다.ast.literal_eval()디버거에서 했는데, 'J IDEA'가 돌아왔어요.None디버거 출력에 표시됩니다.

하지만 나중에 그 출력을 변수에 할당하고 코드로 인쇄했을 때.그것은 잘 작동했다.공유 코드 예:

import ast
sample_string = '[{"id":"XYZ_GTTC_TYR", "name":"Suction"}]'
output_value = ast.literal_eval(sample_string)
print(output_value)

Python 버전 3.6입니다.

언급URL : https://stackoverflow.com/questions/15197673/using-pythons-eval-vs-ast-literal-eval

반응형