Seções iMasters
JQuery + Mobile + Segurança

Melhore a segurança dos aplicativos web com o jQuery Mobile – Parte 01

Muitos desenvolvedores da Web consideram a segurança como uma prioridade baixa. Frequentemente, a segurança é relegada ao final do ciclo de vida do desenvolvimento de software, como se fosse apenas algo um pouco mais importante que um assunto de última hora.

Às vezes, a segurança de software é totalmente negligenciada, o que tem como resultado aplicativos cheios de vulnerabilidades comuns. Como os erros desse tipo talvez só se manifestem sob as condições presentes durante um ataque, podem ser difíceis de detectar antes desses eventos sem o conhecimento de como o processo de utilização funciona.

Com o surgimento dos smartphones e dispositivos semelhantes, a segurança dos aplicativos da Web foi ampliada para incluir aplicativos remotos. Por causa das limitações impostas pelas interfaces de vários dos dispositivos desse tipo, às vezes os desenvolvedores trabalham com a premissa falsa de que a validação das entradas no lado do cliente é suficiente para a proteção contra ataques.

Entretanto, as solicitações enviadas pelos aplicativos remotos podem ser manipuladas da mesma forma que os aplicativos da Web tradicionais. Por causa dessa vulnerabilidade, não é possível confiar no cliente. Com os dados sensíveis às vezes armazenados em dispositivos e nos servidores que eles usam, a proteção dos usuários contra hackers “do mal” é crítica.

Usando um aplicativo da Web desenvolvido com jQuery Mobile, PHP e MySQL, veremos como vários tipos de vulnerabilidades ocorrem e algumas das contramedidas que podem ser estabelecidas para mitigar os invasores que tentam utilizá-las. Os seguintes tipos de vulnerabilidades são abordados:

Pré-requisitos

São necessárias as seguintes ferramentas:

  • Servidor da Web — Pode-se usar qualquer servidor da Web com suporte para PHP. Muitas utilizações deste tutorial são específicas do Windows, mas podem ser adaptadas para outros sistemas operacionais. Sugerimos o servidor da Web Apache ou o IBM HTTPServer.
  • PHP — Já que alguns ataques descritos não funcionam com a versão mais recente, foi usado o PHP 5.3.1. As incompatibilidades desse tipo são indicadas ao longo do tutorial.
  • MySQL — Este tutorial usa o MySQL, um banco de dados de software livre. A versão 5.1.41 foi usada neste tutorial, mas as outras versões podem funcionar bem.
  • Proxy de depuração da Web — Como há necessidade de uma forma de manipular solicitações de HTTP, um proxy de depuração da Web é muito útil. Ao longo deste tutorial, o Fiddler v2.3.2.4 é usado, mas qualquer outro proxy de depuração da Web que permite a modificação de solicitações funciona.
  • jQuery Mobile — O frontend do aplicativo de amostra desenvolvido neste tutorial usa o jQuery Mobile 1.0 Alpha 3.

Desenvolvendo um aplicativo sem segurança

Você começa este artigo criando um aplicativo de exemplo sem segurança, chamado Contrived Mobile Application (CMA), que serve como área de teste para os diversos tipos de ataque abordados nas seções a seguir. Para fazer esse teste, o CMA tem duas partes centrais de funcionalidade:

  • Um sistema de perfil do usuário para customização
  • Uma calculadora para fazer cálculos aritméticos básicos

Cada elemento do aplicativo introduz buracos na segurança que os invasores podem usar para atingir os seus próprios objetivos. Com cada vulnerabilidade coberta, o CMA recebe correções adequadas para tentar impedir a ação de futuros hackers.

O esquema

Como um aplicativo centrado no usuário, o CMA tem um esquema simples, que consiste em uma tabela (consulte a Listagem 1).

Listagem 1. Um trecho do script de instalação do CMA

CREATE TABLE UserAccount
(
Id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Username VARCHAR(256) NOT NULL,
Password VARCHAR(32) NOT NULL,
FirstName VARCHAR(256) NOT NULL,
LastName VARCHAR(256) NOT NULL
);

