• Nenhum resultado encontrado

Tecnologias Web 2010/11

N/A
N/A
Protected

Academic year: 2021

Share "Tecnologias Web 2010/11"

Copied!
83
0
0

Texto

(1)

Tecnologias Web 2010/11

Comon Gateway Interface (CGI)

Departamento de Ciência de Computadores

Faculdade de Ciências da Universidade do Porto

(2)

CGI — O que é?

• Common Gateway Interface

• Interface standard para a execução de

programas via web

(server-side scripts)

– RFC 3875

– Define aspectos como

• Directório em que o

script

é executado

• Variáveis de ambiente

• Tratamento dos descritores de ficheiro standard

• Etc.

• Não necessariamente em Perl…

(3)

CGI — Como funciona?

• O servidor web recebe um pedido HTTP com URL que

identifica como recurso dinâmico gerado por CGI

– E.g., através da extensão ".cgi" ou por estar no directório cgi-bin

• O servidor lança um novo processo para executar o

programa CGI

– Se for um executável nativo é corrido normalmente

– Se for um script é corrido o interpretador da linguagem respectiva

– Se for bytecode Java é corrida a JVM

• Os parâmetros são passados através de variáveis de

ambiente

– Query string

– Cabeçalhos HTTP

(4)

CGI — Como funciona?

• O corpo do pedido HTTP é passado através da

entrada-padrão

– Apenas se existir (e.g., no método POST)…

• O programa CGI gera a resposta na saída-padrão

– Cabeçalhos (parciais)

– Corpo da resposta

• O servidor recebe a saída-padrão do CGI e envia-a para o

cliente (navegador)

(5)

Exemplo simples

#!/usr/bin/perl -wT

print << "END_OF_HTML";

Content-type: text/html

<html>

<head><title>Hello, World!</title></head>

<body>

<h1>About this server</h1>

<ul>

<li>Server name: $ENV{SERVER_NAME}</li>

<li>Running on port: $ENV{SERVER_PORT}</li>

<li>Server software: $ENV{SERVER_SOFTWARE}</li>

<li>Server protocol: $ENV{SERVER_PROTOCOL}</li>

<li>CGI revision: $ENV{GATEWAY_INTERFACE}</li>

</ul>

</body>

</html>

(6)

Descritores standard

• STDIN

– Usado para obter o corpo do pedido (se existir)

– Não existe marcador de fim de ficheiro 

tentativas de ler mais do que foi recebido

bloqueiam o script

• Não tentar ler quando o método for GET

• Quando o método for POST, deve obter-se o valor de

Content-length: e ler apenas esse número de bytes

• STDOUT

– Resposta a devolver

– Alguns cabeçalhos, uma linha em branco e o corpo

da resposta

(7)

Descritores standard

• STDERR

– CGI não impõe tratamento do STDERR

– A escrita para STDERR normalmente induz um erro

500 Internal Server Error

– Alguns servidores (e.g., Apache) guardam saída nos

logs

• Útil para debugging

Servidor Web CGI

v.a. e stdin stdout stderr Pedido Resposta

(8)

Variáveis de ambiente standard

Variável

Descrição

AUTH_TYPE

Método de autenticação (vazio se o pedido não requeria

autenticação)

CONTENT_LENGTH

Comprimento em bytes do corpo da mensagem

CONTENT_TYPE

Tipo do corpo (e.g., ―application/x-www-form-urlencoded‖)

DOCUMENT_ROOT

Directório-base a partir de onde se servem os documentos

GATEWAY_INTERFACE

Versão da interface CGI usada pelo servidor

PATH_INFO

Informação adicional de caminho passada ao script CGI

(e.g., sub-recurso)

PATH_TRANSLATED

Pathname do recurso no sistema de ficheiros

QUERY_STRING

Pergunta no URL pedido (tudo a seguir ao "?").

REMOTE_ADDR

Endereço IP do cliente que fez o pedido (navegador ou

proxy HTTP)

REMOTE_HOST

Nome do cliente que fez o pedido (navegador ou proxy)

(9)

Variáveis de ambiente standard

Variável

Descrição

REMOTE_IDENT

Utilizador que fez o pedido (indicado pelo identd)

REMOTE_USER

Login do utilizador (se autenticado pelo servidor web)

REQUEST_METHOD

Método HTTP usado para fazer o pedido (GET ou POST)

SCRIPT_NAME

Caminho no URL (e.g., /cgi-bin/program.cgi) do script

SERVER_NAME

Nome ou endereço IP do servidor web

SERVER_PORT

Porta na qual o servidor web está à escuta

SERVER_PROTOCOL

Nome e versão do protocolo do pedido (e.g., "HTTP/1.1")

SERVER_SOFTWARE

Nome e versão do servidor web

(10)

Cabeçalhos do pedido HTTP

• Todos os cabeçalhos do pedido HTTP que não estão em

variáveis standard podem ser acedidos através de

HTTP_*

– HTTP_ACCEPT, HTTP_ACCEPT_CHARSET, HTTP_ACCEPT_ENCODING,

HTTP_ACCEPT_LANGUAGE, HTTP_COOKIE, HTTP_FROM, HTTP_HOST,

HTTP_REFERER, HTTP_USER_AGENT, …

– Também cabeçalhos desconhecidos

• Variável HTTPS indica se a conexão é segura (―on‖ ou

―ON‖ se for, ―‖ ou ―OFF‖ se não for)

• Geralmente é possível configurar o servidor web para

passar variáveis adicionais que possam ser úteis

(11)

Saída

• O programa tem que gerar pelo menos um

cabeçalho — um dos seguintes é obrigatório

– Content-type: especificando o conteúdo do corpo

gerado

– Location: especificando um URL para

redireccionamento

– Status: com um código de estado que não requeira

dados adicionais (e.g., 204 No Response)

• A mensagem em texto pode diferir da standard, mas um

(12)

Saída

• Geração de um documento

– É necessário especificar o tipo de documento

gerado

– As duas mudanças de linha correspondem à linha

