13

元の質問:

Python Web サーバーで数学的なユーザー コードを実行する場合、最も簡単で安全な方法は何ですか?

  • ユーザーが送信したコードを Python Web サーバーで実行できるようにしたいと考えています。コードは、本質的に単純で数学的なものになります。

Python のこのような小さなサブセットが必要なため、私の現在のアプローチは、Python の抽象構文ツリーをたどることによって、許可される構文をホワイトリストに登録することです。関数と名前は特別扱いされます。明示的にホワイトリストに登録された関数のみが許可され、未使用の名前のみが許可されます。

import ast

allowed_functions = set([
    #math library
    'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh',
    'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf',
    'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod',
    'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp',
    'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians',
    'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc',
    #builtins
    'abs', 'max', 'min', 'range', 'xrange'
    ])

allowed_node_types = set([
    #Meta
    'Module', 'Assign', 'Expr',
    #Control
    'For', 'If', 'Else',
    #Data
    'Store', 'Load', 'AugAssign', 'Subscript',
    #Datatypes
    'Num', 'Tuple', 'List',
    #Operations
    'BinOp', 'Add', 'Sub', 'Mult', 'Div', 'Mod', 'Compare'
    ])

safe_names = set([
    'True', 'False', 'None'
    ])


class SyntaxChecker(ast.NodeVisitor):

    def check(self, syntax):
        tree = ast.parse(syntax)
        self.visit(tree)

    def visit_Call(self, node):
        if node.func.id not in allowed_functions:
            raise SyntaxError("%s is not an allowed function!"%node.func.id)
        else:
            ast.NodeVisitor.generic_visit(self, node)

    def visit_Name(self, node):
        try:
            eval(node.id)
        except NameError:
            ast.NodeVisitor.generic_visit(self, node)
        else:
            if node.id not in safe_names and node.id not in allowed_functions:
                raise SyntaxError("%s is a reserved name!"%node.id)
            else:
                ast.NodeVisitor.generic_visit(self, node)

    def generic_visit(self, node):
        if type(node).__name__ not in allowed_node_types:
            raise SyntaxError("%s is not allowed!"%type(node).__name__)
        else:
            ast.NodeVisitor.generic_visit(self, node)

if __name__ == '__main__':
    x = SyntaxChecker()
    while True:
        try:
            x.check(raw_input())
        except Exception as e:
            print e

これは必要な構文を受け入れているように見えますが、私はプログラミングにかなり慣れていないため、ギャップのあるセキュリティホールがいくつも欠けている可能性があります.

だから私の質問は次のとおりです。これは安全ですか、より良いアプローチはありますか、他に取るべき予防策はありますか?

4

3 に答える 3

7

あなたがまだ改善できることに気づいた2つのポイント:

なんらかの形式のユーザー入力から生成できる出力は、常にエスケープする必要があります。あなたの例では、許可されていない識別子がミラーリングされ、変更されずに出力に戻されます。これは潜在的に悪用される可能性があり、その一例がクロス サイト スクリプティングです。したがって、これを防ぐために、そのようなエラーメッセージをさらにエスケープします。

もう 1 つ注意する必要があるのは、サービス拒否攻撃です。誰かが Ackermann 関数とスクリプトを作成して、サーバーに数千回送信することを想像してみてください...これを防ぐには、送信されるコードの実行時間をタイムボックス化する必要があります。このタイプの「攻撃」はしばしば意図せずに発生するため、これは不可欠です - 誰かが無限ループを作り出すことができました.

編集:

最後に、 「hashDoS」攻撃を防ぐために Python のバージョンを更新することもお勧めします。

于 2012-05-19T00:23:18.080 に答える
3

pypy のサンドボックス機能を見たことがありますか? これは、CPython サンドボックス化の取り組みよりもはるかに安全であると言われています。サービス拒否を防ぐために、ヒープ サイズと CPU 実行時間を制限することもできます。

于 2012-05-20T03:05:01.307 に答える
3

Openerp のソース コードには、同様のことを行うsafe_eval.pyが含まれています。ただし、ソースのastをチェックする代わりに、実行できるバイトコードを制限します。あなたもそれを見ているかもしれないと思います:)

于 2012-05-20T05:37:49.557 に答える