quarta-feira, 18 de agosto de 2010

Canto da segurança: Fixação de sessão

Continuando com a seção de traduções de artigos, lá vem mais um do Chris Shiflett, complementar à tradução anterior. Lá vai:

Canto da segurança: Fixação de sessão


Segurança está ganhando mais e mais atenção entre os profissionais de PHP. Como o PHP continua sendo um componente chave do futuro da web, atacantes maliciosos começaram a atacar fraquezas nas aplicações em PHP de forma mais frequente, e os desenvolvedores precisam estar prontos.

Eu estou muito contente de introduzir o Canto da Segurança, uma nova coluna mensal que é completamente focada em segurança no PHP. A cada mês discutirei um tópico importante com grande detalhamento que pode ajudar a aumentar a segurança de suas aplicações em PHP e a se defender de vários tipos de ataques. Estes tópicos não serão vagos, introduções gerais, então se você está procurando por uma introdução sobre segurança em aplicações PHP, você estará melhor servido por outras fontes de informação, como o capítulo sobre segurança do manual do PHP.

O tópico deste mês é Fixação de Sessão, um método de obter um identificador de sessão válido, sem a necessidade de adivinhar ou de capturar um. O nome deste tipo de ataque se originou de uma publicação da Acros Security entitulado Session Fixation Vulnerability in Web-based Applications, embora este método por si próprio seja anterior à publicação. Irei expandir a idéia básica de fixação de sessão e demonstrar alguns métodos de prevenção, tudo dentro do contexto específico do PHP.

Segurança de sessões é um tópico vasto e complexo. Um dos princípios fundamentais da segurança em aplicações web é nunca confiar nos dados vindos do cliente. Contudo, com o objetivo de atingir o estado completo, o cliente deve identificar a si mesmo enviando um identificador único. Este conflito fundamental cria complexidades significantes pra os desenvolvedores que querem construir aplicações de estado completo e seguras. De fato, o mecanismo de sessões em qualquer aplicação web é a característica mais vulnerável da aplicação, e segurança de sessões é um dos tópicos mais complexos das aplicações web em qualquer plataforma.

Existem numerosos tipos de ataques baseados em sessão. Muitos dos quais se enquadram numa categoria chamada personificação (roubo de sessão), onde um usuário malicioso tenta acessar a sessão de outro usuário, tentando se passar por aquele usuário. No fundo, estes tipos de ataque necessitam que o usuário malicioso obtenha um identificador de sessão válido, porque esta é a quantidade de informação mínima que deve ser usada para a identificação.

Existem, pelo menos, três formas para que um identificador de sessão válido seja obtido por um atacante:

  1. Predição
  2. Captura
  3. Fixação

Predição envolve apenas adivinhar um identificador de sessão válido. O intervalo desta adivinhação pode variar desde chute até uma mais certeira, dependendo da sofisticação do ataque que se usa. Com o mecanismo de sessões nativo do PHP, identificadores de sessão válidos são extremamente difíceis de predizer, então este não está para ser o ponto mais fraco da sua implementação.

Capturar um identificador de sessão válido é muito mais comum, e existem numerosos tipos de ataque que usam essa abordagem. Quando um cookie é usado para armazenar o identificador de sessão, uma vulnerabilidade no browser pode ser explorada para se obter o identificador de sessão. Quando uma variável de URL é usada, o identificador de sessão é mais exposto, e existem muitos métodos potenciais de captura. Por esta razão, cookies são geralmente considerados mais seguros do que variáveis de URL para a propagação de identificadores de sessão, embora as preferências de usuário devem ser respeitadas, e vulnerabilidades de browser existam em todas as versões do browser mais popular, Internet Explorer (veja peacefire.org/security/iecookies/ e Passport Hacking Revisited para mais informações).

Fixação de sessão é um método que engana a vítima para ela usar um identificador de sessão escolhido pelo atacante. Se bem sucedido, ele representa o método mais simples no qual um identificador de sessão pode ser obtido.

Um simples ataque


No mais simples caso, a fixação de sessão usa um link:

<a href="http://host/index.php?PHPSESSID=1234">
Click here
</a>

Ou um redirecionamento no nível de protocolo:
<?php
header('Location: http://host/index.php?PHPSESSID=1234');
?>

Outro métodos incluem o cabeçalho Refresh, seja passado como um legitimo cabeçalho HTTP ou usando o atributo http-equiv da tag meta. O ponto é fazer o usuário visitar uma URL remota que inclua um identificador de sessão escolhido pelo atacante. Este é o primeiro passo num ataque básico, e o fluxo completo do ataque é ilustrado na figura 1.

Figura 1.

Se bem sucedido, um atacante está apto a evitar a necessidade de capturar ou predizer um identificador de sessão válido, e é subsequentemente possível lançar ataques de outros tipos e mais perigosos.

Você acha que não está vulnerável? Considere o código da Lista 1. Salve este código como session.php em algum lugar que você possa testá-lo. Depois que você se assegurar que não existem cookies do mesmo servidor (limpe todos os cookies caso não tenha certeza), use a URL terminando em session.php?PHPSESSID=1234 para visitar a página. Por exemplo, http://host/session.php?PHPSESSID=1234. O script deve escrever 0 na sua tela após a primeira visita. Recarregue a página algumas vezes, e você irá notar que o número aumenta cada vez, indicando o número de visitas.