INSERT INTO UserAccount (Username, Password, FirstName, LastName)
VALUES ('Jane', md5('Password1'), 'Jane', 'Smith');

INSERT INTO UserAccount (Username, Password, FirstName, LastName)
VALUES ('John', md5('Password1'), 'John', 'Doe');

São criados dois usuários: Jane e John. Para simplificar as coisas, os níveis de permissão ficaram de fora.

Seleção do idioma

A lógica de seleção do idioma é executada sempre que uma página é carregada (consulte a Listagem 2).

Listagem 2. Tratamento inadequado da entrada passada para require_once

if (isset($_COOKIE['language'])){    require_once($_COOKIE['language'] . ".php");    }

Primeiro, uma instrução condicional verifica se um cookie de idioma está presente. Caso esteja, a extensão PHP é anexada ao valor do cookie e a cadeia de caractere é passada para a função require_once para carregar o script adequado.

Autenticação e autorização

Em seguida, você acrescenta uma proteção rudimentar. Um formulário de login (veja a Figura 1) é criado juntamente com a lógica de autenticação e autorização e, no momento do login bem-sucedido do valor de sessão, CurrentUser é definido como o nome do usuário autenticado. A lógica de autorização é implementada ao verificar esse valor de sessão.

Figura 1. O formulário de login do CMA

A Listagem 3 mostra a criação da lógica de autenticação sem segurança.

Listagem 3. Lógica de autenticação sem segurança

<?php

function Authenticate($Username, $Password)
{
$query = "SELECT COUNT(*) FROM useraccount " .
"WHERE Username = '" . $Username . "' AND " .
"Password = md5('" . $Password . "');";

$result = mysql_query($query);

$_SESSION["CurrentUser"] = $Username;

return mysql_result($result, 0);
}

?>

A Listagem 4 mostra a criação da lógica de autorização sem segurança.

Listagem 4. Lógica de autorização sem segurança

<?php

if (!isset($_SESSION["CurrentUser"]) || $_SESSION["CurrentUser"] == NULL)
{
header("Location: login.php");
}

?>

Busca de usuários

Nenhum aplicativo centrado no usuário está completo sem a capacidade de procurar outros usuários dentro do sistema. O recurso de busca de usuários aceita palavras-chave e retorna as correspondências como uma lista não ordenada (A Figura 2). A Listagem 5 mostra a lógica de busca.

Listagem 5. Tratamento sem segurança de dados enviados pelo usuário na implementação da busca de usuários

Query: <?php echo $query; ?>

<ul data-role="listview" data-theme="c" style="margin-top:12px;">
<?php

$query = "SELECT FirstName, LastName " .
"FROM UserAccount " .
"WHERE FirstName LIKE '%$query%' OR " .
"LastName LIKE '%$query%';";

$result = mysql_query($query) or die(mysql_error());;

while ($row = mysql_fetch_assoc($result)) {
echo "<li>" . $row["FirstName"] . " " . $row["LastName"] . "</li>";
}

?>
</ul>

A Listagem 5 realiza uma série de funções. Primeiro, realiza a saída da consulta enviada pelo usuário na parte superior da página, como lembrete para os usuários daquilo que foi procurado. Depois disso, as condições de uma instrução SELECT são desenvolvidas dinamicamente a partir das palavras-chave fornecidas pelo usuário. A instrução SQL é passada para mysql_query, e o script faz um loop nos resultados, ecoando dados pertinentes como itens de lista em HTML.

Figura 2. Os resultados de uma busca de usuário

Calculadora

Como os usuários podem precisar resolver problemas aritméticos básicos de imediato, o CMA oferece um recurso de calculadora. A calculadora consiste em três entradas: x, y e operation. A Listagem 6 mostra o código para resolver problemas aritméticos e exibir os resultados.

Listagem 6. Tratamento sem segurança de dados enviados pelo usuário na implementação da calculadora

<?php
$operation = $_GET["operation"] == "operation-add" ?
"+" : "-";
$arithmetic = "$_GET[x] $operation $_GET[y]";
echo $arithmetic . " = ";
$code = "echo $arithmetic;";
eval($code);
?>

