ใช้ eval python () กับ ast.literal_eval () หรือไม่


176

ฉันมีสถานการณ์ที่มีรหัสบางอย่างที่eval()เกิดขึ้นเป็นวิธีแก้ปัญหาที่เป็นไปได้ ตอนนี้ฉันไม่เคยใช้eval()มาก่อน แต่ฉันได้เจอข้อมูลมากมายเกี่ยวกับอันตรายที่อาจเกิดขึ้นได้ ที่กล่าวว่าฉันระมัดระวังมากเกี่ยวกับการใช้มัน

สถานการณ์ของฉันคือฉันมีอินพุตที่ได้รับจากผู้ใช้:

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

ในกรณีที่datamapความต้องการที่จะเป็นพจนานุกรม ฉันค้นหาไปรอบ ๆ และพบว่าeval()สามารถแก้ไขปัญหานี้ได้ ฉันคิดว่าฉันอาจสามารถตรวจสอบชนิดของอินพุตก่อนที่จะพยายามใช้ข้อมูลและนั่นจะเป็นการป้องกันความปลอดภัยที่ทำงานได้

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

ฉันอ่านเอกสารและฉันยังไม่ชัดเจนว่าจะปลอดภัยหรือไม่ Eval ประเมินข้อมูลทันทีที่ป้อนหรือหลังจากdatamapเรียกตัวแปรหรือไม่

เป็นastโมดูล.literal_eval()ตัวเลือกที่ปลอดภัยเท่านั้น?

คำตอบ:


190

datamap = eval(raw_input('Provide some data here: '))หมายความว่าคุณประเมินรหัสจริง ๆก่อนที่จะถือว่าไม่ปลอดภัยหรือไม่ มันประเมินรหัสทันทีที่เรียกใช้ฟังก์ชัน ดูเพิ่มเติมอันตรายของeval

ast.literal_eval ทำให้เกิดข้อยกเว้นหากอินพุตไม่ใช่ประเภทข้อมูล Python ที่ถูกต้องดังนั้นรหัสจะไม่ถูกเรียกใช้ถ้าไม่ใช่

ใช้เมื่อใดก็ตามที่คุณต้องการast.literal_eval evalโดยปกติแล้วคุณไม่ควรประเมินข้อความ Python ตามตัวอักษร


20
นี่ไม่ใช่คำแนะนำที่ถูกต้อง 100% เนื่องจากตัวดำเนินการระดับบิตใด ๆ (หรือตัวดำเนินการโอเวอร์โหลด) จะล้มเหลว เช่น. ast.literal_eval("1 & 1")จะโยนข้อผิดพลาด แต่eval("1 & 1")จะไม่
Daniel van Flymen

1
แค่สงสัย. เราไม่ควรใช้ตัวแยกวิเคราะห์นิพจน์หรือบางอย่างถ้าเราคาดหวังบางอย่างเช่น "1 & 1"
thelinuxer

@theluxux คุณยังควรใช่ คุณจะไม่สามารถใช้ast.literal_evalสิ่งเช่นนั้นได้ (เช่นคุณสามารถใช้เครื่องมือแยกวิเคราะห์ด้วยตนเอง)
ความผันผวน

104

ast.literal_eval() พิจารณาว่าซินแท็กซ์ส่วนเล็ก ๆ ของไพ ธ อนนั้นถูกต้องเท่านั้น:

สตริงหรือโหนดที่จัดเตรียมอาจประกอบด้วยโครงสร้างตัวอักษร Python ดังต่อไปนี้เท่านั้น: สตริงตัวเลขสิ่งอันดับรายการ dicts บูลีนและไม่มี

การผ่าน__import__('os').system('rm -rf /a-path-you-really-care-about')เข้าไปast.literal_eval()จะทำให้เกิดข้อผิดพลาด แต่eval()จะทำความสะอาดไดรฟ์ของคุณ

ast.literal_eval()เพราะมันดูเหมือนว่าคุณกำลังเพียงปล่อยให้ผู้ใช้ป้อนพจนานุกรมธรรมดาใช้ มันทำสิ่งที่คุณต้องการอย่างปลอดภัยและไม่มีอะไรเพิ่มเติม


