问题 如何在JavaScript中安全地将任何内容转换为String


如果我有:

var test = {toString: function(){alert("evil code"); return "test";}};

我怎么转换 test 到一个字符串?没有打电话 test.toString() 并且不使用 typeof x == "string" 检查,因为我想允许非字符串。

注意:这是针对FF扩展处理来自内容页面的js范围的对象。


5301
2017-11-30 04:28


起源

如果没有打电话,你期望什么回来? toString()? - casablanca
好吧,总有JSON序列化器...... - cdhowie
我修复了示例语法。 - Matthew Flaschen
可以将任何东西转换为字符串吗?您如何期望将JSON类表示为“安全字符串”?


答案:


JavaScript允许您修改脚本可访问的几乎任何对象的属性,包括 Object.prototype 本身,意义 任何 对象容易受到你所解释的“邪恶代码”的攻击。

只保证基元是安全的,因此确保永远不执行“恶意代码”的唯一方法是执行以下操作:

function safeToString(x) {
  switch (typeof x) {
    case 'object':
      return 'object';
    case 'function':
      return 'function';
    default:
      return x + '';
  }
}

5
2017-11-30 04:45



对于需要更多粒度的人(例如仍希望翻译) [1,2,3] 至 '1,2,3' CSV)使用类似的东西 Lodash阵列/物体检测 可以放在对象案例中: return _.isArray(x) && !_.some(x, _.isObject) ? _.toString(x) : 'object'; - jmmygoggle


你的 (toString: function(){alert("evil code"); return "test";}) 甚至没有在这里解析,它会引发语法错误。我想你想用 {} 代替 ()

通常你 可以 使用空字符串和加号运算符来执行强制转换:

""+test;
""+2; // "2"
""+4.5 // "4.5"
""+[1, 2, 3] // "1,2,3"
""+{} // '[object Object]'

但在这里,没有真正的方法可以安全地转换对象。

您可以使用 delete test.toString 为了摆脱被覆盖的方法,之后它将恢复正常 toString 返回的方法 '[object Object]'。你也可以转换 toString 方法本身变为字符串via test.toString.toString()

"function () { alert("evil code"); return "test"; }"

这取决于你在这里想要做什么。


6
2017-11-30 04:31



这仍然隐含地称之为“邪恶” toString。 - Matthew Flaschen
@Matthew确实是早上5:30在这里,他的示例代码也被破坏了,要更新它。 - Ivo Wetzel
更新答案。 - Ivo Wetzel
邪恶的代码可能是附加到原型链的toString的一部分,所以 delete test.toString()不会一直有效。 - erikvold


一种选择是:

Object.prototype.toString.call(test)

这给出了:

"[object Object]"

在测试的情况下。基本上,它只是提供类型信息。但是,我想知道具体情况是什么。如何将恶意对象加载到页面中?如果他们可以在页面上执行任意代码,那么你基本上就不走运了。除此之外,还可以重新定义 Object.prototype.toString


4
2017-11-30 04:34



即使这不是万无一失的,因为它可以覆盖 Object.prototype.toString。 - casablanca
@casa,是的。这实际上只有在他们所能做的就是将一个自包含的对象传递到页面中时才有用。如果他们可以修改全局变量的属性,那么所有的赌注都会被取消。 - Matthew Flaschen
这是捕获,恶意代码创建在与我的代码不同的范围内。这是一个处理内容页面范围的FF扩展。所以来自该范围的对象应该有不同的Object.prototype,所以使用 Object.prototype.toString 应该是安全的。 - erikvold
我认为这个答案和卡萨布兰卡的开关的组合应该可以解决问题。 - erikvold


可悲的是,@ Matthew Flaschen(目前接受的)答案不适用于 Symbol ES6 / ES2015课程:

console.log("" + Symbol("foo"));
// results in an exception: 
//   `Uncaught TypeError: Cannot convert a Symbol value to a string` 
// (at least in Chrome as of this writing).

https://jsfiddle.net/L8adq9y4/

(我不知道为什么,因为 Symbol 有一个非常好的 toString() 方法:)

console.log(Symbol("foo").toString());

https://jsfiddle.net/v1rqfhru/

但是有一个解决方案: String() 函数似乎能够将任何值(至少从我尝试的那些)转换为 String。它甚至会打电话 toString() 如果它存在:

console.log(String("A String"));
console.log(String(undefined));
console.log(String(null));
console.log(String({value: "An arbitrary object"}));
console.log(String({toString: function(){return "An object with a toString() method";}}));
console.log(String(function(){return "An arbitrary function"}));

https://jsfiddle.net/6uc83tsc/

所以,传递你喜欢的任何东西 String() 你会得到一个相当不错的结果。


1
2017-10-21 21:26



“把你喜欢的任何东西都带进去 String() 你会得到一个相当不错的结果。“不太好。 String([Symbol('AAAA')]) 给出“Uncaught TypeError:无法将符号值转换为字符串”。 - Gipsy King


您可以使用lodash toString方法。

_.toString(null);
// => ''

_.toString(-0);
// => '-0'

_.toString([1, 2, 3]);
// => '1,2,3'

链接到文档


0
2017-10-06 19:00