Compreendendo a integridade dos sub-recursos
Publicados: 2022-03-10 Se você já usou uma versão hospedada em CDN de uma biblioteca JavaScript, pode ter notado um atributo de integrity
de aparência estranha na tag de script. Este atributo contém lixo alfanumérico aparentemente infinito que você pode ficar tentado a remover na busca por um código mais limpo.
Todo esse lixo é na verdade um recurso de segurança muito útil chamado Subresource Integrity (SRI) que pode ajudar a defender seu site contra certos tipos de hacks e comprometimentos. Neste artigo, veremos o que é o SRI, como ele pode ajudar a protegê-lo e como você pode começar a usá-lo em seus próprios projetos, não apenas para arquivos hospedados em CDNs.
Um pouco de história
Nos dias em que o JavaScript era o primo mais pobre do HTML e do CSS, não precisávamos pensar muito sobre como nossos scripts poderiam ser usados como vetor de ataque para nossos sites. A maioria dos sites estava hospedada em um único servidor físico em algum lugar em nossa própria infraestrutura de hospedagem, e foi o servidor que pensamos em defender quando se tratava de práticas recomendadas de segurança.
À medida que os navegadores se tornaram mais capazes e as conexões de rede ficaram mais gordas, começamos a usar cada vez mais JavaScript e, eventualmente, bibliotecas JavaScript reutilizáveis começaram a surgir. Naqueles primeiros dias, bibliotecas como script.aculo.us, Prototype e eventualmente jQuery começaram a ganhar adoção entre os desenvolvedores que buscavam adicionar mais interatividade em suas páginas.
Com essas bibliotecas adicionadas e plugins subsequentes, veio o peso da página, e em pouco tempo estávamos começando a pensar seriamente sobre o desempenho do front-end. Recursos como Content Delivery Networks (CDNs), que antes eram reserva de corporações gigantes, estavam se tornando comuns para a construção de sites rápidos.
Ao longo do caminho, alguma faísca notou que todos os sites estavam solicitando suas próprias cópias de bibliotecas comuns - coisas como o jQuery mais recente - e se houvesse uma versão CDN comum dessas bibliotecas que pudesse ser usada por todos os sites, o usuário não t precisa continuar baixando o mesmo arquivo. Eles seriam atingidos pelo primeiro site a usar o arquivo, mas ele ficaria no cache do navegador local e os downloads poderiam ser ignorados para cada site subsequente. Gênio!
É por isso que você verá links de CDN para suas bibliotecas favoritas usando URLs como jsdelivr.com
— eles estão usando uma CDN comum para hospedar os arquivos para que seus usuários vejam os benefícios de desempenho.
O que poderia dar errado?
Essa continua sendo uma maneira boa e prática de trabalhar, mas introduz um vetor potencial para ataque. Vamos imaginar que estamos em 2012 e todos estão usando o novíssimo jQuery 1.8. De volta à maneira tradicional de fazer as coisas, todos teriam seu próprio arquivo jQuery 1.8 hospedado como parte de seu próprio site em seu próprio servidor.
Se você fosse algum tipo de ator malvado - como algum tipo de Hamburglar baseado em jQuery - e tivesse descoberto uma maneira sorrateira de hackear a biblioteca para seus próprios ganhos malignos, você teria que segmentar cada site individualmente e comprometer seus servidores para ter qualquer impacto. Isso é muito esforço.
Mas não é assim que as coisas são agora, pois todo mundo está usando jQuery carregado de um CDN comum. E quando digo todos, não me refiro a centenas de páginas da web. Quero dizer milhões de páginas da web. De repente, esse arquivo se tornou um alvo muito atraente para nosso hacker obscuro. Se eles podem comprometer esse arquivo, eles podem rapidamente ter código em execução em milhões de páginas da web em todo o mundo.
Não importa qual seja esse código. Pode ser uma brincadeira para desfigurar páginas, pode ser um código para roubar suas senhas, pode ser um código para minerar criptomoedas ou pode ser rastreadores sorrateiros para segui-lo pela web e criar um perfil de marketing. O importante é que o arquivo inocente que o desenvolvedor adicionou a uma página foi alterado e agora você tem algum JavaScript maligno sendo executado como parte do seu site. Isso é um grande problema.
Insira a integridade do sub-recurso
Em vez de reverter os relógios e abandonar uma maneira útil de usar o código, o SRI é uma solução que adiciona um nível simples de segurança no topo. O que o SRI e o atributo de integrity
fazem é garantir que o arquivo que você vinculou a uma página nunca seja alterado. E se mudar, o navegador irá rejeitá-lo.
Verificar se o código não mudou é um problema muito antigo na ciência da computação e, felizmente, tem algumas soluções muito bem estabelecidas. O SRI faz um bom trabalho ao adotar o mais simples — hash de arquivos.
O hash de arquivo é o processo de pegar um arquivo e executá-lo por meio de um algoritmo que o reduz a uma representação de string curta, conhecida como hash ou checksum. Sem entrar nas ervas daninhas, o processo é repetível ou reversível, tanto que, se você der a outra pessoa um arquivo junto com o hash, ela poderá executar o mesmo algoritmo para verificar se os dois correspondem. Se o arquivo for alterado ou o hash for alterado, não haverá mais correspondência e você saberá que algo está errado e deve desconfiar do arquivo.
Ao usar o SRI, sua página da Web contém o hash e o servidor (CDN ou qualquer lugar) mantém o arquivo. O navegador baixa o arquivo e, em seguida, calcula rapidamente para garantir que ele corresponda ao hash no atributo de integrity
. Se corresponder, o arquivo é usado e, se não, é bloqueado.
Experimentando
Se eu for para getbootstrap.com
hoje para obter um link CDN para uma versão do Bootstrap, recebo uma tag parecida com esta:
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
Você pode ver que o atributo src
é como estamos acostumados, e o atributo de integrity
contém o que agora sabemos ser um hash.
O hash é na verdade em duas partes. O primeiro é um prefixo para declarar qual algoritmo de hash deve ser usado. Neste caso, é sha384
. Isso é seguido por um traço e, em seguida, o próprio hash, codificado com base64
.
Você pode estar familiarizado com base64
como uma forma de codificar arquivos embutidos como imagens em páginas. Não é um processo criptográfico — é apenas uma maneira rápida e conveniente de codificar dados potencialmente confusos de uma maneira que se traduz perfeitamente em ASCII. É por isso que é muito usado na web.

