简体   繁体   English

解析以字符串形式存储在对象常量/ JSON语法中的函数,并进行区分

[英]Parsing functions stored as strings in object literal/JSON syntax and differetiating

There is a previous question ( here ), however the answers there does not answer my question exactly - the accepted answer contained invalid JSON (mandating the use of eval() ), which is simply not possible to do even something like that as far as I am aware. 有一个先前的问题( 在这里 ),但是那里的答案并不能完全回答我的问题-接受的答案包含无效的JSON(强制使用eval() ),就目前而言,甚至不可能做类似的事情我知道。

I'm planning to use code from my own server that is stored as object literal syntax in a string, however I'd like the ability to store functions in there as well. 我打算使用来自我自己的服务器的代码,该代码作为对象文字语法存储在字符串中,但是我也希望能够在其中存储函数。

Currently, I've thought of the following possibilities: 目前,我想到了以下可能性:

  1. Simply use eval() to parse the string 只需使用eval()解析字符串
  2. Place functions in string form (with something like "\\bfunction" to be able to identify them), running JSON.parse() on it and then use a for-in loop to see whether any such functions need to be parsed (probably quite slow) 将函数以字符串形式放置(使用类似"\\bfunction"以能够识别它们),在其上运行JSON.parse() ,然后使用for-in循环查看是否需要解析任何此类函数(可能相当慢)
  3. Use the DOM to run the code using a <script> tag and just run it there 使用DOM使用<script>标签运行代码,然后在其中运行

This code will not contain anything that's supposed to be user-editable, however I'm not sure whether there would still be a safety issue or just a speed one. 该代码将不包含任何应该由用户编辑的内容,但是我不确定是否仍然存在安全问题或只是速度问题。 Would using eval() be appropriate for my situation, and is there a more effective method of doing this than parsing for functions manually or using eval() ? 使用eval()是否适合我的情况,并且有比手动解析函数或使用eval()更有效的方法吗?

EDIT: would an alternative syntax to parse be better or would that just make things even more complicated? 编辑:解析语法的替代语法会更好还是会使事情变得更加复杂?

EDIT2: I'm looking simply to do something like the following: EDIT2:我只是想做类似以下的事情:

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

I'm not looking to just parse an entire function from a string, eg 只是想从字符串中解析整个函数,例如

eval('function(){}');

You basically have only two options to bring function code to the client: 基本上只有两个选项可以将功能代码带到客户端:

  • Use a JavaScript object literal, in which you include function expressions. 使用JavaScript对象文字,其中包含函数表达式。 Whether you use AJAX + eval or a JSONP-like approach with a <script> node doesn't really matter in terms of performance. 在性能方面,您是否真正使用AJAX + eval或具有<script>节点的类似JSONP的方法都没有关系。 AJAX will be more flexible though (HTTP methods, synchronous requests) and not need a global callback function. AJAX会更灵活(HTTP方法,同步请求),并且不需要全局回调函数。

    Using a JS expression will give you all possible freedom, that includes custom data types (like Date objects or invoking your own constructors) or even circular structures (if wrapped within an IEFE ). 使用JS表达式将为您提供所有可能的自由,包括自定义数据类型(例如Date对象或调用您自己的构造函数),甚至是圆形结构(如果包装在IEFE中 )。 However, serializing such data on the server into a script will be more difficult, might need to be hand-crafted and is more error-prone (syntax or even runtime errors will cause the whole parsing to fail). 但是,将服务器上的此类数据序列化为脚本会更加困难,可能需要手工制作,并且更容易出错(语法甚至运行时错误都会导致整个解析失败)。

  • Use valid JSON , and then create functions from the code strings of which you know. 使用有效的JSON ,然后从您知道的代码字符串创建函数。 Using that standardized format will simplify the serverside serialization, and make your data accessible to clients without a JS interpreter. 使用该标准化格式将简化服务器端的序列化,并使无需JS解释器的客户端即可访问您的数据。 This approach is very common, most people who use objects with custom prototypes are well accustomed to this task. 这种方法非常普遍,大多数将对象与自定义原型一起使用的人都非常熟悉此任务。 You just will use the Function constructor instead. 您将只使用Function构造Function

    Depending on your schema, you can either iterate/access the parsed structure and redefine the appropriate properties, or you use the reviver callback argument to JSON.parse . 根据您的模式,您可以迭代/访问已解析的结构并重新定义适当的属性,或者对JSON.parse使用reviver回调参数。 An example: 一个例子:

     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); }); 

Effectiveness (Will they do what they should do?) 有效性 (他们会做应做的事吗?)

