2

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

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

目前,我想到了以下几种可能性:

  1. 简单地eval()用来解析字符串
  2. 以字符串形式放置函数(类似于"\bfunction"能够识别它们的东西),JSON.parse()在其上运行,然后使用 for-in 循环查看是否需要解析任何此类函数(可能很慢)
  3. 使用 DOM 使用<script>标签运行代码并在那里运行

此代码不会包含任何应该是用户可编辑的内容,但是我不确定是否仍然存在安全问题或只是速度问题。使用eval()是否适合我的情况,有没有比手动解析函数或使用更有效的方法eval()

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

EDIT2:我只是想做以下事情:

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

我不想从字符串中解析整个函数,例如

eval('function(){}');
4

5 回答 5

2

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

1、2和3都可以

  1. 评估 responseText:这将正常工作。如果您根据安全建议 #3 添加同源挑战,则必须先更正它。

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

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

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

    如果您使用 f=(1,eval)(responseText),这是一个“间接评估”。该函数将具有全局范围。请参阅http://perfectkills.com/global-eval-what-are-the-options/

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

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

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

如果传递的消息在您的控制之下(100% 确保没有用户/攻击者可以修改它): 所有选项 1、2 和 3 都不会损害用户的浏览器状态(即 XSS,例如启用窃取他们的 cookie 之类的事情)。

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

  • 接受安全风险(用户/攻击者将能够任意更改您的站点功能,包括但不限于:窃取您用户的域 cookie、窃取用户在您的域上可以访问的任何信息、将您的用户引导至恶意软件-被感染的页面可能会安装病毒)

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

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

  • 使用您可以控制的白名单函数,只需传递白名单函数的名称(或本质上是白名单函数的工厂函数,带有一些灵活的参数)

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

  • 3 对于传递敏感的用户数据是不安全的(脚本可以很容易地使用来自任何域的用户 cookie 调用),除非您的服务器在返回响应之前对请求的引用/源 HTTP 标头实施检查。即使那样,它也可能是不安全的。见http://en.wikipedia.org/wiki/Cross-site_request_forgery

  • 选项 1、2的幼稚实现也以这种方式不安全(即 XSRF。例如,恶意站点可以使用他们的 cookie 代表用户伪造对数据的请求,然后用它做他们喜欢的事情)。第三方域可以重载内置的 Array 构造函数,然后插入指向 JSON 文件的脚本标记。浏览器会用用户的cookies请求这个文件,将用户的JSON返回给第三方站点,javascript引擎会“运行”这个JSON,这只是一条语句,但是因为Array构造函数已经被重载,所以可以对数据做任何想做的事情,因为 javascript 会尝试在单个 JSON“语句”中构造值

  • 为了使 1 和 2 是 XSRF 安全的,您应该放置一个语句,如果将其解释为脚本(例如无效语法或无限循环)会导致其中断,那么您的脚本应该接收文本并对其进行修改以消除错误。这是因为同域脚本在解析请求之前可以读取请求的 responseText,而跨域脚本只能通过插入以 this 作为源的脚本标签来访问此信息。

于 2013-09-18T22:13:18.547 回答
2

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

  • 使用 JavaScript 对象文字,其中包含函数表达式。无论您是使用 AJAX +eval还是对节点使用类似 JSONP 的方法,<script>在性能方面并不重要。AJAX 将更加灵活(HTTP 方法、同步请求)并且不需要全局回调函数。

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

  • 使用有效的 JSON,然后根据您知道的代码字符串创建函数。使用这种标准化格式将简化服务器端的序列化,并使您的数据可以在没有 JS 解释器的情况下被客户端访问。这种方法很常见,大多数使用带有自定义原型的对象的人都很好地习惯了这项任务。您只需使用Function构造函数即可。

    根据您的架构,您可以迭代/访问解析的结构并重新定义适当的属性,或者使用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);
    });
    
于 2013-09-17T03:44:45.733 回答
1

有很多方法可以从字符串创建 JS 函数,但eval都是邪恶的,应该尽可能避免。

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

eval和之间的主要区别在于new Function前者可以访问局部变量,而后者不能。看到这个答案

于 2013-05-27T07:10:18.400 回答
0

针对您的具体情况,需要考虑一些事项。

我认为经验法则应该是,如果您不确定是否使用eval,您可能不应该使用它。我还认为有些人痴迷于每秒操作数实际上可以忽略不计的性能。您的应用程序是一个依赖于挤出每一盎司性能的大型应用程序吗?100,000ops/sec 与 1,000,000ops/sec 是否会成为您的应用程序是否可用的区别?此外,您还必须考虑支持,因为所有浏览器本身都不包含 JSON 支持。

尽管如此,我的第一个想法是:

假设您需要通过 JSON 发送这些上述功能(这对我来说听起来有点奇怪)并且您可以控制服务器发送给您的内容,那么您可以使用前缀来标记您的功能,例如js-. 如果它们需要在收到后立即执行,您可以js-使用substr剥离并使用括号表示法执行它,例如:

    //loop over values
    if (value[i].substr && value[i].indexOf('js-') === 0) {
        var func = value[i].substr(3);

        //assuming this is a global function you're calling
        window[func]();
    }

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

于 2013-09-17T04:46:35.940 回答
0

您确实没有太多选择,但还有第四种选择(JQuery 使用这种方法):

var yourFunction = new Function("var a=1; return a; //this is your function body");
于 2013-05-27T06:38:41.870 回答