hi 但是实际上您正在调用该函数: React.createElement( /* type *...">

为什么React元素具有$$ typeof属性?

关于防止XSS注入JSON的React机制,以及避免常见漏洞。


您可能认为您正在编写JSX:


<marquee bgcolor="#ffa7c4">hi</marquee> 

但是实际上您正在调用该函数:


 React.createElement( /* type */ 'marquee', /* props */ { bgcolor: '#ffa7c4' }, /* children */ 'hi' ) 

这个函数返回一个常规的对象,称为React元素。 因此,遍历所有组件后,将获得一棵相似对象的树:


 { type: 'marquee', props: { bgcolor: '#ffa7c4', children: 'hi', }, key: null, ref: null, $$typeof: Symbol.for('react.element'), } 

如果您以前使用过React,则可能熟悉type,props,key和ref字段。 但是, $$typeof属性是什么? 为什么将符号Symbol()作为其值?




在UI库流行之前,为了在应用程序代码中显示客户端输入,已生成包含HTML标记的行,并通过innerHTML直接将其插入DOM:


 const messageEl = document.getElementById('message'); messageEl.innerHTML = '<p>' + message.text + '</p>'; 

除非将message.text设置为<img src onerror="stealYourPassword()">否则该机制将正常工作。 因此,我们得出结论,您不需要将所有客户端输入都解释为HTML标记。


为了防止此类攻击,可以使用不解释文本的安全API,例如document.createTextNode()textContent 。 另外,通过用安全的字符替换诸如<>类的潜在危险字符来转义字符串。


但是,错误的可能性很高,因为很难跟踪使用用户在页面中记录的信息的所有位置。 这就是为什么诸如React之类的现代库可以安全地使用任何默认文本的原因:


 <p> {message.text} </p> 

如果message.text是带有<img>的恶意字符串,则不会变成真正的<img> 。 React会转义文本内容,然后将其添加到DOM中。 因此,您无需看到<img> ,而只是将其标记视为字符串。


要在React元素内显示任意HTML,您必须使用以下构造: dangerouslySetInnerHTML={{ __html: message.text }}设计是故意不舒服的。 由于它的荒谬性,它变得更加引人注目,并在查看代码时引起注意。




这是否意味着React完全安全? 不行 有许多基于HTML和DOM的已知攻击方法。 标签属性值得特别注意。 例如,如果您编写<a href={user.website}> ,则可以将恶意代码替换为文本链接: 'javascript: stealYourPassword()'


在大多数情况下,客户端上存在漏洞是服务器端问题的结果,因此应首先对其进行修复。


但是,安全显示自定义文本内容是可以反映许多潜在攻击的合理的第一道防线。


基于先前的考虑,我们可以得出结论,以下代码应该是完全安全的:


 //   <p> {message.text} </p> 

但事实并非如此。 在这里,我们将进一步说明React元素中是否存在$$typeof




如前所述,React元素是简单的对象:


 { type: 'marquee', props: { bgcolor: '#ffa7c4', children: 'hi', }, key: null, ref: null, $$typeof: Symbol.for('react.element'), } 

通常,React元素是通过调用React.createElement()函数创建的,但是您可以立即使用文字来创建它,就像我上面所做的那样。


假设我们在服务器上存储用户先前发送给我们的字符串,并且每次在客户端上显示它时。 但是有人而不是字符串向我们发送了JSON:


 let expectedTextButGotJSON = { type: 'div', props: { dangerouslySetInnerHTML: { __html: '/*     */' }, }, // ... }; let message = { text: expectedTextButGotJSON }; //    React 0.13 <p> {message.text} </p> 

也就是说,突然, expectedTextButGotJSON变量的值不是预期的字符串,而是JSON。 这将由React作为文字处理,从而执行恶意代码。


React 0.13容易受到类似XSS的攻击,但是从版本0.14开始,每个元素都用符号标记:


 { type: 'marquee', props: { bgcolor: '#ffa7c4', children: 'hi', }, key: null, ref: null, $$typeof: Symbol.for('react.element'), } 

这样的保护有效,因为字符不是有效的JSON值。 因此,即使服务器具有潜在的漏洞并返回JSON而不是文本,JSON也不能包含Symbol.for('response.element') 。 React检查元素中的element.$$typeof ,如果元素缺失或无效,则拒绝处理。


Symbol.for()的主要优点是符号在上下文之间是全局的,因为它们使用全局注册表。 这样即使在iframe中也可以确保相同的返回值。 即使页面上有多个React副本,它们仍然可以通过$$typeof的单个值进行“匹配”。




不支持字符的浏览器呢?


las,他们将无法实现上面讨论的附加保护,但是React元素将仍然包含$$typeof属性以保持一致性,但它只是一个数字0xeac7


为什么正好是0xeac7 ? 因为它看起来像React。

Source: https://habr.com/ru/post/zh-CN432350/


All Articles