eval()
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
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 디렉토리의 정보가 그대로 노출
__import__('os').system('rm -rf /')
같은 위험한 값을 사용자가 입력했을 때 그대로 실행할 수 있음!
→__import__('os').system('rm -rf /')
코드는 매우 위험한 Python 명령어입니다.
절대로 실행해서는 안 됩니다.
이 코드는 Python의 내장 함수인 import()를 사용하여 os 모듈을 동적으로 가져오고, 그 모듈의 system() 함수를 호출하여 'rm -rf /' 명령을 실행하려고 합니다.
'rm -rf /' 명령은 Unix 계열 운영체제에서 루트 디렉토리(/)부터 시작해 모든 파일과 디렉토리를 강제로 재귀적으로 삭제하는 매우 위험한 명령입니다.
이 명령이 실행되면 시스템의 모든 데이터가 영구적으로 삭제될 수 있으며, 운영체제가 완전히 손상될 수 있습니다. 따라서 이런 코드는 절대 실행해서는 안 되며, 보안상의 이유로 대부분의 현대 운영체제에서는 이런 명령의 실행을 방지하는 안전장치가 있습니다.
이러한 코드는 악의적인 목적으로 사용될 수 있으므로, 출처가 불분명하거나 신뢰할 수 없는 코드는 실행하지 않도록 주의해야 합니다.
→ cf. Python Pickle에서 언피클을 신중하게 해야 하는 이유: 신뢰할 수 없는 데이터를 함부로 unpickle 하면, 매우 위험한 함수가 호출될 수 있습니다. 따라서, 신뢰할 수 없는 데이터는 절대로 unpickle하면 안 됩니다.
eval() 함수는 해당 표현식을 그대로 실행하기 때문에 Command Injection Flaws를 그대로 노출할 수 있으며 대형 참사로 이어질 수 있음
이뿐 아니라, eval() 명령은 코드의 가독성을 떨어뜨리고 디버깅을 어렵게 만들 수 있음
또한 eval을 사용해 일부 로컬 환경에 의존하도록 구현할 경우 환경 의존성도 생길 수 있으므로 되도록 사용하지 않는 방향을 권장
literal_eval은 정말 안전한 eval인 것인가
literal_eval
dictionary 형태로 저장된 string값을 str_dict에 저장 후, 이를 literal_eval을 통해 실제 dictionary로 가공할 수 있음
import aststr_dict = "{'a': 3, 'b': 5}"print type(str_dict) # <type 'str'>convert_dict = ast.literal_eval(str_dict)print type(convert_dict) # <type 'dict'>print convert_dict['a'] # 3print convert_dict['b'] # 5
import ast
str = '__import__("os").system("ls /")'
print type(str) # <type 'str'>
eval(str)
'''
Applications Volumes home tmp
Developer bin installer.failurerequests usr
Library cores macOS Install Data var
Network data net
System dev private
Users etc sbin
'''
ast.literal_eval(str)
'''
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ast.py", line 80, in literal_eval
return _convert(node_or_string)
File "/usr/local/Cellar/python@2/2.7.15_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ast.py", line 79, in _convert
raise ValueError('malformed string')
ValueError: malformed string
'''
→ literal_eval의 경우는 ValueError를 발생시킴킴
가만히 보면 Python 표현식(expression statement)도 문자열(str)
a = 1 + 2
라는 구문(statement)이 있을 때 ‘1 더하기 2’를 나타낸 우변도 결국 '1 + 2' 형태의 문자열이라는 의미Python 작업을 하다 보면 이렇게 문자열에 담겨 있는 표현식을 꺼내 써야 할 때가 종종 발생함
예시
동창회 멤버 10명의 생년월일 데이터를 자주 사용하는 모임 총무가 있다고 칩시다. 길이(len)가 고작 10이고 각 요소(element)도 8자리 정수(int)인 자그마한 데이터를 굳이 pickle 같은 것을 사용해 보관할 필요는 없습니다.(오히려 번거롭지요.)
birth_day_list = ['19950415', '19950527', '19951103', '19950606', '19950717', '19950327', '19951030', '19950424', '19950815', '19950901']
print(len(birth_day_list)) # 10
print(len(birth_day_list[0])) # 8
이 정도의 데이터는 Python 표현식 모양 그대로 메모장이나 클라우드 노트(Evernote, OneNote 따위)에 저장해 놓았다가 복사/붙여넣기로 다시 가져다 쓰는 경우가 많음
표현식의 일괄 읽기는 그렇게 어렵지 않음
open()
, read()
, readlines()
를 적절히 쓰면 됨list_in_str = '[1, 2, 3, 4, 5]'
list_data = list_in_str
print(type(list_data)) # str - wrong solution
list_in_str
을 그대로 재할당하는 작업일 뿐임list_in_str
에 내포된 데이터를 복구해 낼 수 있을까?ast.literal_eval()
을 사용하는 것import ast
list_in_str = '[1, 2, 3, 4, 5]'
list_data = ast.literal_eval(list_in_str)
print(type(list_data)) # list - correct solution
import ast
list_in_str = "{'a': 1, 'b': 2, 'c': 3}"
list_data = ast.literal_eval(list_in_str)
print(type(list_data)) # dict
사실 eval()이라는 내장(built-in) 함수가 있습니다. 그렇지만 이 함수는 악용될 소지가 커 가급적 사용을 자제해야 합니다.
한편, ast.literal_eval()은 위에서 우리가 하려고 했던 목적을 이루게 해 주면서도, 안전성을 보장합니다. 정확히 이야기하자면, 인자(parameter)로 주어진 표현식을 실행하기 전에 그것이 ‘수상한 표현식’인지 아닌지 먼저 검증합니다.
ast.listeral_eval()
은 안전한 대신 인자로 제한된 리터럴(literal)로 구성된 표현식만 허용eval()이 내장 함수이기도 하고 Python이 아닌 다른 언어에도 보통 등장하는 기능이라, 무심코 사용하는 경향이 있습니다. 물론 개인적인 용도로 사용할 프로그램이거나 믿을 수 있는 사용자들만 사용할 프로그램이라면 괜찮겠으나, 믿을 수 있는 그룹이 아닌 불특정 다수 그룹이 사용할 것이라면, eval()의 사용을 최소한으로 하고 ast.literal_eval()과 같은 대체 기능을 사용해야 합니다.