vazia que separa os cabeçalhos do corpo

– O servidor web converte cada mudança de linha em

CR-LF, confome especificado pelo HTTP

(13)

Saída

• Redireccionamento

– É necessário um cabeçalho Location: para

especificar o alvo do redireccionamento

– Se o URL for absoluto ou se for relativo com um

caminho relativo, é devolvida a resposta ao cliente

que faz outro pedido para o novo URL

– So o URL for relativo com caminho absoluto ocorre

um redireccionamento interno

• O servidor web vai buscar o recurso indicado e retorna-o

como se fosse a resposta do CGI  mais rápido

(14)

Saída

• Especificação do código de resposta

– Feita através do pseudo-cabeçalho Status:

– Script indica código e descrição

– O servidor web intercepta este pseudo-cabeçalho e

gera a linha de estado em conformidade

– Este pseudo-cabeçalho é opcional:

• Se for gerado um ―Content-type:‖, o servidor web gera

automaticamente uma resposta 200 OK

• Se for gerado um ―Location:‖, o servidor gera

automaticamente uma resposta 302 Found

(15)

Cabeçalhos completos

• Normalmente não é necessário o script gerar

todos os cabeçalhos

– O servidor web interpreta os cabeçalhos emitidos

e completa-os conforme necessário

• Contudo, é possível ser o script a gerar a

totalidade dos cabeçalhos

– Modo NPH (Non-Parsed Headers)

– Seleccionado iniciando o nome do script por ―nph-‖

(e.g., nph-mycgi em vez de mycgi)

– Neste modo, o script tem que gerar também a linha

de estado da resposta HTTP

(16)

Exemplo simples com NPH

#!/usr/bin/perl -wT

print << "END_OF_HTML";

$ENV{SERVER_PROTOCOL} 200 OK

Content-type: text/html

<html>

<head><title>Hello, World!</title></head>

<body>

<h1>About this server</h1>

<ul>

<li>Server name: $ENV{SERVER_NAME}</li>

<li>Running on port: $ENV{SERVER_PORT}</li>

<li>Server software: $ENV{SERVER_SOFTWARE}</li>

<li>Server protocol: $ENV{SERVER_PROTOCOL}</li>

<li>CGI revision: $ENV{GATEWAY_INTERFACE}</li>

</ul>

</body>

</html>

(17)

Exemplo 1

#!/usr/bin/perl -wT

use strict;

my $image_type = $ENV{HTTP_ACCEPT} =~ m|image/png| ? "png" : "jpeg";

my $basename = $ENV{PATH_INFO} =~ /^(\w+)/;

my $image_path = "$ENV{DOCUMENT_ROOT}/images/$basename.$image_type";

unless ( $basename and -B $image_path and open IMAGE, $image_path ) {

print "Location: /errors/not_found.html\n\n";

exit;

}

my $buffer;

print "Content-type: image/$image_type\n\n";

binmode;

while ( read( IMAGE, $buffer, 16_384 ) ) {

print $buffer;

}

(18)

Exemplo 2

$remote_user = $ENV{REMOTE_USER};

if ( $remote_user eq "mary" ) {

print "Welcome Mary, how is your company doing these days?\n";

} elsif ( $remote_user eq "bob" ) {

print "Hey Bob, how are you doing? I heard you were sick.\n";

}

(19)

Descodificação da entrada de formulários

• Frequentemente, a entrada para um CGI é

obtida a partir de formulários

• Estes formulários podem estar em páginas

estáticas ou ser gerados pelo mesmo ou por

outro CGI

• Se o método for GET, os pares <chave>=<valor>

são colocados na

query string

• Se o método for POST, são colocados no

corpo do pedido

(20)

Descodificação da entrada de formulários

1. Ler a

query string

de $ENV{QUERY_STRING}

2. Se $ENV{REQUEST_METHOD} é POST, determinar o tamanho do

pedido usando $ENV{CONTENT_LENGTH} e ler essa quantidade de

bytes de STDIN. Acrescentar estes dados aos lidos da

query

string

(se presente); a junção faz-se com ―&‖

3. Separar os resultados pelo carácter " &" character, que separ os

diferentes pares <chave>=<valor>

4. Separar cada par <chave>=<valor> pelo carácter ―=‖

5. Descodificar os caracteres URL-encoded no nome (chave) e no

valor

6. Associar cada nome com o(s) respectivo(s) valore(s), recordando

que cada opção pode ter múltiplos valores

(21)

Descodificação da entrada de formulários

sub parse_form_data {

my %form_data;

my $name_value;

my @name_value_pairs = split /&/, $ENV{QUERY_STRING};

if ($ENV{REQUEST_METHOD} eq 'POST') {

my $query = "";

read(STDIN, $query, $ENV{CONTENT_LENGTH}) == $ENV{CONTENT_LENGTH}

or return undef;

push @name_value_pairs, split /&/, $query;

}

foreach $name_value (@name_value_pairs) {

my($name, $value) = split /=/, $name_value;

$name =~ tr/+/ /;

$name =~ s/%([\da-f][\da-f])/chr(hex($1))/egi;

$value = [] unless defined $value;

$value =~ tr/+/ /;

$value =~ s/%([\da-f][\da-f])/chr(hex($1))/egi;

push @{$form_data{$name}}, $value;

}

return %form_data;

}

(22)

O módulo CGI.pm

• Simplifica muito as tarefas necessárias num CGI

– É uma das razões para o Perl ser tão popular para CGIs

• Tratamento da entrada

– Informação de ambiente (cabeçalhos, etc.)

– Parsing da entrada de formulários

– Gestão de uploads

• Geração da saída

– Geração de cabeçalhos

– Geração de código HTML

• Controlo de erros

– CGI::Carp permite apanhar

die

e outras condições de erro

que poderiam terminar abruptamente o script

(23)

CGI.pm — exemplo simples

#!/usr/bin/perl -Tw use strict; use CGI; my $q = new CGI; my $name = $q->server_name(); print $q->header("text/html"), $q->start_html("Welcome"),