Essa parte do código primeiro verifica os dados de GET para determinar qual operação o usuário selecionou. Em seguida, desenvolve o problema de aritmética como uma cadeia de caractere. Depois, realiza a saída do problema completo, para que o usuário possa vê-lo, antes de interpretar a cadeia de caractere desenvolvida dinamicamente como PHP.

A Figura 3 mostra a calculadora em ação.

Figura 3. A calculadora do CMA fazendo algumas contas

Agora que você viu a calculadora do CMA, eu tratarei do núcleo do aplicativo: as preferências do usuário.

Preferências do usuário

A capacidade de customizar o CMA de forma pessoal é a sua principal funcionalidade. Permite que os usuários alterem as informações pessoais e façam o upload de uma foto para o seu perfil (consulte a Listagem 7).

Listagem 7. Tratamento sem segurança de dados enviados pelo usuário na implementação das preferências do usuário

<?php

$message = "User preferences have been saved.";

$validated = TRUE;

$update = "UPDATE UserAccount " .
"SET " .
"FirstName = '$_REQUEST[firstname]', " .
"LastName = '$_REQUEST[lastname]' ";

$password1 = $_REQUEST["newpassword1"];
$password2 = $_REQUEST["newpassword2"];

if ($password1 != NULL && $password1 != '')
{
if ($password1 != $password2)
$validated = FALSE;

$update .= ", Password = md5('$password1') ";
}

$update .= "WHERE Id = $_REQUEST[userid]";

if ($validated)
mysql_query($update) or die(mysql_error());

if (isset($_FILES["picture"]))
{
$image = $_FILES["picture"]["tmp_name"];

// For this example ping will be used as a mock image
// compression tool.
$compress_command = "ping $image $_REQUEST[imagecompression]";

exec($compress_command);

move_uploaded_file($image,
"images/" . $_FILES["picture"]["name"]);
}

echo $message;

?>

O código da Listagem 7 primeiro desenvolve e executa uma instrução UPDATE para a tabela UserAccount. Se o usuário fez o upload de uma imagem, ela é processada com um utilitário de compressão de imagem simulada e, em seguida, é movida para o diretório de imagens.

A Figura 4 mostra a interface de preferências do usuário com os campos First Name, Last Name, New Password, Repeat New Password e Profile Picture.

Figura 4. A interface de preferências do usuário mostrando a conta de John Doe

Depois de tratar da funcionalidade relevante do CMA, está na hora de examinar mais detalhadamente a sua implementação.

Veja agora as vulnerabilidades presentes, como alguém pode utilizá-las e o que se pode fazer para impedir esse uso.

Cross-site scripting (XSS)

Um Web site está vulnerável ao XSS quando um hacker pode injetar scripts no lado do cliente para atacar outros usuários. Há dois tipos de XSS: refletido e persistente. É comum a interpretação equivocada de que o XSS não é nada mais que um incômodo. Em alguns casos de XSS refletido, a ameaça é pequena; entretanto, em muitos casos, deixa os usuários vulneráveis ao comprometimento da conta ou algo pior.

XSS refletido

O XSS refletido ocorre quando os dados de solicitação são renderizados sem codificação e sem filtragem na resposta. Com a ajuda da engenharia social, o invasor pode enganar o usuário, fazendo-o visitar uma página que cria uma solicitação desse tipo e permitindo que o invasor execute o JavaScript no contexto do usuário atacado. O que se pode fazer com isso varia de acordo com a natureza do “buraco”, mas geralmente o XSS é utilizado para sequestrar sessões, roubar credenciais ou realizar outras ações que, de outra forma, não seriam autorizadas.

Persistente

Geralmente uma ameaça maior que o tipo refletido, uma vulnerabilidade de XSS é considerada persistente quando o servidor salva os dados da solicitação enviados pelo usuário. Já que os dados mal intencionados persistem dentro do aplicativo, o aspecto de engenharia social do ataque se torna mais simples ou é totalmente eliminado, dependendo da utilização.

Utilização

