EA878:2009 2S:atividade 10

De DCA-Wiki

Contents

Objetivos

Implementação de suporte a requisições POST no servidor Web.

Introdução

Uma das funcionalidades mais importantes de um servidor Web é prover uma porta para a introdução de dados que venham alimentar bases de dados ou outros programas. Para isso pode-se contar com a requisição POST do protocolo HTTP, a qual está detalhadamente descrita na mesma RFC2616 que especifica o protocolo HTTP 1.1.

O uso da requisição POST se baseia em uma página html formatada especialmente como um formulário para poder receber dados e enviá-los posteriormente ao servidor. Esta página precisa ser inicialmente obtida do servidor pela requisição GET, apresentada na tela do navegador, preenchida com os dados solicitados e submetida ao servidor (normalmente através de um botão tipo "Enviar" disponível na mesma).

Esta página especial tipo formulário pode ser criada facilmente com editores especializados em HTML (como o NVU, por exemplo) e não vamos tratar do formato interno da mesma aqui. Para os objetivos deste laboratório, é suficiente saber que :

  • a página html de formulário para o POST será obtida normalmente (via GET) do servidor;
  • esta página terá um ou mais campos para preenchimento e cada um será indentificado com um nome único;
  • cada campo deverá ter um tipo associado: text, password, submit button, reset button são alguns exemplos;
  • os dados preenchidos em cada campo formarão um par com o nome do respectivo campo e estes pares serão agrupados em uma ou mais linhas de dados que serão codificadas e enviadas ao servidor Web logo após o cabeçalho de uma requisição POST (detalhes sobre a codificação estão mais abaixo).

Exemplo de uma página tipo formulário

A seguir é apresentado o código da página ano_nascimento.html:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
    <html>
    <head>
    <title>Obtenção do ano de nascimento do usuário</title>
    </head>
    <body>
    <h2 style="text-align: center;">EA878 - Laboratório de mini e microcomputadores</h2>
    <h3 style="text-align: center;">Formulário para atualizar ano de nascimento</h3>
    <form method="post">
      <table style="text-align: left; margin-left: auto; margin-right: auto;">
        <tbody>
          <tr>
            <td style="text-align: right;">Nome do usuário: </td>
            <td style="text-align: center;">
            <input name="nomeusuario" size="12" maxlength="20" type="text">
            </td>
          </tr>
          <tr>
            <td style="text-align: right;">Ano de nascimento:</td>
            <td><input name="ano de nascimento" size="12" maxlength="4" type="text"></td>
          </tr>
        </tbody>
      </table>
      <br>
      <div style="text-align: center;"><input value="Enviar" name="confirmar" type="submit"></div>
    </form>
    </body>
    </html>

Este link mostra como a página acima é exibida num browser.