All of 1, 2, and 3 will work 1、2和3都可以使用

  1. eval the responseText: This will work fine. 评估responseText:可以正常工作。 If you add in a same-origin challenge per security recommendation #3, you must correct it first. 如果您根据安全建议3添加相同来源的挑战,则必须首先更正它。

  2. Identify individual items in the object to "revive". 确定对象中要“重新生成”的单个项目。 I've used this approach before, and prefer to use a naming convention for the keys to determine when to revive, rather than a marker in the value. 我以前使用过这种方法,并且更喜欢对键使用命名约定来确定何时恢复,而不是在值中标记。 See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse for a description of revivers. 请参阅https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse ,以获取有关复活器的描述。

    In both cases 1 and 2, you will have to somehow cause the function text to be parsed & executed. 在情况1和2中,您都将不得不以某种方式导致函数文本被解析和执行。 Here are your options. 这是您的选择。 None is really less evil than the others (see section on security) 没有人比其他人真正邪恶(请参阅安全性部分)

    If you use f=eval(responseText), this is a "direct eval". 如果使用f = eval(responseText),则这是“直接评估”。 The function will have local scope where the eval is called. 该函数将具有调用eval的本地范围。

    If you use f=(1,eval)(responseText), this is an "indirect eval". 如果使用f =(1,eval)(responseText),则这是一个“间接eval”。 The function will have global scope. 该功能将具有全局范围。 See http://perfectionkills.com/global-eval-what-are-the-options/ 参见http://perfectionkills.com/global-eval-what-are-the-options/

    If you use f=new Function(responseText), the function will have a separate local scope under the global scope. 如果使用f = new Function(responseText),则该函数将在全局范围内具有单独的局部范围。

  3. This method is called JSONP (for JSON with padding). 此方法称为JSONP(用于带填充的JSON)。 It will work and is probably the easiest to implement. 它会起作用,并且可能是最容易实现的。 It is also not good when the response may contain sensitive user data (see below). 当响应中可能包含敏感的用户数据时,这也不是一件好事(请参阅下文)。

Security (Will they not do what they should not do?) 安全性 (他们会不做应该做的事吗?)

IF the delivered message IS under your control (100% sure no user/attacker can modify it): All of options 1, 2, and 3 do NOT compromise the user's browser state (ie XSS, eg enabling things like stealing their cookies). 如果传递的消息在您的控制之下(100%确保没有用户/攻击者可以对其进行修改):所有选项1、2和3都不会损害用户的浏览器状态(即XSS,例如,启用诸如窃取其cookie的操作)。

