Python's eval () ile ast.literal_eval () mi kullanıyorsunuz?


176

Bazı kod ile eval()olası bir çözüm olarak geldi bir durum var . Şimdi daha eval()önce hiç kullanmak zorunda kalmadım, ancak neden olabileceği potansiyel tehlike hakkında bol miktarda bilgiye rastladım. Bununla birlikte, kullanmak konusunda çok temkinli olduğumu söyledi.

Benim durumum bir kullanıcı tarafından verilen girdi olması:

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

Nerede datamapbir sözlük olması gerekir. Etrafı araştırdım ve bunun eval()işe yarayabileceğini buldum . Verileri kullanmaya çalışmadan önce girişin türünü kontrol edebileceğimi ve bunun uygun bir güvenlik önlemi olabileceğini düşündüm.

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

Belgeleri okudum ve bunun güvenli olup olmayacağı hala belirsiz. Eval, verileri girilir girilmez veya datamapdeğişken çağrıldıktan sonra değerlendirir mi?

astmodülün .literal_eval()tek güvenli seçenek?

Yanıtlar:


190

datamap = eval(raw_input('Provide some data here: '))kodu güvenli olmadığını düşünmeden önce değerlendirdiğiniz anlamına gelir . İşlev çağrıldığında kodu değerlendirir. Ayrıca bkz tehlikelerieval .

ast.literal_eval giriş geçerli bir Python veri türü değilse bir istisna oluşturur, bu nedenle kod çalıştırılmazsa yürütülmez.

ast.literal_evalİhtiyacınız olduğunda kullanın eval. Genellikle değişmez Python deyimlerini değerlendirmemelisiniz.


20
Herhangi bir bitsel operatör (veya aşırı yüklenmiş operatör) başarısız olacağından bu% 100 doğru tavsiye değildir. Örneğin. ast.literal_eval("1 & 1")bir hata verir ama göndermez eval("1 & 1").
Daniel van Flymen

1
Sadece merak. "1 & 1" gibi bir şey bekliyorsak, ifade ayrıştırıcıları kullanmamalı mıyız?
thelinuxer

@ thelinuxer hala yapmalısınız, evet; böyle bir ast.literal_evalşey için kullanamazsınız (örn. elle bir ayrıştırıcı uygulayabilirsiniz).
Volatilite

104

ast.literal_eval() yalnızca Python'un sözdiziminin küçük bir alt kümesinin geçerli olduğunu düşünür:

Sağlanan dize veya düğüm yalnızca şu Python değişmez yapılarından oluşabilir: dizeler, sayılar, tuples, listeler, dikteler, booleans ve Yok.

Geçme __import__('os').system('rm -rf /a-path-you-really-care-about')içine ast.literal_eval()bir hata döndürür, ancak eval()mutlu sürücünüzü silecek.

Kullanıcının yalnızca düz bir sözlük girmesine izin veriyorsunuz gibi göründüğünden, kullanın ast.literal_eval(). İstediğinizi güvenle yapar ve daha fazlasını yapmaz.


bayt dizelerini (sınıf bayt) da destekler. Örneğin. b'Hello World '
XChikuX

52

eval: Bu çok güçlü, ancak güvenilmeyen girdilerden değerlendirmek için dizeleri kabul ederseniz çok tehlikelidir. Değerlendirilmekte olan dizenin "os.system ('rm -rf /')" olduğunu varsayalım? Bilgisayarınızdaki tüm dosyaları silmeye gerçekten başlayacaktır.

ast.literal_eval: Bir ifade düğümünü veya Python değişmezi veya kapsayıcı görüntüsü içeren bir dizeyi güvenle değerlendirin. Sağlanan dize veya düğüm yalnızca şu Python değişmez yapılarından oluşabilir: dizeler, baytlar, sayılar, tuples, listeler, dikteler, kümeler, booleans, Yok, baytlar ve kümeler.

Sözdizimi:

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

Misal:

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

Yukarıdaki kodda ().__class__.__bases__[0]nesnenin kendisinden başka bir şey yoktur. Şimdi tüm alt sınıfları başlattık , burada asıl enter code hereamacımız n adında bir sınıf bulmak .

Örneklenmiş alt sınıflara itiraz etmeli codeve functionitiraz etmeliyiz . Bu, CPythonnesnenin alt sınıflarına erişmenin ve sistemi eklemenin alternatif bir yoludur .

Python 3.7'den ast.literal_eval () artık daha katı. Artık rasgele sayıların toplanmasına ve çıkarılmasına izin verilmemektedir. bağlantı


1
python 2.7 kullanıyorum ve ben sadece python 3.x üzerinde iyi çalışıyor kontrol. Benim kötü ben python 2.7 üzerinde çalışırken tuttu
Mourya

3
ast.literal_eval("1+1")python 3.7'de çalışmaz ve daha önce de belirtildiği gibi, literal_eval bu birkaç veri yapısının değişmezleriyle sınırlandırılmalıdır. İkili bir işlemi ayrıştıramaz.
Sesshu

KABOOMKodunuzu açıklayabilir misiniz lütfen? Burada KABOOM
buldum

3
@winklerrr KABOOMburada güzel açıklanmıştır: nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
Elijas

41

Python değerlendirmesinde heveslidir , bu nedenle eval(raw_input(...))daha evalsonra verilerle ne yaptığınızdan bağımsız olarak kullanıcının girdisini vurur vurmaz değerlendirir. Bu nedenle, özellikle kullanıcı girişi yaptığınızda , bu güvenli değildireval .

Kullanın ast.literal_eval.


Örnek olarak, bunu bilgi istemine girmek sizin için çok, çok kötü olacaktır:

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

3

İhtiyacınız olan tek şey kullanıcı tarafından sağlanan bir sözlükse, olası daha iyi bir çözümdür json.loads. Temel sınırlama json dicts dize anahtarları gerektirir olmasıdır. Ayrıca yalnızca değişmez veri sağlayabilirsiniz, ancak durum böyledir literal_eval.


1

Ben sıkışmıştım ast.literal_eval(). IntelliJ IDEA hata ayıklayıcıda deniyordum ve Nonehata ayıklayıcı çıktısını döndürmeye devam etti.

Ama daha sonra çıktısını bir değişkene atadığımda ve kodda yazdırdığımda. İyi çalıştı. Paylaşım kodu örneği:

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

Python sürüm 3.6.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.