Menggunakan eval python () vs. ast.literal_eval ()?

176

Saya memiliki situasi dengan beberapa kode di mana eval()muncul sebagai solusi yang mungkin. Sekarang saya belum pernah menggunakan eval()sebelumnya tetapi, saya telah menemukan banyak informasi tentang potensi bahaya yang dapat ditimbulkannya. Yang mengatakan, saya sangat khawatir menggunakannya.

Situasi saya adalah saya mendapat input dari pengguna:

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

Di mana datamapperlu kamus. Saya mencari-cari dan menemukan bahwa ini eval()bisa berhasil. Saya berpikir bahwa saya mungkin dapat memeriksa jenis input sebelum mencoba menggunakan data dan itu akan menjadi tindakan pencegahan keamanan yang layak.

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

Saya membaca dokumen dan masih belum jelas apakah ini aman atau tidak. Apakah evaluasi mengevaluasi data segera setelah dimasukkan atau setelah datamapvariabel dipanggil?

Apakah astmodul .literal_eval()satu-satunya opsi yang aman?

tijko
sumber

Jawaban:

190

datamap = eval(raw_input('Provide some data here: '))berarti Anda benar-benar mengevaluasi kode sebelum Anda menganggapnya tidak aman atau tidak. Ini mengevaluasi kode segera setelah fungsi dipanggil. Lihat juga bahayaeval .

ast.literal_eval memunculkan pengecualian jika input bukan tipe data Python yang valid, jadi kode tidak akan dieksekusi jika tidak.

Gunakan ast.literal_evalkapan pun Anda butuhkan eval. Anda seharusnya tidak biasanya mengevaluasi pernyataan Python literal.

Keriangan
sumber
20
Ini bukan saran yang benar 100% karena operator bitwise (atau operator kelebihan beban) akan gagal. Misalnya. ast.literal_eval("1 & 1")akan melempar kesalahan tetapi eval("1 & 1")tidak akan.
Daniel van Flymen
1
Hanya penasaran. Tidakkah seharusnya kita menggunakan pengurai ekspresi atau sesuatu jika kita mengharapkan sesuatu seperti "1 & 1"?
thelinuxer
@ Thelinuxer Anda masih harus, ya; Anda tidak akan dapat menggunakan ast.literal_evaluntuk hal seperti itu (misalnya Anda dapat mengimplementasikan parser secara manual).
Volatilitas
104

ast.literal_eval() hanya menganggap sebagian kecil sintaksis Python valid:

String atau node yang disediakan hanya dapat terdiri dari struktur literal Python berikut: string, angka, tuple, daftar, dicts, booleans, dan None.

Melewati __import__('os').system('rm -rf /a-path-you-really-care-about')menjadi ast.literal_eval()akan meningkatkan kesalahan, tapi eval()dengan senang hati akan menghapus drive Anda.

Karena sepertinya Anda hanya membiarkan pengguna memasukkan kamus biasa, gunakan ast.literal_eval(). Dengan aman melakukan apa yang Anda inginkan dan tidak lebih.

Blender
sumber
mendukung byte-string (byte kelas) juga. Misalnya. b'Hello World '
XChikuX
52

eval: Ini sangat kuat, tetapi juga sangat berbahaya jika Anda menerima string untuk dievaluasi dari input yang tidak dipercaya. Misalkan string yang dievaluasi adalah "os.system ('rm -rf /')"? Ini benar-benar akan mulai menghapus semua file di komputer Anda.

ast.literal_eval: Aman mengevaluasi simpul ekspresi atau string yang berisi tampilan literal Python atau wadah. String atau node yang disediakan hanya dapat terdiri dari struktur literal Python berikut: string, byte, angka, tupel, daftar, dicts, set, boolean, Tidak ada, byte dan set.

Sintaksis:

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

Contoh:

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

Dalam kode di atas ().__class__.__bases__[0]hanya objek itu sendiri. Sekarang kita instantiated semua subclass , di sini enter code heretujuan utama kita adalah menemukan satu kelas bernama n dari itu.

Kita perlu codeobjek dan functionobjek dari subclass instantiated. Ini adalah cara alternatif CPythonuntuk mengakses subclass objek dan melampirkan sistem.

Dari python 3.7 ast.literal_eval () sekarang lebih ketat. Penambahan dan pengurangan angka arbitrer tidak lagi diizinkan. tautan

Kiran Kumar Kotari
sumber
1
Saya menggunakan python 2.7 dan saya baru saja memeriksa berfungsi dengan baik di python 3.x. Sayangnya saya terus mencobanya di python 2.7
Mourya
3
ast.literal_eval("1+1")tidak bekerja di python 3.7 dan seperti yang dikatakan sebelumnya, literal_eval harus dibatasi pada literal dari beberapa struktur data tersebut. Seharusnya tidak dapat mengurai operasi biner.
Sesshu
Bisakah Anda menjelaskan KABOOMkode Anda ? Ditemukan di sini:KABOOM
winklerrr
3
@winklerrr KABOOMdijelaskan dengan baik di sini: nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
Elijas
41

Python sangat bersemangat dalam evaluasinya, sehingga eval(raw_input(...))akan mengevaluasi input pengguna segera setelah hits eval, terlepas dari apa yang Anda lakukan dengan data sesudahnya. Karena itu, ini tidak aman , terutama saat Anda evalinput pengguna.

Gunakan ast.literal_eval.


Sebagai contoh, memasukkan ini pada prompt akan sangat, sangat buruk bagi Anda:

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

Jika yang Anda butuhkan adalah kamus yang disediakan pengguna, kemungkinan solusi yang lebih baik adalah json.loads. Keterbatasan utama adalah bahwa json dicts memerlukan kunci string. Anda juga hanya dapat memberikan data literal, tetapi itu juga berlaku untuk literal_eval.

Chinasaur
sumber
1

Saya terjebak dengan ast.literal_eval(). Saya mencoba di IntelliJ IDEA debugger, dan terus kembali Nonepada output debugger.

Tapi kemudian ketika saya menetapkan outputnya ke variabel dan mencetaknya dalam kode. Itu bekerja dengan baik. Contoh kode berbagi:

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

Versi python-nya 3.6.

M Haziq
sumber