Exemplo de uma requisição POST submetida por meio do formulário anterior.

    POST /ano_nascimento.html HTTP/1.1
    Host: localhost:8888
    User-Agent: Firefox/3.5
    Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,
            image/png,*/*;q=0.5
    Accept-Language: pt-br,es;q=0.8,en;q=0.5,en-us;q=0.3
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 300
    Connection: keep-alive
    Referer: http://localhost:8888/ano_nascimento.html
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 48
nomeusuario=ana&ano+de+nascimento=1980&confirmar=Enviar

O cabeçalho da requisição POST acima indica que o Content-Type: é application/x-www-form-urlencoded para explicitar o tipo de codificação aplicado sobre os dados enviados. Esta é a codificação default para todos os formulários, possuindo as seguintes características:

  • cada caracter não alfanumérico é substituído por '%HH', onde HH representa os dígitos hexadecimais correspondentes ao código ASCII do caracter;
  • espaços são substituídos por '+' ;
  • quebras de linha são indicadas por pares CR LF codificados como '%0D%0A';
  • os campos são agrupados na ordem em que aparecem na página (no formulário);
  • nome e valor de um campo são separados pelo símbolo '=' ;
  • os pares nome=valor dos campos são separados pelo símbolo '&';
  • campos que estiverem em branco (incluindo botões não selecionados) não precisam ser enviados pelo navegador na requisição.

Nova funcionalidade do servidor com POST: alteração de senhas via navegador

O servidor Web em desenvolvimento deverá prover suporte para um usuário alterar sua senha já cadastrada. Para isso, deverá ser apresentada ao usuário uma página tipo formulário contendo:

  • título da página: para indicar que se trata de uma página para troca de senha;
  • campo solicitando o nome do usuário: deve ser digitado o nome da forma como está cadastrado no arquivo de senhas;
  • campo solicitando a senha atual do usuário: usuário deve prover sua senha atual como forma de se autenticar perante o servidor;
  • campo solicitando a nova senha do usuário: deve aceitar a nova senha (até 8 caracteres, já que a função crypt usada no servidor não comporta mais);
  • campo solicitando a confirmação da nova senha: o usuário deverá digitar novamente sua nova senha para que sejam detectados eventuais erros de digitação no servidor;
  • campo tipo botão de "submit": botão usado para enviar a requisição POST.

Uma cópia desta página de alteração de senhas deve existir em cada diretório que possuir um arquivo .htaccess, devendo haver um link para ela na parte de baixo de todas as páginas html existentes nestes diretórios. O arquivo de senhas que será atualizado pela requisição POST será aquele indicado dentro do arquivo .htaccess do diretório em questão.

A página ano_nascimento.html apresentada como exemplo acima pode servir como ponto de partida para a criação do formulário de atualização de senhas. O ponto que precisa ser alterado é o tipo do campo para a senha: se ele for do tipo "text", a senha será mostrada na tela do navegador à medida em que é digitada, como acontece com o campo nome. Mas se o tipo for mudado para "password", então só serão mostrados asteriscos para cada caracter digitado no campo, protegendo a senha contra curiosos. Portanto, o tipo adotado deve ser "password".

Após receber a requisição POST o servidor deverá extrair dela os pares nome/valor de cada campo e providenciar a troca da senha do usuário de acordo com as etapas a seguir (não necessariamente nesta ordem):

  • obter o caminho para e abrir o arquivo de senhas a partir do .htaccess do diretório protegido; em caso de falha, enviar uma página de erro com esta informação para o navegador;
  • verificar se o nome do usuário consta do arquivo de senhas selecionado; se não constar, enviar uma página de erro com esta informação para o navegador;
  • criptografar a senha atual enviada pelo POST e comparar com a senha cadastrada no arquivo; se forem diferentes, enviar uma página de erro com esta informação para o navegador;
  • verificar se os dois campos de senha nova enviados pelo POST são rigorosamente iguais; se não forem, enviar uma página de erro com esta informação para o navegador;
  • substituir a senha atual pela versão criptografada da nova senha enviada;
  • enviar uma página indicando o sucesso da operação para o navegador;
  • fechar o arquivo de senhas.

Observe que, independentemente do que acontecer, o servidor deverá devolver ao navegador uma página indicando, da melhor maneira possível, o que aconteceu. Observe ainda que um determinado usuário poderá ter senhas diferentes em diferentes regiões do webspace e que ele poderá alterar cada uma destas senhas de forma independente das outras.

Uso de conexões seguras (cifradas)

Apesar de ser possível definir um campo de senhas com o tipo "password" e assim evitar que curiosos leiam as senhas enquanto são digitadas, não há proteção para os campos digitados em seu trajeto até o servidor. Nem mesmo a codificação base64 é aplicada nos dados transmitidos.

Sendo assim, é necessário algum outro mecanismo que proteja os dados enviados ao servidor. O ideal seria que este mecanismo protegesse também os dados que vêm do servidor para o navegador, dando mais tranquilidade para quem quer ler, por exemplo, o extrato de sua conta bancária na Web.

Este mecanismo existe e se chama Secure Sockets Layer (SSL). Ele foi desenvolvido pela Netscape como um pacote de funções criptográficas que seriam adicionadas sobre a pilha de protocolos do sistema, entre a camada de aplicação (navegador) e a camada de transporte (TCP). SSL surgiu muito cedo e logo se tornou o padrão de fato para conexões seguras na Web, tendo sido adotado até pelos concorrentes da Netscape. Existe uma versão em domínio público chamada de OpenSSL, a qual é bastante utilizada em conjunto com instalações de servidores Web também de domínio público, como o Apache, por exemplo.

O que o SSL faz é entrar como intermediário na comunicação entre navegador e servidor e estabelecer um canal de comunicação seguro (cifrado) entre estes. Para isso é preciso que o SSL esteja instalado e corretamente configurado nos dois extremos da conexão. No início da troca de mensagens para estabelecimento deste canal seguro, as duas implementações de SSL discutem quais serão os algoritmos de criptografia e as chaves secretas que irão usar, mas todo este processo é transparente para o usuário.

Para que o SSL seja ativado como intermediário de uma nova conexão, é preciso que o protocolo especificado na URL inserido no navegador seja o "https:". Se isso for feito e o servidor suportar conexões seguras, várias mensagens serão trocadas entre ele e o navegador para escolha de algoritmos criptográficos e chaves. Correndo tudo bem, aparecerá no navegador uma indicação (normalmente um ícone em forma de cadeado) de que foi estabelecido um canal seguro com o servidor e que o conteúdo da página que está sendo mostrada trafegou criptografado, sendo do conhecimento apenas do servidor e do navegador.

Para inserir o suporte a conexões seguras em um servidor, é preciso inserir no mesmo algumas chamadas a funções da biblioteca do OpenSSL. A leitura e a escrita será feita em sockets abertos pelo OpenSSL. Em um servidor que tenha suporte ao SSL os recursos enviados ao mesmo com POST e aqueles retornados por ele estarão protegidos contra eventuais "grampos" na rede.

A integração da biblioteca OpenSSL ao servidor web em desenvolvimento ficará como atividade opcional neste semestre. Os alunos que a fizerem terão um bônus de até um ponto na média final e que será proporcional a quão usável e seguro se tornou o servidor com esta nova funcionalidade. Alguns tutoriais sobre a utilização desta biblioteca podem ser encontrados na internet, como os que estão nos links abaixo. O professor e o auxiliar didático também estarão a disposição para tirar dúvidas relativas a esta implementação opcional.

Ferramentas pessoais