JavaScript JSON eval()注入

3
我正在制作一个AJAX聊天室,根据一本AJAX书籍的指导,教我使用JSON和eval()函数。 这个聊天室有普通聊天功能和白板功能。 当一个普通的文本消息以JSON格式从php服务器传来时,浏览器中的javascript会执行以下操作:

不包含白板命令----------------------------------------------------

function importServerNewMessagesSince(msgid) {
    //loadText() is going to return me a JSON object from the server
    //it is an array of {id, author, message}
    var latest = loadText("get_messages_since.php?message=" + msgid);
    var msgs = eval(latest);
    for (var i = 0; i < msgs.length; i++) {
                    var msg = msgs[i];
                    displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
    }   ...

白板绘图命令以JSON格式由服务器发送,使用名为“SVR_CMD”的特殊用户名,现在JavaScript已经略有更改:

通过白板命令进行--------------------------------------------------

function importServerNewMessagesSince(msgid) {
    //loadText() is going to return me a JSON object from the server
    //it is an array of {id, author, message}
    var latest = loadText("get_messages_since.php?message=" + msgid);
    var msgs = eval(latest);
    for (var i = 0; i < msgs.length; i++) {
                    var msg = msgs[i];
                    if (msg.author == "SVR_CMD") {

                        eval(msg.contents);  // <-- Problem here ...

                         //I have a javascript drawLine() function to handle the whiteboard drawing
                        //server command sends JSON function call like this: 
                        //"drawLine(200,345,222,333)" eval() is going to parse execute it
                        //It is a hacker invitation to use eval() as someone in chat room can
                        //insert a piece of javascript code and send it using the name SVR_CMD?

                   else {
                        displayMessage(escape(msg.id), escape(msg.author), escape(msg.contents));
                    }

    }   ...

现在,如果黑客将他的用户名更改为SVR_CMD,并开始在消息输入框中键入javascript代码而不是drawLine(200,345,222,333),而是注入redirectToMyVirusSite()。eval() 将在聊天室中的每个人的浏览器中运行它。 因此,正如您所见,让eval来执行来自聊天室中其他客户端的命令显然是邀请黑客。我知道我遵循的书只是用作功能介绍。在实际情况下,我们应该如何使用JSON来处理呢?

比如,是否有服务端php或.net函数可用于javascriptencode/escape以确保没有黑客可以向其他客户端浏览器发送有效的javascript代码进行eval()?或者使用JSON eval() 是否安全,它似乎是一个强大但邪恶的功能?

谢谢, 汤姆


3
我讨厌网络开发书籍的原因是它们无法随着网络的变化而改变。 eval() = 邪恶。 - iambriansreed
3个回答

9
这本书是关于IT技术的。其中提到了eval函数是有害的,绝不应该使用。
要将JSON字符串转换为JavaScript对象,可以按照以下步骤操作:
var obj = JSON.parse(latest)

这意味着您可以使用以下内容:
[].forEach.call(obj, function( o ) {
    // You can use o.message, o.author, etc.
} )

要进行相反的操作(将Javascript对象转换为JSON字符串),可以使用以下方法:

var json = JSON.stringify(obj)

根据Angus Croll的说法,JSON.parse本身使用eval()。https://javascriptweblog.wordpress.com/2010/04/19/how-evil-is-eval/ - Goose
@Goose 是的,eval 有用途。(这个回答已经三年了,我以前对这些事情曾经很坚定。)但是,我仍然建议永远不要使用它。有经验的程序员将会认识到使用场景非常有限,并且他们会知道,在编程中,教条主义不能解决问题 :)。此外,现代浏览器中的 JSON.parse 不使用 eval。例如,请参见v8源代码:https://code.google.com/p/v8/source/browse/trunk/src/json-parser.h#93 - Florian Margaine
@Goose另一件事:你可以直接查看源代码,而不是引用别人的话。在这里是Crockford的代码,在这里是jQuery的代码。顺便说一下,这表明jQuery不再使用eval。(也就是说,你的文章已经过时了。) - Florian Margaine
谢谢你的回答。我没有想到去检查来源。我将来会尝试。JSON/现代浏览器不再使用eval是有道理的。 - Goose

3
只有当执行的代码是由其他客户端而不是服务器生成时,才会不安全。当然,您需要防止任何人使用该名称,但我不明白为什么要使用“作者”字段?只需发送一个对象{"whiteboard":"drawLine(x,y,z)"},而不是{"author":"SVR_CMD","contents":"drawLine(x,y,z)"}。
但是,eval()仍然是黑客的邀请函。人们总是可以发送无效数据并尝试更或少直接地影响输出。唯一的逃避方法是对您想要接收和发送的数据进行适当的序列化-即绘图数据。您如何接收白板命令?没有服务器端的"逃脱"函数可以使JavaScript代码"干净"-它始终都是一个安全漏洞。
我希望看到像这样的序列化:
message = {
    "author": "...", // carry the information /who/ draws
    "whiteboard": {
         "drawline": [200, 345, 222, 333]
    }
}

这样你就可以轻松地净化命令(在这里是“drawline”)。

如果您有非常复杂的命令并希望通过服务器端构建来减少传输数据,则使用eval()可能是可以的。但是,您仍需要正确解析和转义其他客户端接收到的命令。但我建议找到不需要使用eval的解决方案。


  1. 该代码重用了importServerNewMessagesSince()函数,并将author=svr_cmd视为特殊用户,其中'contents'字段包含javascript命令。
  2. 它在'contents'字段中接收白板命令drawLine(x,y,z,w),其中drawLine()是网页中的javascript函数。eval("drawLine(x,y,z,w)")将直接执行该函数。
- Tom
不,问题是“服务器如何接收绘制线条的命令?”。如果服务器接收到JavaScript代码字符串,这不仅是一个入侵你工具的邀请,它也是一个敞开大门的漏洞! - Bergi

1

暂且不考虑 eval 的问题,不要在你的代码中使用可以由用户填写的字段 - .author - 用于身份验证目的。向 JSON 消息添加另一个字段,比如说 .is_server_command,当存在时,将表示消息的特殊处理。这个字段不依赖于用户输入,因此不会被“黑客”劫持。


作者是用户的“显示名称”。用于输入消息和/或在白板上绘制图片的人。我从书上复制粘贴了这段代码。只想让核心部分工作,然后再处理会员事务。我对JSON不太熟悉。复制的代码中的eval()函数引起了我的注意。所以我发了一个帖子。 - Tom
是的。正是因为用户决定了这个字段中会填写什么内容,所以你不能将它用于只应由服务器决定的内容。 - Oleg V. Volkov
“特殊”的用户名可以是SVR_CMD,也可以是“34ee52aa3457ff”或任何令牌。当发出白板绘制命令时,此ID将暴露给任何打开源代码查看的人。因此,黑客始终可以复制该令牌并模仿服务器命令。我的观点是,问题似乎在于eval()过于强大,可以执行来自用户注入的代码片段。 - Tom
如果您使用另一个仅由服务器生成且不受用户输入影响的字段,则可以避免此问题。eval功能强大,但是只有在服务器使用谨慎生成的数据进行授权调用时,它才是相当安全的。 - Oleg V. Volkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接