$q->p("Hi there! Server at $name speaking."), $q->end_html;

Content-Type: text/html; charset=ISO-8859-1 <!DOCTYPE html

PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US"> <head>

<title>Welcome</title>

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head>

<body>

<p>Hi there! Server at localhost speaking.</p> </body>

(24)

Métodos e variáveis correspondentes

Método CGI.pm Variável de Ambiente CGI

auth_type

AUTH_TYPE

N/D

CONTENT_LENGTH

content_type

CONTENT_TYPE

N/D

DOCUMENT_ROOT

N/D

GATEWAY_INTERFACE

path_info

PATH_INFO

path_translated PATH_TRANSLATED

query_string

QUERY_STRING

remote_addr

REMOTE_ADDR

remote_host

REMOTE_HOST

remote_ident

REMOTE_IDENT

remote_user

REMOTE_USER

(25)

Métodos e variáveis correspondentes

Método CGI.pm

Variável de Ambiente CGI

request_method

REQUEST_METHOD

script_name

SCRIPT_NAME

self_url

Indisponível

server_name

SERVER_NAME

server_port

SERVER_PORT

server_protocol

SERVER_PROTOCOL

url

Indisponível

Accept *

HTTP_ACCEPT

http("Accept-charset") HTTP_ACCEPT_CHARSET

http("Accept-encoding") HTTP_ACCEPT_ENCODING

* Quando um método do CGI.pm tem o mesmo nome de uma função interna

ou palavra-chave do Perl, a primeira letra do método é maiúscula.

(26)

Métodos e variáveis correspondentes

Método CGI.pm

Variável de Ambiente CGI

http("Accept-language") HTTP_ACCEPT_LANGUAGE

http("From")

HTTP_FROM

raw_cookie

HTTP_COOKIE

virtual_host

HTTP_HOST

referer

HTTP_REFERER

user_agent

HTTP_USER_AGENT

https

HTTPS

https("Cipher")

HTTPS_CIPHER

https("Keysize")

HTTPS_KEYSIZE

https("SecretKeySize") HTTPS_SECRETKEYSIZE

(27)

Notas sobre alguns métodos

• O funcionamento ―standard‖ para os métodos de acesso a variáveis de

ambiente é ser invocados sem parâmetros e devolver o conteúdo da

variável correspondente; no entanto há excepções a esta regra.

• Accept

– Invocado sem argumentos retorna a lista de tipos aceites

– Invocado com um argumento (e.g., ―text/plain‖), devolve o factor de

preferência associado

• http

– Invocado sem argumentos devolve a lista de variáveis de ambiente HTTP_*

disponíveis

– Invocado com o nome de uma variável HTTP_* ou do cabeçalho

correspondente devolve o valor desse cabeçalho

• https

– Invocado sem argumentos devolve o conteúdo da variável HTTPS, inicializada

pelo servidor se a conexão é segura

– Invocado com um parâmetro, funciona de forma semelhante ao http, mas

para HTTPS_*

(28)

Notas sobre alguns métodos

• query_string

– Se o método for GET devolve a

query string

, incluindo quaisquer alterações

que entretanto lhe tenham sido feitas, ao contrário de

$ENV{QUERY_STRING}

– Se o método for POST, retorna os parâmetros POST enviados no corpo da

mensagem

• Neste caso, não inclui a query string (que normalmente não existe com o POST)

• self_url

– Devolve um URL que pode ser usado para invocar o CGI com o método GET

com os mesmos parâmetros com que foi invocado

• Ainda que tenha sido invocado com o método POST

• url

– Semelhante ao anterior, mas sem parâmetros (informação de caminho ou

query string

)

• virtual_host

– Retorna o conteúdo do cabeçalho ―Host:‖, se este existir (i.e., HTTP/1.1), ou

SERVER_NAME no caso contrário

(29)

Acesso aos parâmetros do formulário

• O acesso aos parâmetros é feito usando o método

param

– Independentemente de o formulário ter sido submetido por

GET ou POST 

• Invocado sem argumentos, param retorna a lista de

todos os parâmetros

• Invocado com o nome de um parâmetro, param devolve

– A lista de valores para esse parâmetro, se invocado em

contexto de lista

– O primeiro (ou único) valor, se invocado em contexto escalar

– undef se não existir esse parâmetro no formulário

(30)

Alteração dos parâmetros do formulário

• É possível alterar o valor de um parâmetro invocando

param com dois ou mais argumentos

• Também é possível apagar um parâmetro específico ou

todos os parâmetros

• Pode ser útil para definir valores-padrão para os

parâmetros de um formulário

$q->param(name => "John Doe");

$q->param(hobbies => "Biking", "Windsurfing", "Music");

$q->delete("age");

$q->delete_all;

(31)

Exportação de parâmetros para

um espaço de nomes

• Método param não permite a interpolação em

strings de forma simples

• Em alternativa é possível exportar todos os

parâmetros para variáveis de um dado espaço

de nomes

• Único senão: maior consumo de memória

$q->import_names("Q");

(32)

Submissão de ficheiros

• É possível efectuar a submissão de ficheiros usando formulários

com enctype=“multipart/form-data”

• O valor fornecido é o nome do ficheiro tal como aparecia no

servidor

– Os delimitadores de directórios são diferentes nos diferentes

sistemas operativos

– Os caracteres válidos num nome também

– Alguns browsers fazem tradução do nome

• O conteúdo é guardado num ficheiro temporário

– Pode obter-se um handle para este temporário usando o método

upload com o nome do ficheiro como argumento

– É possível obter o nome do ficheiro temporário usando o método

tmpFileName (método não-documentado, deve evitar-se)

• Pode pedir-se no formulário o nome com o qual o ficheiro deve

ser guardado no servidor

(33)

Prevenir ataques DoS

• Na configuração standard, o módulo CGI

permite upload de ficheiros e não limita

o tamanho de um POST

– Seria possível lançar um DoS que enchesse

