Requisições Web  —  Tudo que você deve saber para começar em cybersegurança

20 hours ago 5
BOOK THIS SPACE FOR AD
ARTICLE AD

Marília Rocha

Disclaimer: Este artigo é baseado no módulo “Web Request” do HackTheBox, na seção Fundamentals. O conteúdo foi traduzido para português e adaptado.

Atualmente, a maioria das aplicações que utilizamos, seja em dispositivos móveis ou na web, se conecta continuamente à internet para funcionar corretamente. Grande parte dessas interações ocorre por meio de requisições web utilizando o protocolo HTTP.

O HTTP (HyperText Transfer Protocol) é um protocolo de nível de aplicação projetado para acessar e transferir recursos na World Wide Web. O termo “hipertexto” refere-se a textos que contêm links para outros recursos, facilitando a navegação e permitindo uma experiência mais fluida para o usuário.

A comunicação via HTTP segue o modelo cliente-servidor, no qual o cliente envia uma solicitação ao servidor, que a processa e responde com o recurso solicitado. Por padrão, o HTTP utiliza a porta 80, mas essa configuração pode ser alterada conforme as necessidades do servidor web.

Esse tipo de requisição é o mesmo que ocorre sempre que acessamos a internet para visitar sites. Inserimos um Nome de Domínio Completamente Qualificado (FQDN), como www.example.com, que atua como um Localizador Uniforme de Recursos (URL) para identificar o recurso desejado e permitir o acesso.

Arquitetura Cliente-Servidor

Os recursos acessados via HTTP são obtidos por meio de uma URL (Uniform Resource Locator), que vai além de simplesmente indicar o site desejado. Uma URL fornece detalhes adicionais, como o caminho específico do recurso, parâmetros de consulta, e até mesmo o protocolo utilizado, permitindo uma comunicação precisa entre cliente e servidor.

Estrutura de uma URL

O diagrama acima apresenta a anatomia de uma requisição HTTP de forma simplificada. Quando um usuário insere uma URL (por exemplo, exemplo.com) no navegador pela primeira vez, o navegador envia uma requisição ao servidor DNS (Sistema de Nomes de Domínio) para resolver o domínio e obter seu endereço IP correspondente.

O servidor DNS realiza a busca pelo endereço IP vinculado ao domínio solicitado (exemplo.com) e retorna essa informação ao navegador. Vale destacar que todos os nomes de domínio precisam passar por esse processo de resolução, pois a comunicação entre servidores depende exclusivamente de endereços IP, e não de nomes de domínio.

Disclaimer: Os navegadores, ao resolver um domínio, normalmente consultam primeiro os registros no arquivo local /etc/hosts. Caso o domínio solicitado não seja encontrado ali, o navegador recorre a servidores DNS externos para obter o endereço IP correspondente. O arquivo /etc/hosts pode ser utilizado para adicionar manualmente registros de DNS, permitindo que um domínio específico seja associado a um endereço IP definido. Basta inserir o endereço IP seguido pelo nome do domínio, facilitando testes locais ou redirecionamentos personalizados.

Quando o navegador obtém o endereço IP associado ao domínio solicitado, ele envia uma requisição GET para a porta HTTP padrão (geralmente a porta 80), solicitando o caminho raiz /. O servidor web recebe essa requisição e a processa.

Por padrão, os servidores são configurados para retornar um arquivo de índice quando uma solicitação é feita ao caminho raiz. Nesse caso, o servidor lê o conteúdo do arquivo index.html e o retorna como uma resposta HTTP. Além do conteúdo, a resposta inclui um código de status, como 200 OK, que indica que a requisição foi processada com sucesso. O navegador então renderiza o conteúdo do index.html e o exibe para o usuário.

O cURL (Client URL) é uma ferramenta de linha de comando poderosa e uma biblioteca versátil, amplamente utilizada para interagir com o protocolo HTTP e vários outros protocolos. Sua flexibilidade a torna ideal para scripts e automação, sendo essencial para enviar diferentes tipos de requisições web diretamente pelo terminal — uma habilidade indispensável em muitos testes de penetração.

Com o cURL, é possível enviar uma requisição HTTP básica para qualquer URL de forma simples, passando-a como argumento. Abaixo está um exemplo prático de como isso pode ser feito:

malwarilia@htb[/htb]$ curl inlanefreight.com<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
...SNIP...

Observamos que o cURL não renderiza o código HTML, JavaScript ou CSS como um navegador, mas exibe o conteúdo em seu formato bruto. No entanto, para pentesters, essa abordagem é vantajosa, pois nos permite focar no contexto das requisições e respostas de maneira mais rápida e conveniente do que usando um navegador.

Adicionalmente, o cURL pode ser utilizado para baixar uma página ou arquivo e salvar o conteúdo localmente. Para isso, utilizamos a flag -O, que salva o arquivo com o nome original do servidor. Se preferirmos especificar um nome diferente para o arquivo de saída, podemos usar a flag -o seguida pelo nome desejado. Abaixo está um exemplo que ilustra como fazer isso:

malwarilia@htb[/htb]$ curl -O inlanefreight.com/index.html
malwarilia@htb[/htb]$ ls
index.html

Como podemos observar, o resultado não foi impresso na tela desta vez, mas sim salvo no arquivo index.html. Notamos também que o cURL exibiu alguns status durante o processamento da requisição. Para suprimir esses status e obter uma saída mais limpa, podemos utilizar a flag -s, como demonstrado no exemplo a seguir:

malwarilia@htb[/htb]$ curl -s -O exemple.com/index.html

Desta vez, o cURL não imprimiu nada, pois o conteúdo foi salvo no arquivo index.html. Por fim, podemos usar a flag -h para ver outras opções disponíveis com o cURL:

malwarilia@htb[/htb]$ curl -h
Usage: curl [options...] <url>
-d, --data <data> HTTP POST data
-h, --help <category> Get help for commands
-i, --include Include protocol response headers in the output
-o, --output <file> Write to file instead of stdout
-O, --remote-name Write output to a file named as the remote file
-s, --silent Silent mode
-u, --user <user:password> Server user and password
-A, --user-agent <name> Send User-Agent <name> to server
-v, --verbose Make the operation more talkative

Esta não é uma lista completa de opções. O menu é organizado em categorias. Para obter uma visão geral de todas as categorias, utilize o comando -help category. Além disso, você pode consultar o manual do usuário com man curl ou a flag --help all para visualizar todas as opções disponíveis.

Como mencionado anteriormente, o comando --help all fornece um menu de ajuda detalhado, enquanto --help category (por exemplo, -h http) exibe ajuda específica para uma determinada flag. Se você precisar de documentação mais abrangente, pode usar man curl para acessar a página completa do manual do cURL.

Nas próximas seções, abordaremos a maioria das flags mencionadas e discutiremos em quais situações é apropriado utilizar cada uma delas.

Como vimos acima, uma das principais desvantagens do HTTP é que todos os dados são transmitidos em texto claro. Isso significa que qualquer pessoa interceptando a comunicação entre a origem e o destino pode realizar um ataque de Man-in-the-Middle (MiTM) e visualizar os dados transferidos.

Para mitigar esse risco, foi desenvolvido o protocolo HTTPS (HTTP Secure), que criptografa todas as comunicações. Dessa forma, mesmo que uma terceira parte consiga interceptar a requisição, não conseguirá acessar ou extrair os dados.

Ao analisarmos uma requisição HTTP, podemos identificar os riscos associados à falta de segurança nas comunicações entre um navegador web e uma aplicação web. Por exemplo, considere o seguinte conteúdo de uma requisição de login HTTP:

Podemos ver que as credenciais de login são transmitidas em texto claro. Isso facilitaria para alguém na mesma rede (como uma rede sem fio pública) capturar a requisição e reutilizar as credenciais para fins maliciosos. Em contraste, quando alguém intercepta e analisa o tráfego de uma requisição HTTPS, o que se vê é algo como:

Como podemos observar acima, os dados são transferidos em um único fluxo criptografado, dificultando significativamente a captura de informações, como credenciais e outros dados sensíveis, por qualquer pessoa.

Os sites que utilizam HTTPS podem ser identificados pelo prefixo https:// em suas URLs (por exemplo, https://www.google.com), assim como pelo ícone de cadeado na barra de endereços do navegador, à esquerda da URL:

Portanto, ao visitar um site que utiliza HTTPS, como o Google, todo o tráfego será criptografado.

Disclaimer: Embora os dados transferidos pelo protocolo HTTPS estejam criptografados, a requisição ainda pode revelar a URL acessada se for feita uma consulta a um servidor DNS em texto claro. Por essa razão, recomenda-se utilizar servidores DNS criptografados (como 8.8.8.8 ou 1.1.1.1) ou um serviço de VPN para garantir que todo o tráfego permaneça protegido e devidamente criptografado.

Ao digitar http:// em vez de https:// ao visitar um site que utiliza HTTPS, o navegador tenta resolver o domínio e redireciona o usuário para o servidor web que hospeda o site. Inicialmente, uma requisição é enviada para a porta 80, que utiliza o protocolo HTTP não criptografado. O servidor, ao detectar isso, redireciona o cliente para a porta 443, que é a porta segura do HTTPS. Esse redirecionamento é realizado por meio do código de resposta 301 Moved Permanently.

Em seguida, o cliente (navegador) envia um pacote chamado “client hello”, fornecendo informações sobre si mesmo. O servidor responde com um “server hello”, seguido pela troca de chaves, onde os certificados SSL são trocados. O cliente verifica o certificado do servidor e envia o seu próprio. Após essa etapa, inicia-se uma troca de chaves criptografadas para confirmar que a criptografia e a transferência de dados estão funcionando corretamente.

Uma vez que o handshake é concluído com sucesso, a comunicação HTTP normal prossegue, agora criptografada. Esta é uma visão geral simplificada do processo de troca de chaves, que está além do escopo deste módulo.

Importante: Em algumas situações, um atacante pode ser capaz de realizar um ataque de downgrade de HTTP, rebaixando a comunicação de HTTPS para HTTP e fazendo com que os dados sejam transferidos em texto claro. Isso pode ocorrer através da configuração de um proxy Man-in-the-Middle (MiTM) que intercepta todo o tráfego, sem que o usuário perceba. No entanto, a maioria dos navegadores modernos, servidores e aplicações web implementa medidas de proteção contra esse tipo de ataque.

O cURL deve gerenciar automaticamente todos os padrões de comunicação HTTPS, realizando um handshake (processo de inicialização de uma comunicação segura entre duas partes, como um cliente e um servidor) e, em seguida, criptografando e descriptografando os dados de forma automática.

No entanto, se tentarmos acessar um site com um certificado SSL inválido ou desatualizado, o cURL, por padrão, não prosseguirá com a comunicação para se proteger contra os ataques Man-in-the-Middle (MITM) mencionados anteriormente:

malwarilia@htb[/htb]$ curl https://inlanefreight.comcurl: (60) SSL certificate problem: Invalid certificate chain
More details here: https://curl.haxx.se/docs/sslcerts.html
...SNIP...

Os navegadores web modernos fazem o mesmo, alertando o usuário sobre a visita a um site com um certificado SSL inválido.

Podemos enfrentar esse problema ao testar uma aplicação web local ou uma aplicação web hospedada para fins de prática, pois essas aplicações podem ainda não ter implementado um certificado SSL válido. Para ignorar a verificação do certificado com o cURL, podemos usar a flag -k:

malwarilia@htb[/htb]$ curl -k https://inlanefreight.com<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
...SNIP...

As comunicações HTTP são compostas basicamente por uma requisição HTTP e uma resposta HTTP. A requisição é enviada pelo cliente, que pode ser uma ferramenta como cURL ou um navegador, e é processada pelo servidor, como um servidor web. Essa requisição inclui todos os detalhes necessários que queremos do servidor, como o recurso solicitado (por exemplo, URL, caminho e parâmetros), dados da requisição, cabeçalhos e outras opções que exploraremos ao longo deste módulo.

Após receber a requisição, o servidor a processa e responde com uma resposta HTTP. Esta resposta contém um código de status, que será abordado em uma seção futura, além de incluir os dados do recurso solicitado, caso o cliente tenha a devida autorização para acessá-lo.

Vamos começar analisando o seguinte exemplo de requisição HTTP:

A imagem acima mostra uma requisição HTTP GET para a URL:

http://inlanefreight.com/users/login.html

A primeira linha de qualquer requisição HTTP contém três campos principais, separados por espaços:

O próximo conjunto de linhas contém pares de valores de cabeçalho HTTP, como Host, User-Agent, Cookie e muitos outros cabeçalhos. Esses cabeçalhos são utilizados para especificar diversos atributos de uma requisição. Cada cabeçalho é finalizado com uma nova linha, o que é essencial para que o servidor possa validar a requisição. Além disso, uma requisição pode incluir um corpo contendo os dados adicionais.

Nota: A versão HTTP 1.X transmite requisições em texto claro, utilizando um caractere de nova linha para separar diferentes campos e requisições. Em contrapartida, a versão HTTP 2.X envia requisições como dados binários em formato de dicionário.HTTP Response

Uma vez que o servidor processa nossa requisição, ele envia sua resposta. A seguir, está um exemplo de resposta HTTP:

A primeira linha de uma resposta HTTP contém dois campos separados por espaços: o primeiro campo indica a versão do HTTP (por exemplo, HTTP/1.1), e o segundo representa o código de resposta HTTP (por exemplo, 200 OK).

Os códigos de resposta são utilizados para determinar o status da requisição, assunto que será discutido em uma seção posterior. Em seguida, a resposta lista seus cabeçalhos, de maneira semelhante às requisições HTTP. Os cabeçalhos de requisição e resposta serão explorados na próxima seção.

Por fim, a resposta pode incluir um corpo, que é separado por uma nova linha após os cabeçalhos. O corpo da resposta geralmente é formado por código HTML, mas também pode conter outros tipos de conteúdo, como JSON, recursos da web (como imagens, folhas de estilo ou scripts) ou documentos (como um PDF hospedado no servidor web).

Em nossos exemplos anteriores com o cURL, especificamos apenas a URL e recebemos o corpo da resposta em troca. No entanto, o cURL também permite visualizar a requisição HTTP completa e a resposta HTTP, o que pode ser extremamente útil ao realizar pentests ou desenvolver exploits. Para exibir tanto a requisição quanto a resposta HTTP completas, basta adicionar a flag -v (verbose) aos comandos anteriores. Isso resultará na impressão detalhada de ambas as informações:

malwarilia@htb[/htb]$ curl inlanefreight.com -v* Trying SERVER_IP:80...
* TCP_NODELAY set
* Connected to inlanefreight.com (SERVER_IP) port 80 (#0)
> GET / HTTP/1.1
> Host: inlanefreight.com
> User-Agent: curl/7.65.3
> Accept: */*
> Connection: close
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401 Unauthorized
< Date: Tue, 21 Jul 2020 05:20:15 GMT
< Server: Apache/X.Y.ZZ (Ubuntu)
< WWW-Authenticate: Basic realm="Restricted Content"
< Content-Length: 464
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
...SNIP...

Como podemos observar, desta vez recebemos a requisição e a resposta HTTP completas. A requisição enviada foi GET / HTTP/1.1, acompanhada pelos cabeçalhos Host, User-Agent e Accept. Em resposta, a mensagem HTTP retornou HTTP/1.1 401 Unauthorized, indicando que não temos permissão para acessar o recurso solicitado, conforme discutiremos em uma seção posterior. Assim como na requisição, a resposta também incluiu diversos cabeçalhos enviados pelo servidor, como Date, Content-Length e Content-Type. Por fim, o corpo da resposta, que estava em HTML, foi o mesmo que recebemos anteriormente ao usar o cURL sem a flag -v.

A maioria dos navegadores modernos vem equipada com ferramentas de desenvolvedor integradas (DevTools), projetadas principalmente para ajudar desenvolvedores a testar suas aplicações web. No entanto, para pentesters, essas ferramentas representam um recurso vital em qualquer avaliação web, uma vez que um navegador e suas DevTools são ferramentas comuns disponíveis em todos os exercícios de avaliação.

Ao visitar um site ou acessar uma aplicação web, nosso navegador envia várias requisições e gerencia múltiplas respostas HTTP para renderizar a visualização final na janela do navegador.

Para abrir as ferramentas de desenvolvedor, tanto no Chrome quanto no Firefox, podemos pressionar [CTRL+SHIFT+I] ou simplesmente clicar em [F12]. As DevTools possuem várias abas, cada uma com uma finalidade específica. Neste módulo, focaremos principalmente na aba [Network], que é responsável pelo gerenciamento das requisições web.

Ao clicar na aba [Network] e atualizar a página, você deverá conseguir visualizar a lista de requisições enviadas pela página:

As ferramentas de desenvolvedor nos permitem visualizar rapidamente o status da resposta, incluindo o código de resposta, o método da requisição utilizado (GET), o recurso solicitado (ou seja, a URL/domínio) e o caminho requisitado. Além disso, podemos utilizar o filtro de URLs para localizar uma requisição específica, o que é especialmente útil em sites que carregam um grande número de requisições, facilitando a análise.

Acima, apresentamos exemplos de Headers de requisições e respostas HTTP, que são fundamentais para a troca de informações entre o cliente e o servidor. Alguns Headers são específicos para requisições ou respostas, enquanto outros, conhecidos como cabeçalhos gerais, podem ser utilizados em ambos os contextos.

Cada Header consiste em um nome seguido de um ou mais valores, separados por dois-pontos. Esses cabeçalhos podem ser classificados nas seguintes categorias:

General HeadersEntity HeadersRequest HeadersResponse HeadersSecurity Headers

Agora, vamos explorar cada uma dessas categorias em detalhes:

Os cabeçalhos gerais são utilizados tanto em requisições quanto em respostas HTTP. Eles oferecem contexto sobre a mensagem como um todo, em vez de se concentrarem nos detalhes do conteúdo.

Assim como os General Headers, os cabeçalhos de entidade podem ser utilizados tanto em requisições quanto em respostas. Esses cabeçalhos têm a função de descrever o conteúdo (entidade) transmitido por uma mensagem. Eles são frequentemente encontrados em respostas e em requisições do tipo POST ou PUT.

O cliente envia cabeçalhos de requisição durante uma transação HTTP. Esses cabeçalhos são utilizados na requisição e não estão diretamente relacionados ao conteúdo da mensagem. Abaixo, apresentamos alguns dos cabeçalhos mais comuns encontrados em requisições HTTP.

Response Headers são utilizados em uma resposta HTTP e não estão relacionados ao conteúdo da mensagem. Certos cabeçalhos de resposta, como Age, Location e Server, fornecem informações adicionais sobre a resposta. Abaixo, apresentamos alguns dos cabeçalhos mais comuns encontrados em respostas HTTP.

Por fim, temos os Security Headers. Com a crescente diversidade de navegadores e o aumento das ameaças baseadas na web, tornou-se essencial estabelecer cabeçalhos que melhorem a segurança. Os cabeçalhos de segurança HTTP são uma categoria de response headers que definem regras e políticas que o navegador deve seguir ao acessar um site.

Na seção anterior, exploramos como utilizar a opção -v com o cURL para exibir todos os detalhes da requisição e resposta HTTP.

Caso queiramos visualizar apenas os cabeçalhos da resposta, podemos utilizar a opção -I, que envia uma requisição HEAD e exibe exclusivamente os cabeçalhos.

Por outro lado, a opção -i permite mostrar tanto os cabeçalhos quanto o corpo da resposta (como, por exemplo, o código HTML). A principal diferença entre essas duas opções é que -I envia uma requisição HEAD (conforme veremos na próxima seção), enquanto -i envia qualquer requisição especificada e imprime os cabeçalhos junto com a resposta.

O comando a seguir exemplifica a saída ao utilizar a opção -I:

malwarilia@htb[/htb]$ curl -I https://www.inlanefreight.comHost: www.inlanefreight.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko)
Cookie: cookie1=298zf09hf012fh2; cookie2=u32t4o3tb3gg4
Accept: text/plain
Referer: https://www.inlanefreight.com/
Authorization: BASIC cGFzc3dvcmQK
Date: Sun, 06 Aug 2020 08:49:37 GMT
Connection: keep-alive
Content-Length: 26012
Content-Type: text/html; charset=ISO-8859-4
Content-Encoding: gzip
Server: Apache/2.2.14 (Win32)
Set-Cookie: name1=value1,name2=value2; Expires=Wed, 09 Jun 2021 10:18:14 GMT
WWW-Authenticate: BASIC realm="localhost"
Content-Security-Policy: script-src 'self'
Strict-Transport-Security: max-age=31536000
Referrer-Policy: origin

Além de visualizar cabeçalhos, o cURL também nos permite definir cabeçalhos de requisição usando a opção -H, como veremos em uma seção posterior. Alguns cabeçalhos, como os cabeçalhos User-Agent e Cookie, possuem suas próprias opções. Por exemplo, podemos usar a opção -A para definir o nosso User-Agent, da seguinte forma:

malwarilia@htb[/htb]$ curl https://www.inlanefreight.com -A 'Mozilla/5.0'<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
...SNIP...

Por fim, vamos explorar como visualizar os cabeçalhos HTTP utilizando as ferramentas de desenvolvedor do navegador. Assim como na seção anterior, podemos acessar a aba Network para examinar as diversas requisições feitas pela página. Ao clicar em qualquer uma das requisições, teremos acesso a detalhes abrangentes sobre ela:

Na primeira aba Headers, temos a possibilidade de visualizar tanto os cabeçalhos da requisição HTTP quanto os da resposta HTTP. As ferramentas de desenvolvedor organizam esses Headers automaticamente em seções, mas podemos clicar no botão Raw para visualizar os detalhes em seu formato original.

Além disso, podemos acessar a aba Cookies para verificar quais cookies foram utilizados na requisição.

O HTTP suporta múltiplos métodos para acessar recursos. Esses métodos de requisição permitem que o navegador envie informações, formulários ou arquivos para o servidor, indicando como a requisição deve ser processada e qual deve ser a resposta esperada.

Na seção anterior, exploramos diferentes métodos HTTP utilizados nas requisições que testamos. Ao utilizar o cURL com a opção -v para visualizar a requisição completa, a primeira linha revela o método HTTP (por exemplo, GET / HTTP/1.1). Nas ferramentas de desenvolvedor do navegador, o método HTTP é exibido na coluna Método. Além disso, os cabeçalhos de resposta incluem o código de resposta HTTP, que indica o status do processamento da nossa requisição.

Os métodos de requisição (Request Methods) no protocolo HTTP definem as ações que o cliente (como um navegador ou uma ferramenta de linha de comando, como cURL) deseja realizar em um recurso específico no servidor. Cada método tem um propósito distinto e semântico, permitindo que o servidor entenda o que o cliente deseja fazer. Aqui estão alguns dos métodos mais comuns:

Os códigos de status HTTP são utilizados para informar ao cliente o status de sua requisição. Um servidor HTTP pode retornar cinco tipos de códigos de resposta:

A seguir, apresentamos alguns exemplos comuns de cada um dos tipos de métodos HTTP mencionados acima:

Disclaimer: Para uma representação visual, fofa e divertida dos códigos de status HTTP, recomendo visitar http.cat. Ele oferece imagens que correspondem a cada código de status HTTP com gatinhos, tornando a compreensão dos erros e respostas muito mais legais!

http.cat sobre A resposta HTTP 400 que é um código de status que indica “Bad Request” (Requisição Inválida).

Sempre que acessamos uma URL, nossos navegadores realizam uma requisição GET por padrão para obter os recursos remotos hospedados nesse endereço. Após receber a página inicial solicitada, o navegador pode enviar outras requisições utilizando diversos métodos HTTP. Essa atividade pode ser observada na aba Rede das ferramentas de desenvolvedor do navegador, conforme discutido na seção anterior.

HTTP Basic Authentication é um método simples de autenticação utilizado para garantir que apenas usuários autorizados possam acessar recursos em um servidor web.

Ao contrário dos formulários de login habituais, que utilizam parâmetros HTTP para validar as credenciais do usuário (por exemplo, uma requisição POST), esse tipo de autenticação utiliza uma autenticação HTTP básica, que é gerenciada diretamente pelo servidor web para proteger uma página ou diretório específico, sem interagir diretamente com a aplicação web.

Para acessar a página, precisamos inserir um par de credenciais válidas, que, neste caso, são admin

Assim que inserimos as credenciais, conseguimos acessar a página:

Vamos tentar acessar a página com o cURL e adicionar a opção -i para visualizar os cabeçalhos de resposta:

malwarilia@htb[/htb]$ curl -i http://<SERVER_IP>:<PORT>/
HTTP/1.1 401 Authorization Required
Date: Mon, 21 Feb 2022 13:11:46 GMT
Server: Apache/2.4.41 (Ubuntu)
Cache-Control: no-cache, must-revalidate, max-age=0
WWW-Authenticate: Basic realm="Access denied"
Content-Length: 13
Content-Type: text/html; charset=UTF-8
Access denied

Como podemos observar, recebemos 401 Authorization Required no corpo da resposta, e também vemos WWW-Authenticate: Basic realm="Access denied" no cabeçalho, o que confirma que esta página realmente utiliza autenticação HTTP básica, conforme discutido na seção de Cabeçalhos. Para fornecer as credenciais por meio do cURL, podemos usar a opção -u, da seguinte forma:

malwarilia@htb[/htb]$ curl -u admin:admin http://<SERVER_IP>:<PORT>/<!DOCTYPE html>
<html lang="en">
<head>
...SNIP...

Desta vez, conseguimos obter a página na resposta. Existe outro método para fornecer as credenciais de autenticação HTTP básica, que é diretamente através da URL, no formato (username:password@URL), como discutimos na primeira seção. Se tentarmos o mesmo com o cURL ou nosso navegador, também teremos acesso à página:

malwarilia@htb[/htb]$ curl http://admin:admin@<SERVER_IP>:<PORT>/<!DOCTYPE html>
<html lang="en">
<head>
...SNIP...

Podemos também tentar acessar a mesma URL em um navegador, e devemos ser autenticados com sucesso.

Se adicionarmos a opção -v a qualquer um dos comandos cURL que usamos anteriormente:

malwarilia@htb[/htb]$ curl -v http://admin:admin@<SERVER_IP>:<PORT>/* Trying <SERVER_IP>:<PORT>...
* Connected to <SERVER_IP> (<SERVER_IP>) port PORT (#0)
* Server auth using Basic with user 'admin'
> GET / HTTP/1.1
> Host: <SERVER_IP>
> Authorization: Basic YWRtaW46YWRtaW4=
> User-Agent: curl/7.77.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 21 Feb 2022 13:19:57 GMT
< Server: Apache/2.4.41 (Ubuntu)
< Cache-Control: no-store, no-cache, must-revalidate
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Pragma: no-cache
< Vary: Accept-Encoding
< Content-Length: 1453
< Content-Type: text/html; charset=UTF-8
<
<!DOCTYPE html>
<html lang="en">
<head>
...SNIP...

Como estamos utilizando basic HTTP auth, observamos que nossa requisição HTTP define o cabeçalho Authorization como Basic YWRtaW46YWRtaW4=, que é o valor codificado em base64 de admin:admin.

Se estivéssemos empregando um método moderno de autenticação (por exemplo, JWT), o cabeçalho Authorization seria do tipo Bearer e conteria um token criptografado mais longo.

Vamos tentar definir manualmente o Authorization, sem fornecer as credenciais, para verificar se conseguimos acessar a página. Podemos definir o cabeçalho com a opção -H, utilizando o mesmo valor da requisição HTTP mencionada anteriormente.

É possível adicionar a opção -H várias vezes para especificar múltiplos cabeçalhos:

malwarilia@htb[/htb]$ curl -H 'Authorization: Basic YWRtaW46YWRtaW4=' http://<SERVER_IP>:<PORT>/<!DOCTYPE html
<html lang="en">
<head>
...SNIP...

Como podemos ver, isso também nos proporcionou acesso à página. Esses são apenas alguns dos métodos que podemos utilizar para autenticar na aplicação. A maioria das aplicações web modernas implementa formulários de login desenvolvidos com uma linguagem de script do lado do servidor (como PHP), que empregam requisições HTTP POST para autenticar os usuários. Após a autenticação, essas aplicações geralmente retornam um cookie para manter a sessão do usuário.

Uma vez autenticados, temos acesso à funcionalidade City Search, onde podemos inserir um termo de pesquisa e obter uma lista de cidades correspondentes:

À medida que a página retorna os resultados, ela pode estar se conectando a um recurso remoto para obter as informações, exibindo-as em seguida. Para verificar isso, podemos abrir as ferramentas de desenvolvedor do navegador e acessar a aba de Rede, ou usar o atalho [CTRL+SHIFT+E] para navegar diretamente até essa aba. Antes de inserirmos nosso termo de busca e visualizarmos as requisições, é recomendável clicar no ícone de lixeira no canto superior esquerdo para limpar quaisquer requisições anteriores e monitorar apenas as novas requisições:

Depois disso, podemos inserir qualquer termo de busca e pressionar Enter. Imediatamente, notaremos uma nova requisição sendo enviada para o back-end:

Ao clicarmos na requisição, ela é enviada para search.php com o parâmetro GET search=le incluído na URL. Isso nos ajuda a entender que a função de busca solicita outra página para obter os resultados.

Agora, podemos enviar a mesma requisição diretamente para search.php para obter os resultados completos da busca, embora seja provável que eles sejam retornados em um formato específico (como JSON), sem o layout HTML exibido na captura de tela anterior.

Para enviar uma requisição GET com cURL, podemos usar a mesma URL que vimos nas capturas de tela anteriores, uma vez que as requisições GET incluem seus parâmetros na URL. No entanto, as ferramentas de desenvolvedor do navegador oferecem um método mais conveniente para obter o comando cURL. Podemos clicar com o botão direito na requisição e selecionar Copy > Copy as cURL. Em seguida, podemos colar o comando copiado no nosso terminal e executá-lo, e devemos receber exatamente a mesma resposta:

malwarilia@htb[/htb]$ curl 'http://<SERVER_IP>:<PORT>/search.php?search=le' -H 'Authorization: Basic YWRtaW46YWRtaW4='Leeds (UK)
Leicester (UK)

Observação: O comando copiado incluirá todos os cabeçalhos utilizados na requisição HTTP. No entanto, podemos simplificar o comando removendo a maioria desses cabeçalhos e mantendo apenas os essenciais, como o cabeçalho Authorization.

Também é possível repetir a mesma requisição diretamente nas ferramentas de desenvolvedor do navegador, selecionando Copiar > Copiar como Fetch. Isso irá copiar a requisição HTTP utilizando a biblioteca Fetch do JavaScript. Em seguida, podemos acessar a aba do console JavaScript pressionando [CTRL+SHIFT+K], colar nosso comando Fetch e pressionar Enter para enviar a requisição:

Como podemos ver, o navegador enviou nossa requisição e recebemos a resposta em seguida. Podemos clicar na resposta para visualizar seus detalhes, expandir várias informações e lê-las.

Na seção anterior, vimos como as requisições GET podem ser utilizadas por aplicações web para funcionalidades como busca e acesso a páginas. No entanto, quando as aplicações web precisam transferir arquivos ou enviar parâmetros do usuário que não podem ser incluídos na URL, elas utilizam requisições POST.

Diferentemente do GET, que insere os parâmetros do usuário na URL, o POST coloca esses parâmetros dentro do corpo da requisição HTTP. Isso traz três benefícios principais:

Ausência de Registro: Como as requisições POST podem transferir arquivos grandes (como uploads), não seria eficiente para o servidor registrar todos os arquivos enviados como parte da URL requisitada, ao contrário do que ocorreria com um arquivo enviado por meio de uma requisição GET.

Menos Requisitos de Codificação: As URLs são projetadas para serem compartilhadas, o que significa que precisam estar em conformidade com caracteres que podem ser convertidos em letras. A requisição POST coloca os dados no corpo da mensagem, que pode aceitar dados binários. Os únicos caracteres que precisam ser codificados são aqueles usados para separar os parâmetros.

Maior Volume de Dados: O comprimento máximo de uma URL varia entre navegadores (Chrome/Firefox/IE), servidores web (IIS, Apache, nginx), Redes de Entrega de Conteúdo (Fastly, Cloudfront, Cloudflare) e até mesmo encurtadores de URL (bit.ly, amzn.to). De modo geral, o comprimento de uma URL deve ser mantido abaixo de 2.000 caracteres, o que limita a quantidade de dados que podem ser enviados.

Agora, vamos ver alguns exemplos de como as requisições POST funcionam e como podemos utilizar ferramentas como cURL ou as DevTools do navegador para ler e enviar requisições POST.

CRUD API refere-se a um conjunto de operações básicas que podem ser realizadas em um sistema de gerenciamento de dados por meio de uma interface de programação de aplicativos (API). CRUD é um acrônimo que representa quatro operações fundamentais:

Create (Criar): Este método é utilizado para adicionar novos recursos ou dados ao sistema. Na prática, isso geralmente corresponde a uma requisição HTTP POST, onde os dados são enviados ao servidor para serem armazenados.Read (Ler): Essa operação é usada para recuperar informações ou dados do sistema. Normalmente, isso se traduz em requisições HTTP GET, onde o cliente solicita dados específicos ao servidor.Update (Atualizar): Este método permite modificar dados existentes no sistema. Em uma API, isso geralmente é implementado por meio de requisições HTTP PUT ou PATCH, onde o cliente envia as informações atualizadas para o servidor.Delete (Excluir): Esta operação é usada para remover dados do sistema. Geralmente, isso corresponde a uma requisição HTTP DELETE, que informa ao servidor que um determinado recurso deve ser excluído.

Suponha que estamos trabalhando com uma API para gerenciar uma lista de tarefas. Aqui estão exemplos de como cada operação CRUD pode ser realizada:

Create: Enviar uma requisição POST para /tasks com os dados da nova tarefa, como título e descrição.Read: Enviar uma requisição GET para /tasks para obter a lista de todas as tarefas ou /tasks/{id} para obter uma tarefa específica.Update: Enviar uma requisição PUT ou PATCH para /tasks/{id} com os novos dados da tarefa que você deseja atualizar.Delete: Enviar uma requisição DELETE para /tasks/{id} para remover a tarefa correspondente.

Existem vários tipos de APIs. Muitas APIs são utilizadas para interagir com um banco de dados, permitindo que especifiquemos a tabela solicitada e a linha desejada em nossa consulta à API, utilizando um método HTTP para realizar a operação necessária. Por exemplo, para o endpoint api.php em nosso exemplo, se quisermos atualizar a tabela de cidades no banco de dados e a linha que estamos atualizando tiver o nome da cidade como “London”, a URL ficaria mais ou menos assim:

curl -X PUT http://<SERVER_IP>:<PORT>/api.php/city/london ...SNIP...

Como podemos ver, é fácil especificar a tabela e a linha sobre a qual queremos realizar uma operação por meio de APIs desse tipo. Em seguida, podemos utilizar diferentes métodos HTTP para executar diversas operações nessa linha. De maneira geral, as APIs realizam quatro operações principais sobre a entidade de banco de dados solicitada:

Essas quatro operações estão principalmente relacionadas às APIs CRUD, mas o mesmo princípio também é aplicado em APIs REST e em vários outros tipos de APIs. É importante notar que nem todas as APIs funcionam da mesma forma, e o controle de acesso do usuário pode limitar quais ações podemos realizar e quais resultados podemos visualizar. O módulo Introdução a Aplicações Web explica esses conceitos com mais detalhes, então você pode consultá-lo para obter mais informações sobre APIs e seu uso.

A primeira coisa que faremos ao interagir com uma API é ler dados. Como mencionado anteriormente, podemos simplesmente especificar o nome da tabela após a API (por exemplo, /city) e, em seguida, indicar nosso termo de busca (por exemplo, /london), da seguinte forma:

malwarilia@htb[/htb]$ curl http://<SERVER_IP>:<PORT>/api.php/city/london[{"city_name":"London","country_name":"(UK)"}]

Podemos ver que o resultado é enviado como uma string JSON. Para que ele seja formatado corretamente em JSON, podemos direcionar a saída para a ferramenta jq, que irá formatá-lo de forma adequada. Também vamos silenciar qualquer saída desnecessária do cURL usando a opção -s, conforme mostrado a seguir:

malwarilia@htb[/htb]$ curl -s http://<SERVER_IP>:<PORT>/api.php/city/london | jq[
{
"city_name": "London",
"country_name": "(UK)"
}
]

Como podemos ver, recebemos a saída em um formato bem organizado. Também podemos fornecer um termo de pesquisa e obter todos os resultados correspondentes:

alwarilia@htb[/htb]$ curl -s http://<SERVER_IP>:<PORT>/api.php/city/le | jq[
{
"city_name": "Leeds",
"country_name": "(UK)"
},
{
"city_name": "Dudley",
"country_name": "(UK)"
},
{
"city_name": "Leicester",
"country_name": "(UK)"
},
...SNIP...
]

Por fim, podemos passar uma string vazia para recuperar todas as entradas na tabela:

malwarilia@htb[/htb]$ curl -s http://<SERVER_IP>:<PORT>/api.php/city/ | jq[
{
"city_name": "London",
"country_name": "(UK)"
},
{
"city_name": "Birmingham",
"country_name": "(UK)"
},
{
"city_name": "Leeds",
"country_name": "(UK)"
},
...SNIP...
]

Tente visitar qualquer um dos links acima usando seu navegador para ver como o resultado é exibido.

Para adicionar uma nova entrada, podemos utilizar uma solicitação HTTP POST, que é bastante semelhante ao que realizamos na seção anterior. Podemos simplesmente enviar nossos dados em formato JSON, e eles serão adicionados à tabela. Como esta API utiliza dados em JSON, também definiremos o cabeçalho Content-Type como JSON, conforme segue:

malwarilia@htb[/htb]$ curl -X POST http://<SERVER_IP>:<PORT>/api.php/city/ -d '{"city_name":"HTB_City", "country_name":"HTB"}' -H 'Content-Type: application/json'

Agora, podemos ler o conteúdo da cidade que adicionamos (HTB_City) para verificar se foi inserida com sucesso:

malwarilia@htb[/htb]$ curl -s http://<SERVER_IP>:<PORT>/api.php/city/HTB_City | jq[
{
"city_name": "HTB_City",
"country_name": "HTB"
}
]

Como podemos ver, uma nova cidade foi criada, a qual não existia anteriormente.

Exercício: Tente adicionar uma nova cidade por meio das ferramentas de desenvolvedor do navegador, utilizando uma das requisições Fetch POST que você usou na seção anterior.

Agora que sabemos como ler e escrever entradas através de APIs, vamos começar a discutir dois outros métodos HTTP que ainda não utilizamos: PUT e DELETE. Como mencionado no início da seção, o método PUT é utilizado para atualizar entradas na API e modificar seus detalhes, enquanto o DELETE é utilizado para remover uma entidade específica.

Nota: O método HTTP PATCH também pode ser utilizado para atualizar entradas na API em vez do PUT. Para ser mais preciso, o PATCH é usado para atualizar parcialmente uma entrada (modificando apenas alguns de seus dados, como “por exemplo, apenas city_name”), enquanto o PUT é utilizado para atualizar a entrada inteira. Também podemos usar o método HTTP OPTIONS para verificar qual dos dois é aceito pelo servidor e, em seguida, utilizar o método apropriado. Nesta seção, vamos nos concentrar no método PUT, embora seu uso seja bastante semelhante ao do PATCH.

O uso do PUT é bastante parecido com o do POST neste caso, sendo a única diferença que precisamos especificar o nome da entidade que desejamos editar na URL; caso contrário, a API não saberá qual entidade modificar. Portanto, tudo o que precisamos fazer é incluir o nome da cidade na URL, alterar o método da solicitação para PUT e fornecer os dados em JSON, assim como fizemos com o POST, da seguinte forma:

malwarilia@htb[/htb]$ curl -X PUT http://<SERVER_IP>:<PORT>/api.php/city/london -d '{"city_name":"New_HTB_City", "country_name":"HTB"}' -H 'Content-Type: application/json'

No exemplo acima, observamos que primeiro especificamos /city/london como a nossa cidade e passamos uma string JSON que continha “city_name”:”New_HTB_City” nos dados da solicitação. Assim, a cidade London não deve mais existir, e uma nova cidade com o nome New_HTB_City deve ter sido criada. Vamos tentar ler ambas para confirmar:

malwarilia@htb[/htb]$ curl -s http://<SERVER_IP>:<PORT>/api.php/city/london | jqmalwarilia@htb[/htb]$ curl -s http://<SERVER_IP>:<PORT>/api.php/city/New_HTB_City | jq[
{
"city_name": "New_HTB_City",
"country_name": "HTB"
}
]

De fato, conseguimos substituir com sucesso o nome da antiga cidade pelo novo.

Nota: Em algumas APIs, a operação de atualização pode ser utilizada também para criar novas entradas. Basicamente, enviaríamos nossos dados e, se a entrada não existir, ela seria criada. Por exemplo, no caso mencionado acima, mesmo que não houvesse uma entrada para a cidade London, uma nova entrada seria criada com os detalhes que fornecemos. No nosso exemplo, no entanto, isso não ocorre. Tente atualizar uma cidade que não existe e veja o que acontece.

Por fim, vamos tentar deletar uma cidade, o que é tão simples quanto ler uma cidade. Basta especificar o nome da cidade para a API e utilizar o método HTTP DELETE, e a entrada será removida, conforme mostrado a seguir:

malwarilia@htb[/htb]$ curl -X DELETE http://<SERVER_IP>:<PORT>/api.php/city/New_HTB_Citymalwarilia@htb[/htb]$ curl -s http://<SERVER_IP>:<PORT>/api.php/city/New_HTB_City | jq
[]

Como podemos ver, após deletarmos New_HTB_City, ao tentarmos lê-la novamente obtemos um array vazio, indicando que a cidade não existe mais.

Com isso, conseguimos realizar todas as 4 operações CRUD através do cURL. Em uma aplicação web real, tais ações podem não ser permitidas para todos os usuários, pois seria considerado uma vulnerabilidade se qualquer pessoa pudesse modificar ou deletar entradas. Cada usuário teria certos privilégios sobre o que pode ler ou escrever, sendo que “escrever” refere-se a adicionar, modificar ou excluir dados. Para autenticar nosso usuário ao utilizar a API, precisaríamos passar um cookie ou um cabeçalho de autorização (por exemplo, JWT), como fizemos em uma seção anterior. Além disso, as operações são semelhantes às que praticamos nesta seção.

Read Entire Article