Sử dụng eval () so với ast.literal_eval () của python?


176

Tôi có một tình huống với một số mã mà eval()đưa ra như là một giải pháp có thể. Bây giờ tôi chưa bao giờ phải sử dụng eval()trước đây, nhưng tôi đã tìm thấy rất nhiều thông tin về mối nguy hiểm tiềm tàng mà nó có thể gây ra. Điều đó nói rằng, tôi rất cảnh giác về việc sử dụng nó.

Tình huống của tôi là tôi có đầu vào được cung cấp bởi người dùng:

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

Trường hợp datamapcần phải là một từ điển. Tôi đã tìm kiếm xung quanh và thấy rằng eval()có thể làm việc này. Tôi nghĩ rằng tôi có thể kiểm tra loại đầu vào trước khi thử sử dụng dữ liệu và đó sẽ là một biện pháp phòng ngừa bảo mật khả thi.

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

Tôi đã đọc qua các tài liệu và tôi vẫn chưa rõ liệu điều này có an toàn hay không. Liệu eval đánh giá dữ liệu ngay khi nó được nhập hoặc sau khi datamapbiến được gọi?

astmô-đun là .literal_eval()lựa chọn an toàn duy nhất?

Câu trả lời:


189

datamap = eval(raw_input('Provide some data here: '))có nghĩa là bạn thực sự đánh giá mã trước khi bạn cho rằng nó không an toàn hay không. Nó đánh giá mã ngay khi hàm được gọi. Xem thêm những nguy hiểm củaeval .

ast.literal_eval đưa ra một ngoại lệ nếu đầu vào không phải là kiểu dữ liệu Python hợp lệ, vì vậy mã sẽ không được thực thi nếu không.

Sử dụng ast.literal_evalbất cứ khi nào bạn cần eval. Bạn thường không nên đánh giá các câu lệnh Python theo nghĩa đen.


20
Đây không phải là lời khuyên chính xác 100% vì mọi toán tử bitwise (hoặc toán tử quá tải) sẽ không thành công. Ví dụ. ast.literal_eval("1 & 1")sẽ ném một lỗi nhưng eval("1 & 1")sẽ không.
Daniel van Flymen

1
Chỉ tò mò thôi. Chúng ta không nên sử dụng trình phân tích cú pháp biểu thức hay cái gì đó nếu chúng ta mong đợi một cái gì đó như "1 & 1"?
thelinuxer

@thelinuxer bạn vẫn nên, vâng; bạn sẽ không thể sử dụng ast.literal_evalcho những thứ tương tự (ví dụ: bạn có thể triển khai trình phân tích cú pháp theo cách thủ công).
Biến động

104

ast.literal_eval() chỉ coi một tập hợp con nhỏ của cú pháp Python là hợp lệ:

Chuỗi hoặc nút được cung cấp chỉ có thể bao gồm các cấu trúc bằng chữ Python sau: chuỗi, số, bộ dữ liệu, danh sách, ký tự, booleans và Không có.

Đi __import__('os').system('rm -rf /a-path-you-really-care-about')vào ast.literal_eval()sẽ gây ra lỗi, nhưng eval()sẽ vui vẻ xóa sạch ổ đĩa của bạn.

Vì có vẻ như bạn chỉ cho phép người dùng nhập từ điển đơn giản, hãy sử dụng ast.literal_eval(). Nó an toàn làm những gì bạn muốn và không có gì hơn.


cũng hỗ trợ chuỗi byte (byte lớp). Ví dụ. b'Hello World '
XChikuX

52

eval: Điều này rất mạnh mẽ, nhưng cũng rất nguy hiểm nếu bạn chấp nhận các chuỗi để đánh giá từ đầu vào không đáng tin cậy. Giả sử chuỗi được đánh giá là "os.system ('rm -rf /')"? Nó thực sự sẽ bắt đầu xóa tất cả các tập tin trên máy tính của bạn.

ast.literal_eval: Đánh giá một cách an toàn một nút biểu thức hoặc một chuỗi chứa màn hình hiển thị bằng chữ hoặc container của Python. Chuỗi hoặc nút được cung cấp chỉ có thể bao gồm các cấu trúc bằng chữ Python sau: chuỗi, byte, số, bộ dữ liệu, danh sách, dicts, bộ, booleans, Không, byte và bộ.

Cú pháp:

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

Thí dụ:

# 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__':{}})

Trong đoạn mã trên ().__class__.__bases__[0]không có gì ngoài chính đối tượng. Bây giờ chúng tôi khởi tạo tất cả các lớp con , ở đây enter code heremục tiêu chính của chúng tôi là tìm một lớp có tên n từ nó.

Chúng ta cần codeđối tượng và functionđối tượng từ các lớp con khởi tạo. Đây là một cách khác CPythonđể truy cập các lớp con của đối tượng và đính kèm hệ thống.

Từ python 3.7 ast.literal_eval () bây giờ chặt chẽ hơn. Phép cộng và phép trừ các số tùy ý không còn được phép. liên kết


1
Tôi đang sử dụng python 2.7 và tôi vừa kiểm tra nó hoạt động tốt trên python 3.x. Thật tệ, tôi đã tiếp tục thử nó trên python 2.7
Mourya

3
ast.literal_eval("1+1")không hoạt động trong python 3.7 và như đã nói trước đó, lítal_eval nên được giới hạn ở nghĩa đen của một vài cấu trúc dữ liệu đó. Nó không thể phân tích cú pháp một hoạt động nhị phân.
Sesshu

Bạn có thể giải thích KABOOMmã của bạn , xin vui lòng? Tìm thấy nó ở đây:KABOOM
winklerrr

2
@winklerrr KABOOMđược giải thích độc đáo tại đây: nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
Elijas

41

Python háo hức trong đánh giá của nó, vì vậy eval(raw_input(...))sẽ đánh giá đầu vào của người dùng ngay khi nó chạm vào eval, bất kể bạn làm gì với dữ liệu sau đó. Do đó, điều này không an toàn , đặc biệt là khi bạn evalnhập người dùng.

Sử dụng ast.literal_eval.


Ví dụ, nhập thông tin này tại dấu nhắc sẽ rất, rất tệ cho bạn:

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

3

Nếu tất cả những gì bạn cần là một từ điển do người dùng cung cấp, giải pháp tốt hơn có thể là json.loads. Hạn chế chính là json dicts yêu cầu khóa chuỗi. Ngoài ra, bạn chỉ có thể cung cấp dữ liệu bằng chữ, nhưng đó cũng là trường hợp literal_eval.


1

Tôi đã bị mắc kẹt với ast.literal_eval(). Tôi đã thử nó trong trình gỡ lỗi IntelliJ IDEA và nó tiếp tục quay trở lại Nonevới đầu ra của trình gỡ lỗi.

Nhưng sau này khi tôi gán đầu ra của nó cho một biến và in nó bằng mã. Nó hoạt động tốt. Ví dụ mã chia sẻ:

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

Phiên bản python của nó 3.6.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.