解析在对象文字/ JSON语法和差异中存储为字符串的函数

时间:2013-05-27 06:24:24

标签: javascript json eval

之前有一个问题(here),但是那里的答案并没有完全回答我的问题 - 接受的答案包含无效的JSON(强制使用eval()),这根本不可能据我所知,甚至可以做类似的事情。

我打算使用我自己的服务器中的代码作为对象文字语法存储在字符串中,但是我也希望能够在那里存储函数。

目前,我已经考虑过以下几种可能性:

  1. 只需使用eval()来解析字符串
  2. 以字符串形式放置函数(类似于"\bfunction"以便能够识别它们),在其上运行JSON.parse()然后使用for-in循环来查看是否需要任何此类函数解析(可能很慢)
  3. 使用DOM使用<script>标记运行代码,然后在那里运行
  4. 此代码不包含任何应该由用户编辑的内容,但是我不确定是否仍然存在安全问题或速度问题。使用eval()是否适合我的情况,除了手动解析函数还是使用eval()之外,还有更有效的方法吗?

    编辑:要解析的替代语法会更好还是会让事情变得更复杂?

    EDIT2:我只想做以下事情:

    { "test": function () {}
    , "foo": 1
    , "bar": 2 }
    

    希望从字符串中解析整个函数,例如

    eval('function(){}');
    

5 个答案:

答案 0 :(得分:2)

您基本上只有两个选项可以将功能代码带到客户端:

  • 使用JavaScript对象文字,其中包含函数表达式。无论您对<script>节点使用AJAX + eval还是JSONP-like方法,在性能方面都无关紧要。 AJAX虽然更灵活(HTTP方法,同步请求),但不需要全局回调函数。

    使用JS表达式将为您提供所有可能的自由,包括自定义数据类型(如Date对象或调用您自己的构造函数)甚至是循环结构(如果包含在IEFE中)。但是,将服务器上的此类数据序列化为脚本将更加困难,可能需要手工制作并且更容易出错(语法甚至运行时错误都会导致整个解析失败)。

  • 使用valid JSON,然后根据您知道的代码字符串创建函数。使用该标准化格式将简化服务器端序列化,并使客户无需JS解释器即可访问数据。这种方法很常见,大多数使用具有自定义原型的对象的人都习惯于这项任务。您只需使用Function constructor

    根据您的架构,您可以迭代/访问已解析的结构并重新定义相应的属性,或者使用reviver回调参数JSON.parse。一个例子:

    var json = '{"validators":[{"name":"required", "message":"must be filled", "fn":["str", "return str.trim().length > 0;"]}, {…}, …]}';
    var result = JSON.parse(json, function(k, v) {
        if (k != "fn") return v;
        return Function.apply(null, v);
    });
    

答案 1 :(得分:2)

有效性(他们会做他们应该做的事吗?)