o disco ou a memória do servidor

– Para evitar este problema, é conveniente

usar o seguinte código antes de criar o

objecto CGI:

$CGI::DISABLE_UPLOADS = 1;

(34)

Submissão de ficheiros

#!/usr/bin/perl -wT use strict;

use CGI;

use Fcntl qw( :DEFAULT :flock );

use constant UPLOAD_DIR => "/usr/local/apache/data/uploads"; use constant BUFFER_SIZE => 16_384;

use constant MAX_FILE_SIZE => 1_048_576; # Limit each upload to 1 MB

use constant MAX_DIR_SIZE => 100 * 1_048_576; # Limit total uploads to 100 MB

use constant MAX_OPEN_TRIES => 100;

$CGI::DISABLE_UPLOADS = 0;

$CGI::POST_MAX = MAX_FILE_SIZE; my $q = new CGI;

$q->cgi_error and error( $q, "Error transferring file: " . $q->cgi_error );

my $file = $q->param( "file" ) || error( $q, "No file received." ); my $filename = $q->param( "filename" ) || error( $q, "No filename entered." ); my $fh = $q->upload( $file );

my $buffer = "";

if ( dir_size( UPLOAD_DIR ) + $ENV{CONTENT_LENGTH} > MAX_DIR_SIZE ) { error( $q, "Upload directory is full." );

(35)

Submissão de ficheiros (cont.)

# Conversão do nome do ficheiro de destino

$filename =~ s/[^\w.-]/_/g;

if ( $filename =~ /^(\w[\w.-]*)/ ) { $filename = $1;

} else {

error( $q, "Invalid file name; files must start with a letter or number." ); }

# Abrir ficheiro de destino, garantindo que o nome é único

until ( sysopen OUTPUT, UPLOAD_DIR . $filename, O_WRONLY | O_CREAT | O_EXCL ) { $filename =~ s/(\d*)(\.\w+)?$/($1||0) + 1 . $2/e;

$1 + 0 >= MAX_OPEN_TRIES and error( $q, "Unable to save your file." ); }

# Necessário em sistemas não-Unix; não faz nada em sistemas Unix

binmode OUTPUT; binmode $fh;

# Copiar conteúdo do ficheiro temporário para o de destino por blocos

while ( read( $fh, $buffer, BUFFER_SIZE ) ) { print OUTPUT $buffer;

}

(36)

Submissão de ficheiros (cont.)

sub dir_size {

my $dir = shift; my $dir_size = 0;

# Somar o tamanho de todos os ficheiros no directório

opendir DIR, $dir or die "Unable to open $dir: $!"; while ( readdir DIR ) {

$dir_size += -s "$dir/$_"; } return $dir_size; } sub error { my( $q, $reason ) = @_;

print $q->header( "text/html" ), $q->start_html( "Error" ), $q->h1( "Error" ),

$q->p( "Your upload was not procesed because the following error ", "occured: " ),

$q->p( $q->i( $reason ) ), $q->end_html;

(37)

Submissão de ficheiros com

hook

#!/usr/bin/perl use CGI;

# Questões de segurança / prevenção de DoS omitidas

my $q = CGI->new(\&hook, undef, 0); # $hook, $data, $use_tempfile

sub hook { # Invocado antes de CGI->new() retornar => objecto $q não existe

my ($filename, $buffer, $bytes_read, $data) = @_; our $fh;

unless (defined($fh)) { open $fh, '>', $filename; binmode $fh; } print $fh $buffer;

}

print $q->header("text/html");

if (defined($fh)) { # Invocado pela submissão dum ficheiro

print $q->start_html("Upload finished!"), $q->h1("Well Done!"), $q->end_html; } else { # Invocado sem submissão => gerar formulário

print $q->start_html("Testing upload using hooks"), $q->start_multipart_form,

$q->filefield(-name => 'uploaded_file'), $q->submit, $q->end_form, $q->end_html;

(38)

Geração de saída com o CGI.pm

• O módulo CGI.pm simplifica

grandemente a geração de respostas

– Cabeçalhos HTTP

– Código HTML

(39)

Geração de cabeçalhos HTTP

print $q->header( "text/plain" );

print $q->header( -type => "text/plain" );

print $q->header( -type => "text/html", -expires => "+30m" );

print $q->header( -type => "text/html", -target => "main_frame" );

• Tipo de media

• Código de estado

• Expiração do documento gerado p/ caching

– Instante absoluto, relativo ou now

• Especificação do alvo (frame, janela)

(40)

Geração de cabeçalhos HTTP

• Redireccionamento

• Outros cabeçalhos

– Basta passar o par nome/valor ao método header;

os sublinhados são automaticamente convertidos

para hífenes

print $q->redirect( "http://localhost/survey/thanks.html" );

(41)

Geração de (X)HTML

• O método start_html gera a parte inicial do

documento, até à etiqueta <body> (inclusive)

• Algumas opções possíveis:

– Especificação de meta-informação com -meta

– Inclusão de script com –script

• Código do script numa string ou

• Referência a hash com chaves possíveis –language, -src,

ou –code

– Alternativa se o browser não suportar JavaScript

com -noscript

(42)

Geração de (X)HTML

• Algumas opções possíveis (cont.):

– Especificação de folha de estilos CSS com –style

• Código CSS numa string ou

• Referência a hash com chaves possíveis –code ou –src

– Título do documento com –title

– Especificação de URL-base como o URL do script

com –base e valor true

• Útil com sub-recursos

– Especificação de URL-base passado como

argumento seguinte com -xbase

(43)

Geração de (X)HTML

• Elementos HTML standard

– Linha horizontal

– Mudança de linha

– Parágrafo

• Sem espaço adicional

print $q->hr;

print $q->br;

print $q->p( "This is a paragraph." );

print $q->p( "The server name is:", $q->em( $q->server_name ) );

{

local $" = "";

print $q->p( "Server=", $q->server_name );

}

(44)

Geração de (X)HTML

• Elementos HTML standard (cont.)

– Âncora

(link)

– Listas

• Propriedade distributiva

print $q->a( { -href => "/downloads" }, "Download Area" );

print $q->ol( $q->li( [ "First", "Second", "Third" ] ) );

<ol>

<li>First</li>

<li>Second</li>

<li>Third</li>

</ol>

(45)

Geração de (X)HTML

• Elementos HTML standard (cont.)

– Tabelas

print $q->table( { -border => 1,

-width => "100%" }, $q->Tr( [ $q->th( { -bgcolor => "#cccccc" }, [ "Name", "Age" ] ), $q->td( [ "Mary", 29 ] ), $q->td( [ "Bill", 27 ] ), $q->td( [ "Sue", 26 ] ) ] ) );

<table border="1" width="100%"> <tr> <th bgcolor="#cccccc">Name</th> <th bgcolor="#cccccc">Age</th> </tr> <tr> <td>Mary</td> <td>29</td> </tr> <tr> <td>Bill</td> <td>27</td> </tr> <tr> <td>Sue</td> <td>26</td> </tr> </table>

(46)

Geração de formulários

Método CGI.pm

Etiqueta (X)HTML

start_form, start_multipart_form <form>

end_form

</form>

textfield

<input type="text">

password_field

<input type="password">

filefield

<input type="file">

button

<input type="button">

• A geração de formulários também é facilitada pelo

módulo CGI.pm

• A tabela mostra os métodos disponíveis, bem como as

etiquetas (X)HTML por eles geradas

(47)

Geração de formulários (cont.)

Método CGI.pm

Etiqueta (X)HTML

image_button

<input type="image">

submit

<input type="submit">

reset

<input type="reset">

checkbox, checkbox_group <input type="checkbox">

radio_group

<input type="radio">

popup_menu

<select size="1">

scrolling_list

<select size="n"> (n > 1)

textarea

<textarea>

(48)

Geração de formulários (cont.)

• O método HTTP normalmente usado para

submissão por start_form é o POST

– Possível especificar GET com –method => 'GET'

• Nos métodos que geram elementos do

formulário é possível especificar o

valor-padrão com a opção –default

– Se o CGI tiver sido invocado pela submissão de um

formulário com esse elemento, o valor submetido é

usado em vez do padrão

(49)

Formulários — Exemplo

use CGI; my $q = CGI->new(); print $q->header('text/html'), $q->start_html(-title => 'Register'), $q->h1('Registration form'), $q->start_form, $q->start_table, $q->Tr([

$q->td([ 'Name:', $q->textfield(-name => 'name') ]), $q->td([ 'Email:', $q->textfield(-name => 'email') ]),

$q->td([ 'Password:', $q->password_field(-name => 'pass') ]),

$q->td([ 'Confirm password:', $q->password_field(-name => 'cpass') ]), $q->td([ 'Receive newsletter:',

scalar ($q->radio_group(-name => 'rcvnl',

-values => [ 'yes', 'no' ], -default => 'no')) ]), ]), $q->end_table, $q->submit(-value => 'Register'), $q->end_form, $q->end_html;

(50)
(51)

Formulários — Exemplo 2

print $q->start_form;

print $q->table( { -border => 0, -width => 550 }, $q->Tr(

$q->td($q->textfield(-name => 'fname', -default => 'John', -size => 25), $q->br, "First Name"),

$q->td($q->textfield(-name => 'mi', -size => 2, -default => 'A'), $q->br, "M.I."),

$q->td($q->textfield(-name => 'lname', -default => 'Doe', -size => 25), $q->br, "Last Name")

), $q->Tr(

$q->td({ colspan => 3 }, $q->textfield(-name => 'address', -size => 75), $q->br, "Street Address")

), $q->Tr(

$q->td($q->textfield(-name => 'city', -size => 25), $q->br, "City"),

$q->td($q->textfield(-name => 'state', -size => 2), $q->br, "State"),

$q->td($q->textfield(-name => 'zip', -size => 10), $q->br, "Zip Code")

)

(52)

Formulários — Exemplo 2

print $q->table( { -border => 0, -width => 550 }, $q->Tr(

$q->td($q->em("What Operating Systems Do You Use?"), $q->br, $q->checkbox_group(

-name => 'Operating Systems',

-values => ['Linux', 'MacOS', 'Windows', 'Other'], -linebreak => 'yes',

-defaults => ['Linux', 'Windows'] )

), $q->td($q->em("What Platform is used most?"), $q->br, $q->radio_group(

-name => 'platform',

-values => ['PC', 'Mac', 'Sun', 'Other'], -linebreak => 'yes', -default => 'PC' ) ) ) ), $q->hr;

(53)

Formulários — Exemplo 2

print $q->p(

$q->em("How are you connected to the Internet?"), $q->br, $q->popup_menu(

-name => 'Connection',

-values => ['ADSL', 'Cable', 'T-1/E-1', 'Dial-up', 'Satellite'], -default => 'Dial-up'

) );

print $q->p(

$q->em("What Peripherals are connected to your computer?"), $q->br, $q->scrolling_list(

-name => 'configuration',

-values => ['CDROM', 'Sound Card', 'Video Camera', '3D Graphics'], -size => 4,

-multiple => 'true' )

);

print $q->p($q->em("What do you like about the World Wide Web?"), $q->br, $q->textarea(-name => 'Comments', -rows => 8, -columns => 60)); print $q->p($q->checkbox('Add me to your mailing list'));

(54)

Formulários — Exemplo 2

print $q->p( $q->reset,

$q->submit('Action', 'Send Free Catalog'), $q->submit('Action', 'No Free Catalog') );

print $q->endform, $q->hr;

# Imprime valores recebidos da submissao anterior

if ($q->param) {

my (@values, $key);

print $q->h2("Here are the current settings:"); foreach $key ($q->param) {

print $q->strong("$key : "); @values = $q->param($key);

print join(", ",@values), $q->br; }

print $q->hr; } else {

print $q->strong("No query submitted yet."); }

(55)

Tratamento de erros

• Um CGI não deve morrer, pois originaria um erro 500

Internal Server Error

– Um simples warn gera esse erro

• No entanto, existe muito código que invoca die, warn

ou funções relacionadas

• Pode-se colocar código ―perigoso‖ dentro de blocos

eval, mas não é muito prático

• Módulo CGI::Carp simplifica o tratamento de erros

– Adiciona informação (timestamp e nome do CGI) às

mensagens de erro

(56)

Tratamento de erros — CGI::Carp

• Parâmetro fatalsToBrowser para apanhar os erros e warnings

• Ajuda de valor incalculável na fase de desenvolvimento

• CGI::Carp::set_message para personalizar a página de erro

use CGI;

$CGI::HEADERS_ONCE = 1;

use CGI::Carp 'fatalsToBrowser'

;

BEGIN {

sub carp_error {

my $error_message = shift;

my $q = new CGI;

print $q->start_html( "Error" ),

$q->h1( "Error" ),

$q->p( "Sorry, the following error has occurred: " );

$q->p( $q->i( $error_message ) ),

$q->end_html;

}

CGI::Carp::set_message( \&carp_error );

}

(57)

Modularização do código

• Para simplificar a manutenção do código e a uniformidade do site é

conveniente criar código modular

• Criação de módulo

– Definir package com o nome do módulo — sequência de identificadores

separados por ::

– Dar ao ficheiro o nome do último desses identificadores e a extensão .pm

– Guardar esse ficheiro num subdirectório constituído pelos restantes

identificadores separados por / em vez de ::

– Este subdirectório deve estar num directório em @INC

– O ficheiro deve terminar com ―1;‖ para ser incluído com sucesso

– É comum definir a versão em $VERSION

• Exemplo: o módulo MyMods::CGI::Login deve pertencer a um

package com o mesmo nome e ser guardado no ficheiro Login.pm

dentro, e.g., do directório

(58)

Modularização do código: Exemplo

#!/usr/bin/perl -wT

package MyMods::CGI::Login; use CGI;

use base Exporter;

our @EXPORT = qw( login_form ); # Exporta a subrotina login_form()

our $VERSION = "0.1"; # Versão do módulo

sub login_form {

my $q = shift; # Objecto CGI passado como primeiro argumento

print $q->header(-type => 'text/html'), $q->start_html('Login'),

$q->start_form(-action => $scriptname), # Processado pelo próprio script

$q->p('Username:', $q->textfield(-name => 'user')),

$q->p('Password:', $q->password_field(-name => 'pass', -default => '', -override => 1)), $q->p($q->submit(-value => 'Login')), $q->end_form(), $q->end_html(); }

(59)

Manutenção de estado

• HTTP é

stateless

(mesmo com conexões permanentes)

• Por vezes é necessário manter estado entre diversos

ciclos pedido/resposta. Técnicas possíveis:

Técnica

Aplicação

Fiabilidade e

desempenho

Requisitos do

cliente

Query strings

Extra path info

Grupo de páginas ou site

inteiro; perde-se se o

utilizador deixar o site e

voltar mais tarde

Difícil interceptar de

forma fiável todos os

links; é pesado passar

conteúdos estáticos

através de CGIs

Nenhuns

Hidden fields Sequência de submissões

de formulários

Fácil implementação; não

afecta o desempenho

Nenhuns

Cookies

utilizador saia do site e

Sempre, mesmo que o

regresse mais tarde

Fácil implementação; não

afecta o desempenho

Suporte para

cookies

imple-mentado e activo

(60)

Query strings & extra path information

• Necessário configurar servidor web para invocar CGI quando se

acede a determinadas zonas do sistema de ficheiros

• Quando é feito um pedido de

http://example.com/store/index.html

o CGI (

query track

) é invocado como

http://example.com/cgi/track.cgi/store/index.html

• O script atribui ao utilizador um identificador único e altera todos

os

links

para incluir esse identificador

– E.g.,

http://example.com/store/.CC7e2BMb_H6UdK9KfPtR1g/faq.html

<Directory /usr/local/apache/htdocs/store>

AddType text/html .html

AddType Tracker .html

Action Tracker /cgi/track.cgi

</Directory>

(61)

Query strings & extra path information

• Este método implica a intercepção de todos os

links

nos

documentos estáticos servidos para introduzir o ID

• Pode utilizar-se o módulo HTML::Parser para o efeito

– Parsing é tarefa pesada  penalização em termos de desempenho

• Em alternativa podem pré-processar-se os documentos para

agilizar o processo (e.g., colocar em todos os

links

#SESSID# no

sítio onde deve introduzir-se o ID)

sub parse {

my( $filename, $id ) = @_;

local *FH;

open FH, $filename or die "Cannot open file: $!";

while (<FH>) {

s/#SESSID#/$id/g;

print;

}

}

(62)

Cookies

• Geração do cabeçalho ―Set-cookie:‖

• Recuperação de uma

cookie

recebida

• Problema: se houver várias

cookies

com o mesmo nome

apenas retorna a primeira

– Possível desde que tenham

path

ou

domain

distintos

– Podem recuperar-se as restantes através de

$ENV{HTTP_COOKIE}

my $cookie = $q->cookie( -name => 'cart_id',

-value => 12345,

-domain => '.oreilly.com',

-expires => '+1y',

-path => '/cgi',

-secure => 1 );

print $q->header( -type => 'text/html', -cookie => $cookie );

(63)

Cookies

• É possível detectar automaticamente se o

cliente tem suporte para

cookies

activo

• Se o CGI não receber a

cookie

pretendida

– Gera cabeçalho Set-cookie:

– Redirecciona para outro CGI que testa se a

instalação da

cookie

foi bem sucedida

• Este segundo CGI de teste pode redireccionar

outra vez para o CGI de entrada

(64)

Autenticação e manutenção de sessões

Cenário típico de acesso a áreas restritas:

• Utilizador autentica-se usando as suas credenciais

• É criada uma sessão associada ao utilizador

– Permite controlar autorização  permissões associadas ao

utilizador

• Sessão mantém-se activa até que até que

– O utilizador faça logout do site (sessão é apagada)

– Passe um certo período de tempo inactivo (sessão expira)

• Utilizador não precisa de reintroduzir credenciais

enquanto a sessão estiver activa

(65)

Autenticação e manutenção de sessões

Alguns aspectos básicos de segurança:

• Autenticação deve sempre ser feita sobre https

– Ou então usando a autenticação Digest do HTTP

• Em aplicações de elevado risco toda a comunicação

deve ser feita sobre https

– Evita que o identificador de sessão seja capturado na rede

• Após autenticação, o servidor deve sempre criar um

novo identificador de sessão

– Evita a fixação de sessões

– Evita reutilização de sessões antigas

(66)

Autenticação e manutenção de sessões

Alguns aspectos básicos de segurança:

• Sessão deve ter tempo de vida limitado

– Expirar após um período de inactividade

– Expiração garantida pelo servidor — nunca confiar no cliente

– Mecanismo de logout simples de utilizar para apagar sessão

• Identificador de sessão deve ter componente

aleatória

– Evita que se consiga adivinhar o identificador mesmo que se

conheça o algoritmo de geração

• Se possível, apenas um identificador por utilizador

(67)

Autenticação e manutenção de sessões

Alguns aspectos básicos de segurança:

• A aplicação NUNCA deve usar informação sensível ou

pessoal (e.g., username) como estado mantido pelo

cliente

– Seria muito fácil ao cliente forjar essa informação

– Servidor deve passar ao cliente apenas um identificador de

sessão opaco (e.g., gerado criptograficamente) e manter

localmente a informação associada a esse identificador

• A aplicação deve usar uniformemente um único

mecanismo de autenticação e manutenção de sessões

– De preferência um bem testado — não reinventar a roda, que

às vezes sai quadrada…

(68)

Autenticação e manutenção de sessões

Alguns aspectos básicos de segurança:

• NUNCA armazenar na base de dados

passwords

em

clear text

– Enorme problema caso alguém consiga explorar alguma falha

de segurança e aceder à base de dados

• Muitos utilizadores reutilizam

passwords

em diferentes sistemas

– Quando é necessário armazenar passwords, deve-se fazê-lo

sob a forma de um

hash

da concatenação da

password

com um

sal

(69)

Autenticação e manutenção de sessões:

exemplo

Problema

• Implementação de uma

área restrita

• Um utilizador não

auten-ticado é redireccionado

para uma página de login

• Após a autenticação

ini-cial, o utilizador deve

poder aceder à área

restrita sem ter que

reintroduzir as suas

credenciais

Implementação

• Nome de utilizador e

hash da senha

armaze-nados em BD

backend

• Sessão criada quando o

utilizador se autentica

• Sessões mantidas

tam-bém na BD

backend

• Identificadores de

ses-são transportados em

cookies

(70)

CGI::Session

• Módulo genérico para manutenção de sessões

• Sessões armazenadas em disco ou BD relacional

• Não inclui autenticação

– Pode ser usado quando a autenticação não é necessária

– Pode ser usado com autenticação HTTP ou outra

– Programador tem que se preocupar com isso 

• Permite múltiplas sessões por utilizador (  ou  )

• Necessita de limpeza periódica de sessões antigas

– Script CRON:

• Parâmetros da sessão guardados em bruto

– Mesmo em BD relacional não são guardados em colunas separadas

– Não é problemático se a sessão guardar pouca informação (e.g.,

apenas chaves para acesso à base de dados)

(71)

CGI::Session (com sessões na BD)

• Fragmento do script principal

# Objecto CGI já criado em $q e database handle em $dbh

# Tenta carregar sessão estabelecida

my $s = CGI::Session->load('driver:mysql', $q,

{ TableName => 'my_sessions',

Handle => $dbh})

or die CGI::Session->errstr();

if ($s->is_empty) {

# Sessão inexistente, inválida ou expirada

login_page;

# Gera página de entrada para o utilizador se autenticar

} else {

# Utilizador com sessão válida => acesso autorizado

my $user = $s->param('user');

# Parâmetro guardado na sessão

print $q->header('text/html'),

# Conteúdo altamente secreto :-)

$q->start_html,

$q->h1("Hello, $user!"),

$q->end_html;

(72)

CGI::Session (com sessões na BD)

• Fragmento do script de login

• Fragmento do script de logout

# Objecto CGI já criado em $q e database handle em $dbh

# Após verificação das crecedenciais, utilizador em $user

my $s = CGI::Session->new('driver:mysql', $q,

{ TableName => 'my_sessions',

Handle => $dbh})

or die CGI::Session->errstr();

$s->param('user', $user);

# Guarda parâmetro da sessão

print $s->header();

# Invoca $q->header() acrescentando-lhe uma cookie

com o identificador da sessão

# Sessão activa em $s

$s->delete;

(73)

Email

• Por vezes é útil enviar email a partir dum CGI

– Há milhentas maneiras de o fazer em Perl…

• Exemplo com Mime::Lite:TT

use MIME::Lite::TT;

my %params = ( name => 'Rui', day => 'next monday', time => '14:00' );

my $template = <<'

TEMPLATE

';

Hello, [% name %].

Our meeting will be [% day %] at [% time %].

TEMPLATE

my $msg = MIME::Lite::TT->new(

From => '[email protected]',

To => '[email protected]',

Subject => 'Meeting schedule',

Template => \$template,

TmplParams => \%params,

);

(74)

Problemas com o CGI

• Lançamento de novo processo (perl) por cada

invocação do CGI

• Carregamento e recompilação do CGI por cada

invocação

• Sem persistência

– Alguma persistência pode ser obtida através dos métodos

anteriormente descritos

– Informação de sessão tem que ser carregada por cada

invocação

– Ligações a bases de dados têm que ser restabelecidas por

cada invocação

• Possibilidades de interacção com o servidor web

reduzidas

(75)

Aceleração

• Estratégias básicas de aceleração

– Arrancar

pool

de processos perl que depois

são reutilizados

– Correr o código a vermelho apenas uma vez

e reutilizar os resultados

#!/usr/bin/perl –wT

use CGI;

use DBI;

$q = CGI->new;

$dbh = DBI->connect(...);

query_db();

print_response();

(76)

FastCGI

• Extensão ao CGI que proporciona maior velocidade,

escalabilidade e persistência

• Protocolo de comunicação entre servidor web e

aplicação FastCGI

• Bibliotecas que implementam a extensão e o protocolo

Servidor Web CGI

v.a. e stdin stdout stderr Pedido Resposta

Servidor Web FastCGI

Socket full duplex Protocolo FastCGI Pedido Resposta

CGI

FastCGI

(77)

FastCGI

• Um processo por aplicação

– Ou tantos quantos os pedidos que pode atender

concorrentemente

– Permite deslocar o

overhead

de carregamento e

compilação de módulos para o arranque

– Comunicação por sockets permite correr o script

numa máquina diferente do servidor web 

• Conversão de CGI em FastCGI

– Organizar a aplicação em inicialização e ciclo de

processamento

– Eliminar fugas de memória

(memory leaks)

(78)

FastCGI em Perl — FCGI e CGI::Fast

#!/usr/bin/perl –wT

use FCGI;

my $count = 0;

while (FCGI::accept() >= 0) {

print "Content-type: text/html\n\n ",

"<h1>Hello World</h1>\n",

"<p>Request ", ++$count, " from server ",

$ENV{'SERVER_NAME'}, "</p>";

}

#!/usr/bin/perl –wT

use CGI::Fast;

my $count = 0;

while (my $q = new CGI::Fast) {

print $q->header("text/html");

print $q->h1("Hello World");

print $q->p("Request " . ++$count . " from server " . $q->virtual_host);

(79)

mod_perl

• Extensão para embeber um interpretador perl em

cada sub-processo do servidor Apache

– Evita

overhead

de lançamento de novos processos

• Permite elevada interacção com o servidor

– É possível implementar módulos Apache em Perl

• O módulo mais popular é o Apache::Registry

– Permite implementar CGIs em mod_perl

– Cache de aplicações CGI compiladas

– Script de inicialização permite carregar módulos no arranque

do servidor

– Fornece uma boa emulação do ambiente CGI

• Necessárias algumas precauções adicionais…

(80)

Apache::Registry

• Persistência => intolerância a mau código

– O

script

deve ter uma ausência total de

memory leaks

– Inicialização das variáveis

• A sua falta leva a problemas ―interessantes‖ (i.e., difíceis de

descobrir)

– Usar use strict; e activar

warnings

ajuda a descobrir

problemas

• Mais alguns pormenores a ter em atenção

– Expressões regulares compiladas com /o ficam para todas as

invocações do script nesse processo

– Blocos BEGIN são executados apenas no arranque (mas blocos

END são executados por invocação)

– Os scripts não podem usar exit, pois causariam a terminação

do servidor web

(81)

Apache::PerlRun

A ausência de persistência implica que, face ao

Apache::Registry, o Apache::PerlRun é:

• Mais simples e tolerante a ―mau‖ código

– Memory leaks não são cumulativas

– Não ficam valores ―antigos‖ nas variáveis globais

– Não há problemas com expressões regulares compiladas com /o

• Menos eficiente

– Não há

caching

de código já compilado

• Necessário recompilar o

script

de cada vez que se corre

– Não podem ser reutilizadas conexões a bases de dados ou

outros recursos que demoram a obter

(82)

FastCGI vs. mod_perl

FastCGI

• Vantagens

– Configuração flexível

– Pode controlar-se com

ferramentas normais (ps,

kill, …)

– Não interfere (demasiado)

com o servidor

– Independente da linguagem e

do servidor

– Pode correr sem privilégios

especiais

• Inconvenientes

– Menos popular

– Documentação mais escassa

– Variedade de configurações

complica suporte

mod_perl

• Vantagens

– Integração estreita com o

servidor Apache

– Boa documentação e suporte

– Desenvolvimento activo

• Inconvenientes

– Perl em todos os

sub-processos

– Necessário recompilar

Apache

– Pode interferir com o

Apache

(83)

Mais informação

RFC-3875

— CGI version 1.1

• S. Guelich, S. Gundavaram e G.

Birznieks, ―CGI Programming with Perl,‖

2

nd

Edition. O’Reilly, 2000

• Documentação do módulo CGI.pm:

http://perldoc.perl.org/CGI.html

• Open Web Application Security Project

(OWASP):

http://www.owasp.org

Referências

Documentos relacionados

padrão de circulação em níveis médios da atmosfera apresenta uma confi guração similar a um bloqueio no oceano Atlântico, com um anticiclone intenso centrado aproximadamente sobre

Ficou claro na revisão da literatura que o desenvolvimento de produtos é um processo complexo, de natureza multidisciplinar e que exige uma estreita relação

Nesse sentido, medidas preventivas devem ser implementadas, para reduzir os casos do câncer, tais como: estratégias para o controle do tabagismo, que está

Apresentamos a formalização de alguns con- ceitos e operações em grafos, que f oram implementados no sistema Oyster- Clam, sendo de particular interesse a

O processo metodológico adotado para o presente estudo buscou alinhar os tópicos abordados na revisão bibliográfica e o procedimento a ser adotado para seleção das regiões de

(-) Impostos Sobre Venda Receita Líquida (-) Despesas Administrativas Margem de Contribuição EBITDA (-) Despesas Comerciais (-) Custos Variáveis (-) Despesa de Distribuição (-)

No caso em comento, a empresa requerida não apresentou sequer a minuta de proposta do seguro garantia judicial para que suas condições pudessem ser analisadas

A formação do mercado interno acontece de maneira desorientada e com inúmeras desigualdades regionais. A dinâmica distributiva da renda durante o processo de