O CMA está cheio de vulnerabilidades de XSS; só a busca de usuários tem os tipos refletido e persistente. A utilização do tipo refletido é mostrada aqui:
http://localhost/CMA/insecure/search.php?query=%3Cscript%3Ealert (document.cookie)%3C/script%3E

Os efeitos do ataque de XSS refletido ficam visíveis imediatamente ao navegar para o link, a não ser que as contramedidas estejam estabelecidas no lado do cliente:
Query: <script>alert(document.cookie)</script>

Recursos contra o XSS podem ser integrados a um navegador, como o Microsoft® Internet Explorer ® 8, ou instalados como um plug-in, como o noXSS para o Firefox. Para os seus fins, os filtros do lado do cliente não devem ser considerados, já que não é possível depender dos usuários para instalá-los e, em muitos casos, eles só protegem contra o tipo refletido. Em alguns casos, a filtragem no lado do cliente é, na verdade, contraproducente, introduzindo vulnerabilidades de universal XSS (UXSS). O Internet Explorer 8 — antes de a Microsoft corrigir a falha — era um exemplo disso.

Para ver o XSS persistente em ação, insira no campo First Name ou Last Name do formulário User Preferences as tags de script mostradas aqui e, em seguida, procure o usuário:
<script>alert(document.cookie)</script>

O JavaScript é executado quando o usuário é mostrado nos resultados da busca.

Impeça os cross-site scripts

Para impedir ataques de XSS, normalmente é necessário aplicar a codificação correta à entrada do usuário na resposta do servidor. No caso do exemplo de XSS refletido da busca de usuários, a aplicação da codificação de entidade HTML deve ser suficiente para impedir ações mal intencionadas. É possível realizar essa etapa com a API de PHP usando a função htmlentities : Query: <?php echo htmlentities($query); ?>.

Agora, o teste do ataque com relação ao código atualizado dá um resultado diferente:
Query: <script>alert(document.cookie)</script>

Agora os caracteres de “menor que” e “maior que” estão codificados como entidades HTML, o que impede que o invasor injete marcação. A vulnerabilidade persistente é corrigida da mesma forma (consulte a Listagem 8) com a função htmlentities .

Listagem 8. Uma modificação no CMA para impedir o XSS persistente

while ($row = mysql_fetch_assoc($result)) 
{
echo "<li>" . htmlentities($row["FirstName"]) . " " .
htmlentities($row["LastName"]) . "</li>";
}

Quando dados enviados pelo usuário forem injetados em um valor de atributo HTML, tome o cuidado de garantir que os delimitadores de cadeia de caractere usados para delimitar o valor sejam retirados ou codificados dentro do valor em si. Do contrário, pode ocorrer injeção de atributo:
<a href=’http://www.mywebsite.com/’>My Website</a>

A Listagem 9 mostra como a injeção de atributo pode ocorrer.

Listagem 9. Injeção de atributo possibilitada pela falta de codificação das aspas

<a href='http://www.mywebsite.com/'onmouseover='alert(document.cookie)
'>My Website</a>

Como uma camada a mais de segurança, a habilitação do sinalizador HttpOnly do cabeçalho de resposta Set-Cookie impede que scripts do lado do cliente acessem o cookie protegido. Entretanto, não se pode depender desse recurso, porque alguns navegadores não oferecem suporte total para ele.

A próxima seção trata de outra vulnerabilidade comum que pode ser usada para lançar ataques do lado do cliente contra outros usuários de um sistema.

Cross-site request forgery (CSRF ou XSRF)

O CSRF ocorre quando um invasor engana os usuários e os faz realizar ações dentro de seu contexto de segurança. Se não há medidas de segurança estabelecidas, os hackers conseguem fazer isso, independentemente de o método do formulário ser GET ou POST. Desses dois, os ataques de CSRF que usam o método GET são a maior ameaça, porque a solicitação pode ser falsificada usando apenas uma URL, que o invasor pode usar como origem de uma imagem. Se o ataque tem a capacidade de definir arbitrariamente a origem de uma imagem dentro do sistema, os hackers podem aproveitá-la para lançar um ataque de on-site request forgery (OSRF).