รองรับไบต์สตริง (คลาสไบต์) เช่นกัน เช่น. b'Hello World '
XChikuX

52

eval: มันมีประสิทธิภาพมาก แต่ก็เป็นอันตรายเช่นกันหากคุณยอมรับสตริงเพื่อประเมินจากอินพุตที่ไม่น่าเชื่อถือ สมมติว่าสตริงที่จะประเมินคือ "os.system ('rm -rf /')"? มันจะเริ่มลบไฟล์ทั้งหมดในคอมพิวเตอร์ของคุณ

ast.literal_eval: ประเมินโหนดการแสดงออกหรือสตริงที่มีการแสดงตัวอักษรหรือคอนเทนเนอร์ของ Python อย่างปลอดภัย สตริงหรือโหนดที่จัดเตรียมอาจประกอบด้วยโครงสร้างตัวอักษร Python ดังต่อไปนี้เท่านั้น: สตริง, ไบต์, ตัวเลข, สิ่งอันดับ, dicts, ชุด, บูลีน, ไม่มี, ไบต์และชุด

ไวยากรณ์:

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

ในโค้ดข้างต้น().__class__.__bases__[0]ไม่มีอะไรยกเว้นวัตถุตัวเอง ตอนนี้เราสร้างคลาสย่อยทั้งหมดขึ้นที่นี่enter code hereวัตถุประสงค์หลักของเราคือการหาชั้นหนึ่งชื่อnจากมัน

เราจำเป็นต้องcodeคัดแยกวัตถุและfunctionวัตถุจากคลาสย่อย นี่เป็นอีกวิธีหนึ่งในCPythonการเข้าถึง subclasses ของวัตถุและแนบระบบ

จาก python 3.7 ast.literal_eval () ตอนนี้เข้มงวดขึ้น ไม่อนุญาตการเพิ่มและการลบหมายเลขโดยพลการ ลิงค์


1
ฉันใช้ python 2.7 และฉันเพิ่งตรวจสอบว่าทำงานได้ดีกับ python 3.x ฉันไม่ดีฉันพยายามต่อไปกับ python 2.7
Mourya

3
ast.literal_eval("1+1")ไม่ทำงานใน python 3.7 และตามที่กล่าวไว้ก่อนหน้านี้ literal_eval ควร จำกัด เฉพาะตัวอักษรของโครงสร้างข้อมูลน้อยเหล่านั้น ไม่ควรแยกวิเคราะห์การดำเนินการแบบไบนารี
Sesshu

คุณช่วยอธิบายKABOOMรหัสของคุณได้ไหม พบได้ที่นี่:KABOOM
winklerrr

2
@winklerrr KABOOMมีการอธิบายไว้เป็นอย่างดีที่นี่: nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
Elijas

41

Python มีความกระตือรือร้นในการประเมินผลดังนั้นeval(raw_input(...))จะประเมินการป้อนข้อมูลของผู้ใช้ทันทีที่พบเห็นevalโดยไม่คำนึงถึงสิ่งที่คุณทำกับข้อมูลในภายหลัง ดังนั้นสิ่งนี้จึงไม่ปลอดภัยโดยเฉพาะเมื่อคุณevalป้อนข้อมูลผู้ใช้

ast.literal_evalใช้


ตัวอย่างเช่นการป้อนสิ่งนี้ที่พรอมต์จะแย่มากสำหรับคุณ:

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

3

json.loadsหากสิ่งที่คุณต้องเป็นผู้ใช้บริการที่มีให้พจนานุกรมทางออกที่ดีกว่าที่เป็นไปได้ ข้อ จำกัด หลักคือ json dicts ต้องใช้คีย์สตริง นอกจากนี้คุณสามารถให้ข้อมูลที่แท้จริง literal_evalแต่ที่ยังเป็นกรณีสำหรับ


1

ast.literal_eval()ผมติดอยู่กับ ฉันลองในตัวดีบัก IntelliJ IDEA และมันก็กลับมาNoneที่ผลลัพธ์ของตัวดีบัก

แต่ต่อมาเมื่อฉันมอบหมายเอาต์พุตให้กับตัวแปรและพิมพ์ในโค้ด มันใช้งานได้ดี ตัวอย่างรหัสการแชร์:

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

หลามเวอร์ชั่น 3.6

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.