2

Java でリストを要求するときに、ユーザーが独自のフィルターを記述できるようにしたいと考えています。

オプション 1) Rhino で JavaScript を考えています。
ユーザーのフィルターを JavaScript 文字列として取得します。そしてisAccepted(myItem)、このスクリプトを呼び出します。
返信に応じて、要素を受け入れるかどうかを決定します。

オプション 2) Groovy について考えています。
私のユーザーはテキストフィールドに Groovy スクリプトを書くことができます。ユーザーがこのフィルターで検索すると、Groovy スクリプトが Java でコンパイルされ (最初の呼び出しの場合)、Java メソッドが呼び出さisAccepted() れます。応答に応じて、要素を受け入れるかどうかを決定します。

私のアプリケーションはこの機能に大きく依存しており、サーバー上で集中的に呼び出されます。
だから私はスピードが鍵だと信じています。

オプション 1 の考え方: 間違っている場合は訂正してください。ただし、私の場合、Groovy の主な利点は速度ですが、ユーザーはサーバー上で不要なコードをコンパイルして実行できると思います... (回避策はありますか?)

オプション 2 の考え方: ほとんどの人は、JavaScript はおもちゃのようなものだと思っていると思います。たとえそれが私の考えではなくても、おそらくそれは私の顧客にとってそれほど信頼されないでしょう. あなたはそう思いますか?
私が期待するもう1つの悪い点は、Webでの読書から、速度です。
また、ユーザーがJava にアクセスし、サーバー上で不要なコードを実行する可能性があります... (回避策はありますか?)

詳細: アプリのメイン Web サービスとして、Google App Engine でアプリケーションを実行しています。
フィルターは呼び出しで 20 回適用されます。
フィルターは (ほとんどの場合) シンプルです。

このフィルターをサーバーにとって安全にするためのアイデアはありますか?
それを機能させるための他のアプローチはありますか?

4

3 に答える 3

1

いくつかの考え:

  • JavaScriptとGroovyのどちらを使用する場合でも、スクリプトに提供するコンテキストで実行されるため、スクリプトは、スクリプトにアクセスしたくないものにアクセスできないようにする必要があります(ただし、もちろん、次のように広範囲にテストする必要があります。このルートに行くかどうかを確認してください)。

  • 可能であれば、実行可能コードとしてではなく、データとしてフィルター式を指定する方が安全でしょう。もちろん、これはフィルター式がどれほど複雑かによって異なります。おそらく、表現をフィールド、コンパレータ、値などに分割して、データとして扱い、通常の方法で評価できるようにすることができますか?

  • ユーザーがスクリプト言語を介して何を挿入できるかについて心配している場合は、JavaScriptを使用した方がおそらく安全です。パフォーマンスが問題になることはないと思いますが、繰り返しになりますが、確実に徹底的なテストを行うことをお勧めします。

于 2012-04-15T15:22:24.413 に答える
1

私の考え:

  • スクリプトをコンパイルするときは、スクリプトから他のクラスにアクセスできないようにするために、独自のクラスローダーを使用する必要があります。それがGAEで可能かどうかはわかりません。
  • スクリプトがファイル システムやネットワークなどにアクセスできないようにするには、Java の SecurityManager 機能を使用する必要があります。GAE でそれが可能かどうかはわかりません。

上記の 2 つの項目だけを見ると、信じられないほど複雑で脆弱に見えます。既存のプロジェクトとして既存のサンドボックス機能が見つからない場合は、それを避ける必要があります。

正当であると判断した表現を許可するドメイン固有言語を設計することは、はるかに安全であり、上記の項目を見て、とにかく許可したいことを非常によく考える必要があります。そこから言語の設計までは大きなステップではありません。

Groovy クロージャー (内部 DSL) を使用して DSL を実装しないように注意してください。外部言語を定義して解析する必要があります。パーサー コンビネーター jparsec を使用して文法を定義することをお勧めします。その場合、コンパイラコンパイラは必要ありません。

http://jparsec.codehaus.org/