Lista 1
<?php
session_start();

if (!isset($_SESSION['count']))
{
$_SESSION['count'] = 0;
}
else
{
$_SESSION['count']++;
}

echo $_SESSION['count'];
?>

Com um outro browser, ou mesmo com um computador completamente diferente, refaça os exatos passos iniciais. Após visitar a URL pela primeira vez, você perceberá que não vê 0. Mais que isso, ele chama a sua sessão prévia. Assim, você personificou o usuário prévio. Agora, se você considerar que tudo isso começou com um identificador de sessão sendo passado na URL, você pode ver o perigo básico que a fixação de sessão apresenta. Diferente de um cenário típico, o PHP não gerou o identificador de sessão.

Existem poucas falhas neste tipo de ataque simplista. A falha mais importante é que a aplicação alvo deve usar o identificador de sessão passado a ela, senão este ataque falhará. Se o seu mecanismo de sessões não é nada mais do que session_start(), suas aplicações estão vulneráveis como a demosntração prévia ilustra. Para prevenir este tipo específico de vulnerabilidade, você deve sempre se assegurar que um novo identificador de sessão é usado sempre que está iniciando uma sessão pela primeira vez. Existem muitas formas de se alcançar isto, e um exemplo é dado na Lista 2 (esta abordagem, também, tem pelo menos uma brecha, então espere até o você finalizar este artigo antes de decidir qual solução se enquadra melhor nas suas necessidades).

Lista 2

<?php
session_start();

if (!isset($_SESSION['initiated']))
{
session_regenerate_id();
$_SESSION['initiated'] = true;
}
?>

Se o código da lista 2 é usado para iniciar todas as sessões, qualquer sessão existente sempre terá uma variável de sessão iniciada que sempre está configurada. Se não é o caso, é uma nova sessão. A chamada a session_regenerate_id troca o valor atual do identificador de sessão por um novo, embora ainda retenha as informações antigas da sessão. Então, se um atacante força um usuárioem usar um link externo que contenha um identificador de sessão para sua aplicação, esta abordagem previnirá o atacante de saber o novo identificador de sessão, a menos que está sessão já esteja iniciada.

Um Ataque sofisticado

Um ataque mais sofisticado de fixação de sessão é um onde o atacante primeiro inicia a sessão no site alvo, opcionalmente mantém a sessão de incorrer em expiração, e então executa os passos mencionados previamente.

Uma alternativa à abordagem utilizada na Lista 2 é chamar session_regenerate_id() sempre que um usuário faz login de forma bem sucedida, a partir deste momento os dados de sessão se tornam sensíveis para a maioria das aplicações. Por exemplo, sempre que você valida o nome de usuário e a senha de um usuário, você pode configurar uma variável de sessão que indica sucesso:

$_SESSION['logged_in'] = true;

Apenas configurando tal vairável de sessão antes, uma chamada para session_regenerate_id() pode ajudar a proteger contra um ataque de fixação de sessão:

session_regenerate_id();
$_SESSION['logged_in'] = true;

De fato, uma boa abordagem é sempre regenerar o identificador de sessão sempre que o privilégio do usuário mudar de alguma forma, incluindo situações onde o usuário deve re-autenticar devido a expiração. Fazendo isso, você pode estar certo que a vulnerabilidade de fixação de sessão não é o aspecto mais fraco do seu mecanismo de controle de acesso.

Esta abordagem é mais segura do que o exemplo anterior, porque ela adiciona outro obstáculo significante para um atacante transpor, e previne ataques sofisticados, onde o indentificador de sessão é primeiro criado e mantido. Infelizmente, há ainda pelo menos uma fraqueza, embora o design da sua aplicação já deve previni-la.

Um ataque avançado


No mais avançado tipo de ataque de fixação de sessão, o atacante primeiro obtém uma conta válida na aplicação alvo e isto é tipicamente apelativo apenas quando o atacante pode fazer isso anonimamente. Em algumas aplicações PHP, a página de login é um script separado, como login.php, e este script pode não checar o estado do usuário, porque parece seguro assumir que o usuário não foi autenticado ainda.

Pelo contrário, esta abordagem permite que um atacante crie uma sessão, entre na aplicação com aquela sessão, opcionalmente evitando que a sessão expire, e usando a URL da página de login para lançar o ataque. Se o login. Se a página de login aceitar o novo login de usuário mas falhar para regenerar o identificador (porque o nível de acesso não mudou), a vulnerabilidade existe.

Este cenário pode parecer improvável, mas uma análise aprofundada do seu código com esta sitaução em mente vale o tempo investido. Existem dois métodos fáceis de prevenir este caso em especial:

  1. Faça a página de login sempre reconhecer o estado do usuário.
  2. Sempre regenere o identificador de sessão no script receptor, independente do estado do usuário.

Até a pŕoxima...