所有1,2和3都可以使用

  1. 评估responseText:这样可以正常工作。如果您根据安全建议#3添加同源挑战,则必须先对其进行更正。

  2. 识别对象中的各个项目以“恢复”。我之前使用过这种方法,并且更喜欢使用键的命名约定来确定何时复活,而不是值中的标记。有关复活的说明,请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse

    在情况1和2中,您将不得不以某种方式导致函数文本被解析&amp;执行。这是你的选择。没有人比其他人更不邪恶(见安全部分)

    如果使用f = eval(responseText),这是“直接评估”。该函数将具有调用eval的局部范围。

    如果使用f =(1,eval)(responseText),则这是“间接eval”。该功能具有全局范围。见http://perfectionkills.com/global-eval-what-are-the-options/

    如果使用f = new Function(responseText),该函数将在全局范围内具有单独的局部范围。

  3. 此方法称为JSONP(对于带填充的JSON)。它可以工作,可能是最容易实现的。当响应可能包含敏感用户数据时也不好(见下文)。

  4. 安全性(他们不会做他们不应该做的事情吗?)

    如果传送的邮件在您的控制之下(100%确定没有用户/攻击者可以修改它): 所有选项1,2和3都不会危及用户的浏览器状态(即XSS,例如启用窃取cookie等内容)。

    如果传送的消息不是100%肯定在您的控制之下:所有方法1,2和3都会危及用户的浏览器状态并且不安全。您的选择是:

    • 接受安全风险(用户/攻击者可以随意更改您的网站功能,包括但不限于:窃取用户的域名Cookie,窃取用户有权访问您网域的任何信息,将您的用户引导到恶意软件感染的页面,以便可能安装病毒)

    • 将可能不安全的函数传递给webworker并在那里运行它们。它们将被沙箱化,不会影响浏览器窗口。但是,这是更多的工作,并非在所有浏览器中都可用。

    • 将可能不安全的函数传递到单独域上的iframe并在那里运行它们。这可以保护用户的cookie等。但是不能阻止攻击者将它们重定向到利用网站来安装病毒(尽管你可以希望你的用户拥有安全的浏览器: - /)

    • 使用您可以控制的白名单功能,只需传递白名单功能的名称(或带有一些灵活参数的基本列入白名单的功能的工厂功能)

    如果您在数据中传递了特定于用户的信息:

    • 3对于传递敏感用户数据是不安全的(可以使用来自任何域的用户cookie轻松调用脚本),除非您的服务器在返回之前对请求的referer / origin HTTP头执行检查响应。即使这样,它也可能是不安全的。请参阅http://en.wikipedia.org/wiki/Cross-site_request_forgery

    • 选项1,2的天真实现也以这种方式不安全(即XSRF。例如,恶意网站可以代表用户使用其cookie来伪造数据请求然后用它做自己喜欢的事情)。第三方域可以重载内置的Array构造函数,然后插入指向JSON文件的脚本标记。浏览器将使用用户的cookie请求此文件,将用户的JSON返回给第三方站点,javascript引擎将“运行”JSON,这只是一个语句,但由于Array构造函数已经过载,因此当javascript尝试在单个JSON“语句”中构造值时,可以对数据执行任何操作

    • 对于1和2是XSRF安全的,如果解释为脚本(例如无效语法或无限循环),则应该放置一个会导致它中断的语句,然后您的脚本应该接收文本并修改它删除错误。这是因为同域脚本有权在解析请求之前读取请求的responseText,而跨域脚本只能通过插入脚本标记来作为源来访问此信息。

答案 2 :(得分:1)

有很多方法可以从字符串创建JS函数,但eval evil ,应尽可能避免使用。

您可以使用不同的方式创建函数,例如使用new Function("...")语句,或者使用该名称在范围中创建函数,然后使用参数作为字符串调用它。我做了一个带测试的JSFiddle。最快的方法是eval,但正如我之前所说,应该避免。 DOM和new Function()方式同样快,在1000次迭代中它们有几毫秒的差异(fn:3328,DOM:3371) 在这里你有JSFiddle,做一些测试并得出你自己的结论。

evalnew Function之间的主要区别在于前者可以访问局部变量,而后者则不能。请参阅此answer

答案 3 :(得分:0)

你真的没有多少选择,但还有第四种选择(JQuery使用这种方法):

var yourFunction = new Function("var a=1; return a; //this is your function body");

答案 4 :(得分:0)

根据您的具体情况,有几点需要考虑。

我认为经验法则应该是,如果你不确定是否使用eval,你可能不应该使用它。我还认为有些人会对每秒操作可以忽略不计的性能表现出色。你的应用程序是一个巨大的应用程序,依赖于挤压每一盎司的性能? 100,000ops / sec对1,000,000ops / sec是否是您的应用程序可用之间的差异?此外,您还必须考虑支持,因为所有浏览器本身都不包含JSON支持。

然而,我的第一个念头是:

假设您需要通过JSON发送这些所述功能(这对我来说听起来有些奇怪)并且您可以控制服务器发送给您的内容,那么将使用类似{{1}的前缀标记您的函数}。如果他们需要在收到后立即执行,您可以使用substr删除js-并使用括号表示法执行它:

js-

如果您没有立即尝试执行它们,可以将它们添加到数组/对象文字中以便稍后调用。