Utilização

Quase todas as ações do CMA podem ser criadas novamente como um ataque de CSRF. A Listagem 10 é um exemplo de ataque baseado em GET que altera a senha do usuário atacado para new_password.

Listagem 10. Exemplo de CSRF para mudança de senha

<html>
<body>
<img
src="http://localhost/CMA/insecure/preferences.php?firstname=John&lastname
=Doe&newpassword1=new_password&newpassword2=new_password&userid
=2&imagecompression=5" />
</body>
</html>

Se o método GET não funcionar, o invasor poderá tentar falsificar a solicitação usando POST (consulte a Listagem 11).

Listagem 11. Exemplo de CSRF para mudança de senha usando o método POST .

<html>
<body onload="document.forms[0].submit()">
<form method="POST" action="http://localhost/CMA/insecure/preferences.php">
<input type="hidden" name="firstname" value="John" />
<input type="hidden" name="lastname" value="Doe" />
<input type="hidden" name="newpassword1" value="new_password" />
<input type="hidden" name="newpassword2" value="new_password" />
<input type="hidden" name="userid" value="2" />
<input type="hidden" name="imagecompression" value="5" />
</form>
</body>
</html>

O resultado da visualização do HTML renderizado na Listagem 11 é a criação de uma solicitação que é idêntica à de um usuário legítimo que está atualizando as suas preferências, mas todos os valores de formulário são controlados pelo invasor.

Impeça a falsificação de solicitação em sites cruzados

Há duas formas comuns de impedir o CSRF. A forma mais fácil de implementar é verificar o remetente na solicitação de HTTP (consulte a Listagem 12); se a solicitação não vem de uma origem confiável, deve ser rejeitada. Quanto mais granular é a verificação do remetente, melhor é a segurança.

Listagem 12. Uma implementação básica de verificação do remetente

if (strpos($_SERVER["HTTP_REFERER"], $app_host . $app_path) != 0 &&
strpos($_SERVER["HTTP_REFERER"], $app_path) != 0)
die("Invalid request");

Contudo, essa abordagem não é à prova de erros. O uso de um token de segurança é uma contramedida mais segura. Com cada formulário protegido, o servidor inclui um valor de token longo e suficientemente aleatório. Cada valor de token é rastreado no lado do servidor para garantir que seja usado só uma vez e expire depois de um período de tempo predeterminado. No momento do envio do formulário, se o valor está ausente, não é válido ou expirou, a solicitação é rejeitada porque, muito provavelmente é falsa. Sem a capacidade de adivinhar o valor do token, o invasor não consegue realizar o ataque. Se esse mecanismo de segurança é aplicado a todas as páginas do aplicativo, ele também serve para impedir o XSS refletido.

No próximo artigo você verá vários tipos de vulnerabilidades do lado do servidor. Até lá!

*

Artigo originalmente publicado em IBM developerWorks http://www.ibm.com/developerworks/br/xml/tutorials/x-jquerymobilesecuritytut/index.html

Autor: John Leitch – consultor independente de segurança de aplicativos. Mora em Grand Rapids, Michigan. Trabalha principalmente com aplicativos da Web e é especialista em teste de imprecisão, análise dinâmica e revisão de código. Sempre à caça de erros, ele lança frequentemente relatórios sobre vulnerabilidade.

Mensagem do anunciante:

Receba consultoria especializada em WordPress com os melhores profissionais do mercado. Conheça o Apiki WP Consultoria.

Comente também

3 Comentários

Flavio Ferreira

Parabéns pelo post, ótimo material.

Essa questão de segurança realmente é muito precária em muitos Sistemas / Sites, todo desenvolvedor que se preze deveria tomar isso como um dos focos principais do projeto.

Davi

Muito bom.
No meu ponto de vista sempre deve existir a
validação web tanto no servidor e no cliente,
muitas vezes o programador fazendo validações só
javascript e esquecendo do servidor.
Apesar de consumir recursos do servidor a segurança
é essencial.

Carlos H. F. M. Rodrigues

Parabéns pela matéria, muito bom!

Qual a sua opinião?