Uma recomendação genérica boa para prevenir ataque de fixação de sessão é regenerar o identificador de sessão sempre que o usuário providenciar informações de autenticação de qualquer tipo. Desconfie de repassar as sugestões simplistas "pega-tudo", contudo, por causa que más interpretações são mais prováveis quando alguém não está familizarizado com o tipo de ataque a ser prevenido. Não há bom substituto para um bom entendimento da fixação de sessão, e é possível que a melhor prevenção para suas aplicações não esteja nem mencionada neste artigo.

Felizmente, agora você pode eliminar a fixação de sessão da sua lista de riscos sérios à segurança com os quais você deve se preocupar. Se você desenvolver um método de prevenção particularmente criativo, eu adoraria ficar por dentro. Até o próximo mês, e fiquem seguros.

CSS Hacks, IE e Standards

Não, este não é um post para falar de como os outros navedores são mais legais que o Internet Explorer, e sim sobre como fazer uma "gambiarra elegante".

OK, não fui completamente sincero. O IE carece sim de muitos recursos e suporte que os outros navegadores dispõe, e isso faz diferença para quem trabalha com web. O que poucos desenvolvedores e webdesigners sabem na verdade é que o IE tem muitas soluções próprias para muita coisa. Sim, concordo que essas soluções não estão previstas nos padrões, mas o fato aqui é que elas existem.

Vou um pouco além e faço um questionamento sobre os próprios padrões e a implementação nos navegadores. Os novos - e bonitos - recursos de CSS3 incluem bordas arredondadas, sombras, cores com transparência (RGBA e HSLA) entre muitos outros. Pois bem, a nova safra de navegadores traz suporte a esses recursos, mas... nem tudo é tão fácil assim. Muitas das propriedades são implementadas através de:

myElt {
-moz-someNewStandardProperty: 20px;
-webkit-someNewStandardProperty: 20px;
-khtml-someNewStandardProperty: 20px;
-o-someNewStandardProperty: 20px;
someNewStandardProperty: 20px;
}

Sim, o padrão prevê abertura para implementações proprietárias através de prefixos (-moz, -o, -webkit,...). Assim, navegadores modernos como o Firefox, Chrome, Safari e Opera podem desconhecer completamente uma regra padrão, mas ter uma implementação proprietária para o mesmo. A exemplo das bordas arredondadas. Somente o Opera é que tem suporte à declaração nativa, prevista no padrão. Todos os outros tem implementações proprietárias (não foge do padrão por este mecanismo estar previsto, mas não é a mesma propriedade).

No meu ponto de vista, isso é uma coisa boa, mas também me parece uma gambiarra elegante e regulamentada.

O IE oferece alternativas a muitos desses recursos (não a todos, eu sei), e também a muitos recursos que os outros nem sonham em ter. O fato é que muitos desses recursos no Internet Explorer usam declarações completamente proprietárias. Da versão 7 pra cima, algumas propriedades já puderam são reconhecidas através de declarações com prefixo -ms-.

Enfim, vamos à minha sugestão de como fazer as coisas. Os benefícios são:
- Mantém o código válido (W3C)
- Segue recomendação da Microsoft
- Agiliza o processo de desenvolvimento

Primeiro, desenvolva o trabalho como de costume, SEM DAR A MÍNIMA PARA O LAYOUT NO IE, mas por favor, marcação semântica, enxuta, inteligente.
Depois, hora de alinhar as coisas no IE, mas sem sujar o código que já está feito e funcionando. Para isso, use a marcação no final (depois das suas declarações de CSS e Javascript) do seção <head>:

<!--[if IE]>
<script type="text/javascript" src="/scripts/ie.js"></script>
<link rel="stylesheet" type="text/css" href="/styles/ie.css" />
<![endif]-->

Esse código é um comentário, ou seja, será ignorado por outros navegadores e pelo validador W3C. Mas é reconhecido - e recomendado - pela Microsoft/IE. Ótimo, assim podemos apenas adicionar as regras, efeitos e hacks necessários apenas nestes arquivos.

Alguns de vocês podem conhecer esta técnica, e alguns podem até sugerir que se faça uso das comparações avançadas (que também são recomendadas pela MS), como [if gte IE 7] ou [if IE 8], mas neste ponto eu já discordo. Ao invés de usar várias folhas de estilo e/ou vários scripts, sugiro usar regras de fallback num arquivo único. É simples, funcional, e diminui a quantidade de arquivos a serem baixados e mantidos facilitando a manutenção.

A idéia de fallback pra quem não está familizarizado consiste em declarar as propriedades mais antigas, ou não suportadas DEPOIS das regras mais aceitas. Assim, se a última regra falha, o navegador mantém a regra válida anterior.

A produtividade aumenta porque se evita o trabalhoso processo de modificar e testar repetidas vezes até que o resultado desejado seja obtido. Se trabalha uma vez, se faz os ajustes uma única vez também, se a preocupação de quebrar o layout em outros navegadores.

Bem, é isso. Espero que os códigos de vocês fiquem mais limpos, e que o processo de desenvolvimento seja mais rápido.
----------- keepReading