java - 用户如何(安全地)用 Java 编写自己的过滤器

标签 java dynamic groovy rhino

我希望我的用户在用 Java 请求列表时能够编写自己的过滤器。

选项 1) 我正在考虑将 JavaScript 与 Rhino 结合使用。
我将用户的过滤器作为 JavaScript 字符串获取。然后在此脚本中调用 isAccepted(myItem)
根据回复我是否接受该元素。

选项 2) 我正在考虑 Groovy。
我的用户可以在文本字段中编写 Groovy 脚本。当我的用户使用此过滤器进行搜索时,Groovy 脚本会用 Java 进行编译(如果第一次调用)并调用 Java 方法 isAccepted() 根据回复我是否接受该元素。

我的应用程序很大程度上依赖于这种功能,并且它将在我的服务器上被集中调用。
所以我相信速度是关键。

选项 1 思考: 如果我错了,请纠正我,但我认为就我而言,Groovy 的主要优点是速度,但我的用户可以在我的服务器上编译和运行不需要的代码...(有任何解决方法吗?)

选项 2 思考: 我认为在大多数人看来 JavaScript 更像是一个玩具。即使这根本不是我的想法,但对于我的客户来说可能不会那么信任它。你也这么认为吗?
根据我在网上的阅读,我预计的另一个缺点是速度。
我的用户再次可以 access Java并在我的服务器上运行不需要的代码...(有解决方法吗?)

更多信息: 我正在 Google App Engine 上运行我的应用程序,作为我的应用程序的主要网络服务。
过滤器将通过调用应用 20 次。
过滤器(大多数时候)很简单。

有什么想法可以让这个过滤器对我的服务器安全吗?
还有其他方法可以使其发挥作用吗?

最佳答案

我的想法:

  • 在编译脚本时,您必须使用自己的类加载器,以避免从脚本访问任何其他类。不确定这在 GAE 中是否可行。
  • 您必须使用 Java 的 SecurityManager 功能来避免脚本能够访问文件系统、网络等。不确定这在 GAE 中是否可行。

仅看上面的两项,对我来说它看起来非常复杂和脆弱。如果您找不到现有的沙盒功能作为现有项目,则应该远离它。

设计一种允许您认为合法的表达式的领域特定语言要安全得多,并且查看上述项目,无论如何您都必须非常努力地思考您想要允许的内容。从那里到设计语言并不是一个很大的步骤。

小心不要用常规闭包(内部 DSL)来实现 DSL,因为那只是常规而且你也很容易被黑客攻击。您需要定义一种外部语言并解析它。我推荐解析器组合器 jparsec 来定义语法。在这种情况下不需要编译器。

http://jparsec.codehaus.org/

仅供引用,这是我用 jparsec (groovy 代码)编写的一个小解析器:

    //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())
    }

关于java - 用户如何(安全地)用 Java 编写自己的过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10162251/

相关文章:

Groovy 中的 Vim 语法高亮错误

unit-testing - 使用 Groovy MetaClass 覆盖方法

java - 如何将泛型类型转换为String

java - 检查线是否以点开头,然后将其删除

c# - 在c# asp.net中动态分配按钮事件

arrays - 带有动态数组的 DELPHI 类在 SetLength() 中有问题

使用 MultipartConfig 的 Java Servlet

java - 保存游戏(Android 应用程序)

javascript - 如何在 PHP 中正确解码 javascript 编码的 URI?

elasticsearch - 如何将脚本化度量标准中的Elasticsearch字段的类型从Long更改为Double?