参考までに、ここに私が jparsec (グルーヴィーなコード) で書いた小さなパーサーがあります:

    //import some static methods, this will allow more concise code
    import static org.codehaus.jparsec.Parsers.*
    import static org.codehaus.jparsec.Terminals.*
    import static org.codehaus.jparsec.Scanners.*

    import org.codehaus.jparsec.functors.Map as FMap
    import org.codehaus.jparsec.functors.Map4 as FMap4
    import org.codehaus.jparsec.functors.Map3 as FMap3
    import org.codehaus.jparsec.functors.Map2 as FMap2

    /**
     * Uses jparsec combinator parser library to construct an external DSL parser for the following grammar:
     * <pre>
     *     pipeline := routingStep*
     *     routingStep := IDENTIFIER '(' parameters? ')'
     *     parameters := parameter (',' parameter)*
     *     parameter := (IDENTIFIER | QUOTED_STRING)  ':' QUOTED_STRING
     * </pre>
     */
    class PipelineParser {
        //=======================================================
        //Pass 1: Define which terminals are part of the grammar
        //=======================================================
        //operators
        private static def OPERATORS = operators(',', '(', ')', ':')
        private static def LPAREN = OPERATORS.token('(')
        private static def RPAREN = OPERATORS.token(')')
        private static def COLON = OPERATORS.token(':')
        private static def COMMA = OPERATORS.token(',')

        //identifiers tokenizer
        private static def IDENTIFIER = Identifier.TOKENIZER
        //single quoted strings tokenizer
        private static def SINGLE_QUOTED_STRING = StringLiteral.SINGLE_QUOTE_TOKENIZER

        //=======================================================
        //Pass 2: Define the syntax of the grammar
        //=======================================================
        //PRODUCTION RULE: parameter := (IDENTIFIER | QUOTED_STRING) ':' QUOTED_STRING
        @SuppressWarnings("GroovyAssignabilityCheck")
        private static def parameter = sequence(or(Identifier.PARSER,StringLiteral.PARSER), COLON, StringLiteral.PARSER, new FMap3() {
            def map(paramName, colon, paramValue) {
                new Parameter(name: paramName, value: paramValue)
            }
        })

        //PRODUCTION RULE: parameters := parameter (',' parameter)*
        @SuppressWarnings("GroovyAssignabilityCheck")
        private static def parameters = sequence(parameter, sequence(COMMA, parameter).many(), new FMap2() {
            def map(parameter1, otherParameters) {
                if (otherParameters != null) {
                    [parameter1, otherParameters].flatten()
                } else {
                    [parameter1]
                }
            }
        })

        //PRODUCTION RULE: routingStep := IDENTIFIER '(' parameters? ')'
        @SuppressWarnings("GroovyAssignabilityCheck")
        private static def routingStep = sequence(Identifier.PARSER, LPAREN, parameters.optional(), RPAREN, new FMap4() {
            def map(routingStepName, lParen, parameters, rParen) {
                new RoutingStep(
                    name: routingStepName,
                    parameters: parameters ?: []
                )
            }
        })

        //PRODUCTION RULE: pipeline := routingStep*
        @SuppressWarnings("GroovyAssignabilityCheck")
        private static def pipeline = routingStep.many().map(new FMap() {
            def map(from) {
                new Pipeline(
                    routingSteps: from
                )
            }
        })

        //Combine the above tokenizers to create the tokenizer that will parse the stream and spit out the tokens of the grammar
        private static def tokenizer = or(OPERATORS.tokenizer(), SINGLE_QUOTED_STRING, IDENTIFIER)

        //This parser will be used to define which input sequences need to be ignored
        private static def ignored = or(JAVA_LINE_COMMENT, JAVA_BLOCK_COMMENT, WHITESPACES)

        /**
         * Parser that is used to parse extender pipelines.
         * <pre>
         *     def parser=PipelineParser.parser
         *     Pipeline pipeline=parser.parse(pipelineStr)
         * </pre>
         * Returns an instance of {@link Pipeline} containing the AST representation of the parsed string.
         */
        //Create a syntactic pipeline parser that will use the given tokenizer to parse the input into tokens, and will ignore sequences that are matched by the given parser.
        static def parser = pipeline.from(tokenizer, ignored.skipMany())
    }
于 2012-04-15T22:20:59.853 に答える
0

ユーザーに任意のコードを入力させることは決してありません。もろく、安全ではなく、ユーザー エクスペリエンスも悪くなります。ユーザーについて何も知らないので、質問に答えるのに多くの時間を費やすことになると思います. ほとんどのフィルターが単純な場合は、代わりに小さなフィルタービルダーを作成してみませんか?

groovy と JavaScript に関しては、groovy の方が理解しやすく、スクリプト作成に適していると思いますが、それは私の意見です。

于 2012-04-19T05:08:32.090 に答える