Pythonのeval()と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

私はドキュメントを読みましたが、これが安全かどうかはまだわかりません。入力された直後、またはdatamap変数が呼び出された直後にevalはデータを評価しますか?

あるastモジュールの.literal_eval()唯一の安全なオプションは?

回答:


189

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")ません。
ダニエルヴァンフライメン2017年

1
ちょっと興味があるんだけど。「1&1」のようなものが必要な場合は、式パーサーなどを使用するべきではありませんか?
thelinuxer

@thelinuxerあなたはまだすべきです、はい; あなたはそのast.literal_evalような何かのために使うことができないでしょう(例えば、あなたは手動でパーサーを実装することができました)。
ボラティリティ2018年

104

ast.literal_eval() Pythonの構文の小さなサブセットのみが有効であると見なします。

提供された文字列またはノードは、文字列、数値、タプル、リスト、dicts、ブール値、およびNoneのPythonリテラル構造のみで構成されます。

にパス__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リテラル構造のみで構成されます:文字列、バイト、数値、タプル、リスト、辞書、セット、ブール、なし、バイトおよびセット。

構文:

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という名前の1つのクラスを見つけることです。

インスタンス化されたサブクラスのcodeオブジェクトとfunctionオブジェクトを作成する必要があります。これは、からCPythonオブジェクトのサブクラスにアクセスしてシステムをアタッチするための代替方法です。

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はこれらのいくつかのデータ構造のリテラルに制限する必要があります。バイナリ演算を解析できないようにする必要があります。
雪舟

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)

そのpythonバージョン3.6。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.