Ao ver isso, o navegador bootstrap.min.js
. Antes de executá-lo, ele decodificará o hash em base64
e, em seguida, usará o algoritmo de hash sha384
para confirmar que o hash corresponde ao arquivo que acabou de ser baixado. Se corresponder, o arquivo é executado.
Posso testar isso colocando essa tag em uma página e, em seguida, indo para a guia Rede nas ferramentas do meu navegador para ver se o arquivo foi carregado.

Eu posso ver que bootstrap.min.js
(e também o arquivo jQuery que ele precisa) foi carregado com sucesso.
Vamos ver o que aconteceria se eu atualizasse o hash para ser algo que sei estar incorreto.
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-SmashingMagazineIsCoolForCats" crossorigin="anonymous"></script>

Como você pode ver, o hash especificado na minha página não corresponde mais ao arquivo, então o arquivo é bloqueado.
Usando SRI em seus próprios projetos
Ter esse recurso para bibliotecas em uma CDN é ótimo e, se você vir a opção de usar um arquivo incorporado com um atributo de integrity
, definitivamente deve favorecer essa opção. Mas não se limita a grandes projetos em CDNs, você mesmo pode usar isso para seus próprios sites.
Não é nada absurdo imaginar um cenário em que um hacker consiga acessar apenas alguns arquivos em seu site. Acho que a maioria de nós já viu um cliente, colega ou amigo que em algum momento teve um site WordPress comprometido com uma carga de lixo desagradável que eles nem sabiam que existia.
O SRI também pode protegê-lo disso. Se você gerar hashes de integridade para seus próprios arquivos, poderá fazer com que seu site rejeite quaisquer alterações da mesma forma que faria para um arquivo hospedado remotamente.
Gerando Hashes
Você pode, como seria de esperar, executar alguns comandos no terminal do seu computador para gerar um hash para um arquivo. Este exemplo de como fazer isso vem da página MDN Subresource Integrity:
cat FILENAME.js | openssl dgst -sha384 -binary | openssl base64 -A
Isso é obter o conteúdo de FILENAME.js
e passá-lo como entrada para openssl
para criar um resumo usando sha384
, que é então passado como entrada para outro comando openssl
para codificar o resultado em base64
. Isso não é apenas complicado e obscuro, mas também não é o tipo de coisa que você deseja fazer manualmente toda vez que seu arquivo JavaScript é alterado.
Mais útil, você vai querer integrar isso de alguma forma no processo de construção do seu site e, como você imagina, há muitas opções prontas lá. A implementação exata vai variar muito com base no seu projeto, mas aqui estão alguns blocos de construção.
Se você usar o Gulp para construir seus sites, há o gulp-sri que produzirá um arquivo JSON com uma lista de seus arquivos e seus hashes. Você pode então fazer uso disso em seu site. Por exemplo, para um site renderizado dinamicamente, você pode criar um plug-in de modelo para ler esse arquivo e adicionar os hashes aos seus modelos quando necessário.
Se você ainda estiver com o Gulp, mas tiver um site estático (ou um site gerado estaticamente), poderá usar o gulp-sri-hash, que na verdade percorrerá suas páginas HTML e modificará as páginas para adicionar hashes quando necessário, o que é muito útil.
Se você estiver usando o Webpack, há a integridade do sub-recurso da página da Web que, no verdadeiro estilo do Webpack, é mais complexo do que qualquer humano poderia esperar, mas parece funcionar.
Para aqueles que usam o mecanismo de modelagem Handlebars, parece haver opções disponíveis para você e, se o processo de construção for apenas JavaScript básico, também existem soluções simples.
Se você estiver usando um CMS como o WordPress, encontrei um plugin que parece facilitar, embora eu não tenha tentado. Pesquisar no Google para sua própria plataforma de escolha com SRI ou Sub Resource Integrity provavelmente apontará você na direção certa.
Você essencialmente deseja conectar seu hashing depois que seus arquivos JavaScript forem minificados e, em seguida, tornar esse hash disponível de alguma forma para qualquer parte do seu sistema que produza as tags <script>
. Uma das maravilhas da plataforma web é que ela é tão tecnicamente diversificada, mas isso infelizmente me deixa incapaz de lhe dar boas instruções de implementação!
Outras coisas a serem observadas
Neste artigo, falei muito sobre arquivos JavaScript porque é onde realmente faz mais sentido se defender contra ataques de hackers. O SRI também funciona com CSS, então você pode usá-lo exatamente da mesma maneira lá. O risco de CSS malicioso é muito menor, mas o potencial de desfigurar um site ainda existe e quem sabe quais bugs do navegador também podem levar o CSS a expor inadvertidamente seu site a um hacker. Então é trabalho usando SRI lá também.
Outra coisa interessante que você pode fazer é usar uma Política de Segurança de Conteúdo para especificar que qualquer script (ou estilos) em sua página deve usar SRI e, claro, que SRI deve validar.
Content-Security-Policy: require-sri-for script;
Essa é uma maneira de garantir que o SRI seja sempre usado, o que pode ser útil em sites trabalhados por vários membros da equipe que podem ou não estar totalmente atualizados sobre como fazer as coisas. Novamente, um bom lugar para ler mais sobre isso são os sempre ótimos documentos MDN para a integridade de sub-recursos.
A última coisa que vale a pena falar é o suporte do navegador para SRI. O suporte em navegadores modernos é amplo, com a principal exceção sendo o Internet Explorer. Devido à forma como a especificação foi implementada, compatível com versões anteriores, no entanto, é seguro usar imediatamente. Os navegadores que entendem o atributo de integrity
usarão o hash e verificarão a integridade, e os navegadores mais antigos continuarão como sempre e continuarão funcionando. É claro que você não terá a proteção adicional nesses navegadores mais antigos, mas terá nos navegadores que oferecem suporte.
Conclusão
Vimos não apenas o que esses hashes estranhos nos atributos de integrity
fazem, mas como podemos usá-los para nos defender contra certos tipos de ataques em nosso site. É claro que não existe uma bala de prata que defenda nossos sites contra todo tipo de exploração, mas o Subresource Integrity é uma ferramenta realmente útil na cadeia.
Explorar uma falha de segurança geralmente consiste em alinhar várias peças pequenas. Se A está no lugar, e você pode fazer B acontecer, então um bug em C torna D possível. Recursos do navegador como o SRI nos dão uma boa maneira de amarrar as coisas um pouco mais e potencialmente quebrar essa cadeia e impedir que um hacker consiga o que deseja. Além do mais, se você puder integrá-lo ao seu processo de compilação ou CMS, é algo que você deve poder configurar uma vez e depois esquecer e não causará inconvenientes no dia a dia.
Como tal, eu realmente recomendo dar uma olhada séria no Subresource Integrity e implementá-lo em seus sites, se puder.