Por que os elementos React têm uma propriedade $$ typeof?

Sobre o mecanismo React para evitar a possibilidade de injeção de JSON para XSS e sobre como evitar vulnerabilidades comuns.


Você pode pensar que está escrevendo JSX:


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

Mas, na verdade, você está chamando a função:


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

E essa função retorna um objeto regular chamado elemento React. Assim, após atravessar todos os componentes, é obtida uma árvore de objetos semelhantes:


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

Se você usou o React antes, pode estar familiarizado com os campos type, props, key e ref. Mas qual é a propriedade $$typeof ? E por que tem o símbolo Symbol() como seu valor?




Antes que as bibliotecas da interface do usuário se tornassem populares, para exibir a entrada do cliente no código do aplicativo, uma linha contendo marcação HTML foi gerada e inserida diretamente no DOM, via innerHTML:


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

Esse mecanismo funciona bem, a menos que message.text esteja definido como <img src onerror="stealYourPassword()"> . Portanto, concluímos que você não precisa interpretar todas as entradas do cliente como marcação HTML.


Para se proteger contra esses ataques, você pode usar APIs seguras, como document.createTextNode() ou textContent , que não interpretam o texto. E, como medida extra, escape as strings substituindo caracteres potencialmente perigosos como < , > por caracteres seguros.


No entanto, a probabilidade de erro é alta, pois é difícil rastrear todos os locais onde você usa as informações registradas pelo usuário em sua página. É por isso que bibliotecas modernas, como o React, trabalham com segurança com qualquer texto padrão:


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

Se message.text for uma string maliciosa com uma <img> , ela não se transformará em uma <img> real. React escapa o conteúdo do texto e o adiciona ao DOM. Portanto, em vez de ver a <img> , você simplesmente vê sua marcação como uma sequência.


Para exibir HTML arbitrário dentro de um elemento React, você deve usar a seguinte construção: dangerouslySetInnerHTML={{ __html: message.text }} . O design é intencionalmente desconfortável. Devido ao seu absurdo, ele se torna mais visível e atrai a atenção ao visualizar o código.




Isso significa que o React é completamente seguro? Não. Existem muitos métodos de ataque conhecidos baseados em HTML e DOM. Os atributos de tag merecem atenção especial. Por exemplo, se você escrever <a href={user.website}> , poderá substituir o código malicioso: 'javascript: stealYourPassword()' como um link de texto.


Na maioria dos casos, a presença de vulnerabilidades no lado do cliente é o resultado de problemas no lado do servidor e deve ser corrigida antes de tudo.


No entanto, a exibição segura do conteúdo de texto personalizado é uma primeira linha de defesa razoável que reflete muitos ataques em potencial.


Com base em considerações anteriores, podemos concluir que o seguinte código deve ser completamente seguro:


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

Mas esse também não é o caso. E aqui chegamos mais perto de explicar a presença de $$typeof no elemento React.




Como explicamos anteriormente, os elementos React são objetos simples:


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

Normalmente, um elemento React é criado chamando a função React.createElement() , mas você pode criá-lo imediatamente com um literal, como acabei de fazer acima.


Suponha que armazenemos no servidor uma sequência que o usuário nos enviou anteriormente e cada vez que a exibimos no lado do cliente. Mas alguém, em vez de uma string, nos enviou JSON:


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

Ou seja, de repente, em vez da sequência esperada, o valor da variável expectTextButGotJSON acabou sendo JSON. Que será processado pelo React como um literal e, portanto, executará um código malicioso.


O React 0.13 é vulnerável a um ataque semelhante ao XSS, mas a partir da versão 0.14, cada elemento é marcado com um símbolo:


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

Essa proteção funciona porque os caracteres não são um valor JSON válido. Portanto, mesmo se o servidor tiver uma vulnerabilidade em potencial e retornar JSON em vez de texto, o JSON não poderá conter Symbol.for('response.element') . React verifica o elemento quanto ao element.$$typeof e se recusa a processar o elemento se ele estiver ausente ou inválido.


A principal vantagem do Symbol.for() é que os símbolos são globais entre os contextos porque eles usam um registro global. Isso garante o mesmo valor de retorno, mesmo em um iframe. E mesmo se houver várias cópias do React na página, elas ainda poderão "corresponder" por meio de um único valor de $$typeof .




E os navegadores que não suportam caracteres?


Infelizmente, eles não poderão implementar a proteção adicional discutida acima, mas os elementos React ainda conterão a propriedade $$typeof por consistência, mas será apenas um número - 0xeac7 .


Por que exatamente 0xeac7 ? Porque parece reagir.

Source: https://habr.com/ru/post/pt432350/


All Articles