Como funciona a innerHTML? A innerHTML recebe um string, parsea ela, ou seja, a converte para HTML e depois insere esse HTML na página. Mas acontece isso pode ser uma brecha de segurança, já que o fato de essa string ser parseada para HTML, e esse HTML ser renderizado em uma aplicação, isso abre uma brecha gigantesca para um tipo de ataque comum chamado Cross-site Scripting (XSS).
Hoje, quando se trata de eventos em JavaScript, o recomendado é usar o método "addEventListener" para atrelar eventos em um determinado elemento. Mas temos que saber que existem outras formas de atrelar eventos e elas são:
event handler properties (propiedades de manipulação de eventos)
eventos inline (inserindo código JavaScript diretamente em uma tag HTML (esta forma não é recomendada já que poderíamos sofrer ataques XSS))
Event handler properties (propriedades de manipulação de eventos)
Todo elemento do DOM em que é possível atrelar um evento, tem essas propriedades de manipulação de eventos
Exemplo de uso:
const form = document.querySelector('[data-js="add-item-form"]');
const addItems = e => {
e.preventDefault();
//aqui vai o código para adicionar itens
console.log('O item foi adicionado');
}
form.onsubmit = addItems
//então, como podemos ver no código acima, à propriedade "onsubmit" lhe foi adicionada uma função que será executada quando o evento submit acontecer e a mensagem "O item foi adicionado" será mostrada por consola .
Mas acontece que esta forma de lidar com eventos (ou seja, usando "propriedades de manipulação de eventos" ) tem uma desvantagem com respeito ao usar o "addEventListener", já que se agente quiser executar uma segunda função quando o evento submit acontecer, só vai ser executada a segunda função, ou seja, a primeira função não vai ser executada como se mostra a seguir:
const form = document.querySelector('[data-js="add-item-form"]');
const addItems = e => {
e.preventDefault();
//aqui vai o código para adicionar items
console.log('O item foi adicionado');
}
const showMsg = e => {
e.preventDefault();
console.log('Oi');
}
form.onsubmit = addItems;
form.onsubmit = showMsg;
//Então, quando o evento "onsubmit" acontecer, por consola só vermos a mensagem "O item foi adicionado" indicando que só foi executada a segunda função (showMsg).
//Mas por que a primeira função não foi executada? Não foi executada porque não é possível adicionar mais de uma função de callback para o mesmo evento quando o tipo de evento é setado a partir de "propriedades de manipulação de eventos". Em outras palavras, o elemento "form" é um objeto, e "onsubmit" é uma propriedade, portanto, ao adicionar a função "showMsg" para "form.onsubmit" rescrevemos o valor anterior, ou seja, rescrevemos a primeira atribuição (a função addItems) que tínhamos feito.
// Então, ao contrário das "propriedades de manipulação de eventos", quando usamos o "addEventListener" podemos adicionar mais de uma função de callback para o mesmo evento.
Eventos inline (inserindo código JavaScipt diretamente em uma tag HTML
Segue exemplo:
<h2 onclick="console.log('Clicou no título')"></h2>
<!-- então, quando o usuário fizer clique no título h2, por consola será mostrada a mensagem "Clicou no título", isto mostra claramente que como valor do evento "onclick" podemos colocar qualquer código JavaScript e ele será executado -->
Como podemos ver, utilizar "Eventos inline" é uma má prática já que:
o código fica misturado (estamos inserindo JavaScript dentro de código HTML)
a manuntebilidade fica difícil
alguns servidores desabilitam o JavaScript inline por medidas se segurança.
Mas, outro problema de usar JavaScript inline, é que também é possível inserir JavaScript inline dentro de uma "template string" como se mostra a seguir:
const msg = `<button onclick="console.log('Clicou no botão')> Clique aqui</button>`
//então, cada vez que o usuário faça clique no botão, por consola será mostrada a mensagem "Clicou no botão", mostrando claramente que como valor de um evento de um elemento inserido em um template string, podemos colocar qualquer código Javascript já e ele será executado.
O HTML5 e a tag "script"
O HTML5 especifica que uma tag script inserida com innerHTML não é executada.
A seguir vamos ver um exemplo onde vamos supor que nossa aplicação tem um formulário para adicionar nomes de games, e que quando o usuário inserir um game e fizer clique no botão enviar, o evento submit será disparado e o nome do game será renderizado na tela além de inserido em um bando de dados:
const form = document.querySelector('[data-js="add-item-form"]');
const input = document.querySelector('[data-js="input"]');
const feedbackMsg = document.querySelector('[data-js="feedback"]');
const addItems = e => {
e.preventDefault();
const inputValue = e.target.input.value;
feedbackMsg.innerHTML = `<script>${inputValue}</script>`;
//aqui vai o código adicionar o game no banco de dados
}
form.addEventListener('submit', addItems);
//então, vamos supor que o usuário introduziu no input o seguinte código: <script>console.log('O game foi adicionado')</script>
//então, o que vai ocorrer quando o evento submit acontecer será:
//- a mensagem 'O game foi adicionado' não será mostrada por consola, já que como sabemos, o HTML5 especifica que uma tag script inserida com innerHTML não é executada, ou seja, mesmo que essa tag script seja parseada para HTML, essa tag script não será executada, portanto, não veremos a mensagem por consola, e também esse código não será renderizado na tela (para ver que de fato o elemento "feedbackMsg" tem o valor inserido pelo usuário, temos que inspecionar o elemento "feedbackMsg")
//- mas o código <script>console.log('O game foi adicionado')</script> sim será adicionado no banco de dados (isto é um problema de segurança)
Mas, será que nossa aplicação estará segura porque o HTML5 especifica que uma tag script inserida com innerHTML não é executada? Não, mesmo assim a nossa aplicação não está segura, já que se o usuário inserir o seguinte código no input:
<img src="x" onerror="alert('Este código foi executado')" />
este código sim será executado quando o evento sumbmit acontecer, e portanto na tela veremos um pop-up dizendo 'Este código foi executado'. Mas porqué o alert sim foi executado? Foi executado porque a innerHTML parseu a string para HTML quando a página foi carregada, e como o valor do atributo src é "x", isso causou um erro ao tentar renderizar a suposta "imagen", então, o evento "onerror" foi disparado e o alert foi executado (lembrando que qualquer código JavaScript pode ser executado quando inserido como valor de um evento). E por último, essa tag img será inserida no banco de dados (isto é um problema de segurança).
Agora, reforçando a última ideia, vamos dizer que qualquer código JavaScript pode ser executado sempre que seja atribuído para um evento (no exemplo anterior foi no evento onerror), e isso significa que um atacante pode executar um código que pega dados do local storage, ou remove tudo o conteúdo da página, ou faz uma requisição, ou pega dados da aplicação e salva eles em um servidor externo, ou obtém o que está sendo digitado, ou tudo isso e muito mais.
Então, o que descrevemos no parágrafo anterior, é o que se conhece como Cross-site Scripting (XSS), o que é um tipo de vulnerabilidade de segurança que pode ser encontrado em aplicações webs. Ou seja, o Cross-site Scripting (XSS) permite que um atacante injete scripts dentro de aplicações webs visualizadas por outros usuários.
O problema em usar innerHTML
Como funciona a innerHTML? A innerHTML recebe um string, parsea ela, ou seja, a converte para HTML e depois insere esse HTML na página. Mas acontece isso pode ser uma brecha de segurança, já que o fato de essa string ser parseada para HTML, e esse HTML ser renderizado em uma aplicação, isso abre uma brecha gigantesca para um tipo de ataque comum chamado Cross-site Scripting (XSS).
Hoje, quando se trata de eventos em JavaScript, o recomendado é usar o método "addEventListener" para atrelar eventos em um determinado elemento. Mas temos que saber que existem outras formas de atrelar eventos e elas são:
Event handler properties (propriedades de manipulação de eventos)
Todo elemento do DOM em que é possível atrelar um evento, tem essas propriedades de manipulação de eventos
Exemplo de uso:
Mas acontece que esta forma de lidar com eventos (ou seja, usando "propriedades de manipulação de eventos" ) tem uma desvantagem com respeito ao usar o "addEventListener", já que se agente quiser executar uma segunda função quando o evento submit acontecer, só vai ser executada a segunda função, ou seja, a primeira função não vai ser executada como se mostra a seguir:
Eventos inline (inserindo código JavaScipt diretamente em uma tag HTML
Segue exemplo:
Como podemos ver, utilizar "Eventos inline" é uma má prática já que:
Mas, outro problema de usar JavaScript inline, é que também é possível inserir JavaScript inline dentro de uma "template string" como se mostra a seguir:
O HTML5 e a tag "script"
O HTML5 especifica que uma tag script inserida com innerHTML não é executada.
A seguir vamos ver um exemplo onde vamos supor que nossa aplicação tem um formulário para adicionar nomes de games, e que quando o usuário inserir um game e fizer clique no botão enviar, o evento submit será disparado e o nome do game será renderizado na tela além de inserido em um bando de dados:
Mas, será que nossa aplicação estará segura porque o HTML5 especifica que uma tag script inserida com innerHTML não é executada? Não, mesmo assim a nossa aplicação não está segura, já que se o usuário inserir o seguinte código no input:
este código sim será executado quando o evento sumbmit acontecer, e portanto na tela veremos um pop-up dizendo 'Este código foi executado'. Mas porqué o alert sim foi executado? Foi executado porque a innerHTML parseu a string para HTML quando a página foi carregada, e como o valor do atributo src é "x", isso causou um erro ao tentar renderizar a suposta "imagen", então, o evento "onerror" foi disparado e o alert foi executado (lembrando que qualquer código JavaScript pode ser executado quando inserido como valor de um evento). E por último, essa tag img será inserida no banco de dados (isto é um problema de segurança).
Agora, reforçando a última ideia, vamos dizer que qualquer código JavaScript pode ser executado sempre que seja atribuído para um evento (no exemplo anterior foi no evento onerror), e isso significa que um atacante pode executar um código que pega dados do local storage, ou remove tudo o conteúdo da página, ou faz uma requisição, ou pega dados da aplicação e salva eles em um servidor externo, ou obtém o que está sendo digitado, ou tudo isso e muito mais.
Então, o que descrevemos no parágrafo anterior, é o que se conhece como Cross-site Scripting (XSS), o que é um tipo de vulnerabilidade de segurança que pode ser encontrado em aplicações webs. Ou seja, o Cross-site Scripting (XSS) permite que um atacante injete scripts dentro de aplicações webs visualizadas por outros usuários.