ㅍㅍㅋㄷ

python eval() 함수 - 사용을 조심해야 하는 이유 본문

Programming/Python

python eval() 함수 - 사용을 조심해야 하는 이유

클쏭 2019. 1. 22. 17:32

python eval() 함수


 python 의 built-in 함수 중 하나인 eval 함수는 매우 강력하면서도 사용을 자제 하도록 권고하는 양날의 검과 같은 기능이다.

 먼저 python docs 의 정의를 보자.


eval(expression, globals=None, locals=None)


 The arguments are a string and optional globals and locals. If provided, globals must be a dictionary. If provided, locals can be any mapping object.

 The expression argument is parsed and evaluated as a Python expression (technically speaking, a condition list) using the globals and locals dictionaries as global and local namespace. If the globals dictionary is present and does not contain a value for the key __builtins__, a reference to the dictionary of the built-in module builtins is inserted under that key before expression is parsed. This means that expression normally has full access to the standard builtins module and restricted environments are propagated. If the locals dictionary is omitted it defaults to the globals dictionary. If both dictionaries are omitted, the expression is executed in the environment where eval() is called. The return value is the result of the evaluated expression. Syntax errors are reported as exceptions.


 eval 함수는 expression 인자에 string 값을 넣으면 해당 값을 그대로 실행하여 결과를 출력해 준다.

 어떤 방식인지는 아래를 보면 쉽게 이해가 될 것이다.


eval("(5 * 10) / 2")

>>> 25


간단한 연산식을 string 에 저장 후 eval로 실행하면 위와 같이 연산된 값을 얻을 수 있다.

좀 더 명확히 살펴보자.


expr = "10 + 10"


type(expr)

>>> <type 'str'>


eval(expr)

>>> 20


변수 expr은 string 타입인데도 불구하고, eval 로 실행하면 그대로 연산 값을 얻을 수 있다는 편리함이 있다.

단순 연산뿐 아니라 내장 함수나 객체도 실행 가능하다.

 

eval( "max([1, 2, 3, 4])" )

>>> 4



list = ['a', 'b', 'c', 'd', 'e']

expr = "len(list)"


eval(expr)

>>> 5


여기까지 보면 eval 은 굉장히 사용성이 넓고 유용하게 느껴질 수 있다.

하지만, eval 은 이러한 자유스로움으로 인해 발생할 수 있는 커다란 위험을 내포하고 있다.


아래와 같은 간단한 함수를 만들었다고 가정해보자. 


x = str(input(">> input some text for mathematical expression : ")


print eval(x)


사용자에게 수학 표현식을 문자열로 받고, 계산 결과값을 출력해주는 간단한 함수이다.

하지만 이때 사용자가 아래와 같이 입력을 하면 어떻게 될까.


>> input some text for mathematical expression: __import__('os').system('ls /')


Applications Volumes home tmp

Developer bin installer.failurerequests usr

Library cores macOS Install Data var

Network data net

System dev private

Users etc sbin


 서버의 root 디렉토리의 정보가 그대로 노출되었다.


>> input some text for mathematical expression: __import__('os').system('rm -rf /')    # 흐흐흐


이와 같이 eval() 함수는 해당 표현식을 그대로 실행하는 것이기 때문에 Command Injection Flaws 를 그대로 노출할 수 있으며 대형 참사로 이어질 수 있다.

이는 시스템 명령을 삽입할 수 있는 스크립트 언어(PHP, Javascript 등)은 모두가 가지고 있는 취약점이기도 하다.


 이뿐 아니라, eval() 명령은 코드의 가독성을 떨어뜨리고 디버깅을 어렵게 만들수 있다. 또한 eval을 사용해 일부 로컬 환경에 의존하도록 구현할 경우 환경 의존성도 생길 수 있으므로 되도록 사용하지 않는 방향을 권장한다.


 마지막으로, ast module에 literal_eval 이라는 것이 있는데 안전한 evaluate 를 제공한다고 되어 있는데 약간의 사용 범위가 다른 녀석이다. (속지말자)

literal_eval 의 경우는 다음 포스팅에서 소개할 예정이다.


 ( python eval 과 literal_eval의 차이 )



[참고]

  • https://docs.python.org/3/library/functions.html#eval
  • https://stackoverflow.com/questions/1832940/why-is-using-eval-a-bad-practice


Comments