安全でないコードが誤って使用されないようにする


10

関数f()は、プログラムを実行eval()local_fileているマシンに作成して保存したデータを使用します(または危険なものです)。

import local_file

def f(str_to_eval):
    # code....
    # .... 
    eval(str_to_eval)    
    # ....
    # ....    
    return None


a = f(local_file.some_str)

f() 私が提供する文字列は自分のものなので、実行しても安全です。

しかし、私がこれを安全ではないもの(ユーザー入力など)に使用することを決定した場合、事態はひどく間違ったものになる可能性があります。また、local_file停止がローカルになると、そのファイルを提供するマシンも信頼する必要があるため、脆弱性が発生します。

(特定の基準が満たされない限り)この関数の使用が安全でないことを「忘れない」ようにするにはどうすればよいですか?

注:eval()危険であり、通常は安全なものに置き換えることができます。


1
私は通常、「あなたはこれを行うべきではない」と言うよりは、尋ねられる質問に沿って進みたいのですが、この場合は例外とします。evalを使用する必要がある理由はないと私は確信しています。これらの関数は、PHPやPythonなどの言語に、インタプリタ言語でどれだけのことができるかを示す概念実証として含まれています。コードを作成するコードを書くことは基本的に考えられませんが、同じロジックを関数にコーディングすることはできません。Pythonには、コールバックと関数バインディングがあり、それを使用して必要なことを実行できます
user1122069

1
そのファイルを提供するマシンも信頼する必要があります」-コードを実行しているマシンを常に信頼する必要があります。
Bergi

「この文字列は評価されます。悪意のあるコードはここに置かないでください」というコメントをファイルに書き込みますか?
user253751 2016年

@immibisコメントだけでは不十分です。もちろん、私は巨大な「警告:この関数は関数のドキュメントでeval()を使用します」ですが、私はそれよりもはるかに安全なものに依存したいと思います。たとえば、(限られた時間のため)関数のドキュメントを常に再読み込みするとは限りません。私もそうあるべきだとは思いません。マーフィーが彼の回答でリンクした方法のように、安全でないものを知るためのより高速な(つまり、より効率的な)方法があります。
フェルミパラドックス

@Fermiparadoxは、evalを質問から除外すると、有用な例がなくなります。SQLパラメーターをエスケープしないなど、故意の見落としのために安全でない関数について話しているのですか。テストコードは運用環境にとって安全ではないということですか?とにかく、今私はアモンの答えについてのあなたのコメントを読みました-基本的にあなたはあなたの機能を保護する方法を探していました。
user1122069 2016年

回答:


26

「安全な」入力を装飾するタイプを定義します。関数f()で、引数がこの型であることをアサートするか、エラーをスローします。ショートカットを定義しないように十分に賢くしてくださいdef g(x): return f(SafeInput(x))

例:

class SafeInput(object):
  def __init__(self, value):
    self._value = value

  def value(self):
    return self._value

def f(safe_string_to_eval):
  assert type(safe_string_to_eval) is SafeInput, "safe_string_to_eval must have type SafeInput, but was {}".format(type(safe_string_to_eval))
  ...
  eval(safe_string_to_eval.value())
  ...

a = f(SafeInput(local_file.some_str))

これは簡単に回避できますが、誤って回避することは困難になります。人々はそのような過酷なタイプチェックを批判するかもしれませんが、彼らは要点を見逃すでしょう。以来SafeInput種類だけで、データ型ではなくて型アイデンティティをチェック、サブクラス化することができ、オブジェクト指向のクラスであるtype(x) is SafeInputとの型の互換性をチェックするよりも安全ですisinstance(x, SafeInput)。特定のタイプを強制したいので、明示的なタイプを無視して暗黙のダックタイプを実行するだけでも、ここでは満足できません。Pythonには型システムがあるので、それを使用して起こり得る間違いをキャッチしましょう!


5
ただし、小さな変更が必要になる場合があります。assert最適化されたpythonコードを使用するため、自動的に削除されます。のようなものif ... : raise NotSafeInputErrorが必要になります。
フェルミパラドックス

4
とにかくこの道を進むなら、明示的な型チェックを必要としないように、をのメソッドに移動することを強くお勧めします。メソッドに、他のクラスで使用されていない名前を付けます。そうすれば、テスト目的ですべてをモックアウトできます。eval()SafeInput
Kevin

@Kevin:evalローカル変数へのアクセス権があるため、コードが変数をどのように変更するかに依存している可能性があります。evalを別のメソッドに移動すると、ローカル変数を変更する機能がなくなります。
Sjoerd Job Postmus

さらにSafeInput、コンストラクターにローカルファイルの名前/ハンドルを渡して読み取りを行わせ、データがローカルファイルからのものであることを確認することもできます。
オーウェン

1
私はpythonの経験があまりありませんが、例外をスローした方がいいですか?ほとんどの言語では、アサートをオフにすることができ、(私が理解しているように)安全のためではなくデバッグのためです。例外とコンソールへの終了により、次のコードが実行されないことが保証されます。
user1122069

11

Joelが指摘したように、間違ったコードを間違って見えるようにします(「実際の解決策」までスクロールするか、完全に読んでください。関数名ではなく変数をアドレス指定する場合でも、その価値はあります)。だから私は謙虚にあなたの関数を次のような名前に変更することを提案しますf_unsafe_for_external_use()-あなたはおそらく自分でいくつかの省略形を思い付くでしょう。

編集:私はアモンの提案は同じテーマの非常に良い、OOベースのバリエーションだと思います:-)


0

@amonの提案を補足するために、ここでは戦略パターンとメソッドオブジェクトパターンの組み合わせを検討することもできます。

class AbstractFoobarizer(object):
    def handle_cond(self, foo, bar):
        """
        Handle the event or something.
        """
        raise NotImplementedError

    def run(self):
        # do some magic
        pass

そして、「通常の」実装

class PrintingFoobarizer(AbstractFoobarizer):
    def handle_cond(self, foo, bar):
        print("Something went very well regarding {} and {}".format(foo, bar))

そして、admin-input-eval実装

class AdminControllableFoobarizer(AbstractFoobarizer):
    def handle_cond(self, foo, bar):
        # Look up code in database/config file/...
        code = get_code_from_settings()
        eval(code)

(注:実際のように見える例では、より良い例を示すことができるかもしれません)。

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