IF the delivered message IS NOT 100% certainly under your control: All of methods 1, 2, and 3 DO compromise the user's browser state and are unsafe. 如果确定不是100%完全控制了所传递的消息:方法1,方法2和方法3都会破坏用户的浏览器状态,并且是不安全的。 Your options are: 您的选择是:

  • Accept the security risk (users/attackers will be able to arbitrarily change your site functionality, including but not limited to: stealing your users' cookies for the domain, stealing any information the user has access to on your domain, directing your users to malware-infested pages to potentially install viruses) 接受安全风险(用户/攻击者将能够任意更改您的站点功能,包括但不限于:窃取您域中用户的Cookie,窃取用户在您域中有权访问的任何信息,将您的用户定向到恶意软件-感染网页以可能安装病毒)

  • Pass the potentially unsafe functions into a webworker and run them there. 将潜在的不安全功能传递给Webworker并在其中运行它们。 They will be sandboxed and cannot affect the browser window. 它们将被沙盒化,并且不会影响浏览器窗口。 However, this is more work and not available in all browsers. 但是,这需要更多工作,并且并非在所有浏览器中都可用。

  • Pass the potentially unsafe functions into an iframe on a separate domain and run them there. 将可能不安全的功能传递到单独域中的iframe中,然后在其中运行它们。 This protects your users' cookies,etc. 这样可以保护您用户的Cookie等。 but does not prevent attackers from redirecting them to exploit sites to install viruses (though you can just hope your users have secure browsers :-/ ) 但不能阻止攻击者将其重定向到利用站点来安装病毒(尽管您可以希望您的用户使用安全的浏览器:-/)

  • Use whitelisted functions that you do control and just pass around the name of the whitelisted function (or factory functions that are essentially whitelisted functions with a few flexible parameters) 使用您可以控制的列入白名单的函数,并仅传递该列入白名单的函数的名称(或实质上是具有几个灵活参数的列入白名单的函数的工厂函数)

IF you pass user-specific information in your data: 如果您在数据中传递了用户特定的信息:

  • 3 is not safe for passing around sensitive user data (the script can easily be called with the user's cookies from any domain), unless your server implements checks on the referer/origin HTTP headers of the request before returning the response. 3不能传递敏感的用户数据(可以轻松地从任何域使用用户的cookie调用脚本),除非您的服务器在返回响应之前对请求的引用/来源HTTP标头执行检查。 Even then it can supposedly be unsafe. 即使那样,它还是可以认为是不安全的。 See http://en.wikipedia.org/wiki/Cross-site_request_forgery 参见http://en.wikipedia.org/wiki/Cross-site_request_forgery

  • The naive implementations of options 1, 2 are also unsafe in this way (ie XSRF. eg a malicious site can forge a request for the data on behalf of the user using their cookies and then do what they like with it). 选项1 天真的实现,2也以这种方式不安全(即XSRF。例如,一个恶意站点可以代表使用他们的cookies的用户的伪造的数据的请求,然后做什么,他们有什么样子)。 A third party domain can overload the built-in Array constructor and then insert a script tag pointing to the JSON file. 第三方域可以重载内置Array构造函数,然后插入指向JSON文件的脚本标签。 The browser will request this file with the user's cookies, return the user's JSON to the third-party site, and the javascript engine will "run" the JSON, which is just a single statement, but because the Array constructor has been overloaded, it can do whatever it wants with the data as javascript tries to construct the value in that single JSON "statement" 浏览器将使用用户的cookie来请求此文件,将用户的JSON返回到第三方站点,并且javascript引擎将“运行” JSON,这只是一条语句,但是由于Array构造函数已被重载,因此它可以做任何想要的数据处理,因为javascript试图在单个JSON“语句”中构造值

  • For 1 and 2 to be XSRF safe, you should put a statement that would cause it to break if interpreted as a script, such as invalid syntax or an infinite loop, then your script should receive the text and modify it to remove the error. 为了使1和2成为XSRF安全,您应该放置一条语句,如果将其解释为脚本(例如无效的语法或无限循环),则该语句将导致其中断,然后您的脚本应接收文本并对其进行修改以消除错误。 This is because same-domain scripts have access to read the responseText of a request before they parse it, whereas cross-domain scripts can only access this information by inserting a script tag with this as the source. 这是因为同域脚本可以在解析请求之前读取请求的responseText,而跨域脚本只能通过插入带有此标签作为源的脚本标签来访问此信息。

There are a lot of ways to create a JS function from a string, but eval is evil and should be avoided when it's possible. 有很多方法可以从字符串创建JS函数,但是eval邪恶的 ,应尽可能避免使用eval

You can create the function in different ways, for example using the new Function("...") statement, or creating a function in your scope with that name and then call it with parameters as string. 您可以通过不同的方式来创建函数,例如,使用new Function("...")语句,或者在您的作用域中使用该名称创建一个函数,然后使用参数作为字符串来调用它。 I have done a JSFiddle with a test. 我已经通过测试完成了JSFiddle。 The fastest way is eval , but as I said before it should be avoided. 最快的方法是eval ,但是正如我之前所说,应该避免。 DOM and new Function() ways are equally fast, on 1000 iterations they have a difference of few milliseconds(fn: 3328, DOM: 3371) Here you have the JSFiddle , do some tests and draw your own conclusions. DOM和new Function()方式同样快,经过1000次迭代,它们之间的差异只有几毫秒(fn:3328,DOM:3371),这里您有JSFiddle ,进行了一些测试并得出了自己的结论。

The main difference between eval and new Function is that the former can access the local variables while the latter can't. evalnew Function之间的主要区别是前者可以访问局部变量,而后者则不能。 See this answer . 看到这个答案

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

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

There are a few things to consider for your specific situation. 针对您的具体情况,需要考虑一些事项。

I think the rule of thumb should be, if you're not sure whether or not to use eval , you probably shouldn't use it. 我认为经验法则应该是,如果不确定是否使用eval ,则可能不应该使用它。 I also think some people obsess over performance where operations per sec could really negligible. 我还认为有些人对性能的痴迷,因为每秒的操作量实际上可以忽略不计。 Is your app a huge app that relies on squeezing every ounce of performance out? 您的应用程序是否是一个庞大的应用程序,是否需要充分利用每一分性能? Is 100,000ops/sec vs 1,000,000ops/sec going to be the difference between whether or not your app is usable? 100,000ops / sec与1,000,000ops / sec是您的应用是否可用之间的区别? Also you'll have to consider support as all browsers do not include JSON support natively. 另外,您还必须考虑支持,因为所有浏览器本身都不包含JSON支持。

Nevertheless, my first thought: 不过,我的第一个想法是:

Assuming you need to have these said functions being sent via JSON (which sounds a little odd to me) and you have control over what the server is sending you, would be to label your functions with a prefix like js- . 假设您需要通过JSON发送这些所说的函数(这对我来说有点奇怪),并且您可以控制服务器向您发送的内容,那就是使用js-这样的前缀来标记您的函数。 If they needed to be executed as soon as they are received, you could strip the js- off using substr and execute it using bracket notation something like: 如果需要在接收到它们后立即执行它们,则可以使用substr剥离js-并使用括号表示法执行它,例如:

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

If you're not trying to execute them immediately, maybe add them to an array/object literal for calling later. 如果您不打算立即执行它们,则可以将它们添加到数组/对象常量中以供以后调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM