A Internet é muito mais do que apenas o tráfego WEB, no entanto esta é parte crucial de muitos, talvez até da maioria, dos sistemas que rodam online hoje em dia, então nada mais elementar do que sabermos testar se uma aplicação WEB está se comportando corretamente.
Para este tipo de teste abrimos um navegador, sem qualquer tipo de bloqueador, filtros, plugins e preferencialmente sem caches e analisamos o resultado. Se a página for corretamente exibida, então temos sucesso.
O problema é termos estes navegadores devidamente preparados desta forma, principalmente em linha de comandos. Embora seja possível, o esforço é muitas vezes desnecessário então a ferramenta que usaremos será a tradicional “curl” disponível para praticamente qualquer sistema Linux.
Curl
O curl é uma ferramenta praticamente onipresente nos sistemas Linux e BSD. Caso não venha instalada por padrão, é certo que ela possua um pacote pronto para ser instalado usando o gerenciador de pacotes da sua distribuição.
No Debian e seus derivados (Ubuntu, Mint etc) a instalação é feita com o tradacional:
sudo apt install curl
A sua importância é tanta que seu “motor” principal é uma biblioteca compartilhada (libcurl) que é usada por centenas ou talvez milhares de aplicações desenvolvidas em PHP, Python, Java, entre inúmeras outras.
O uso básico envolve apenas invocar a ferramenta com a URL desejada, como no exemplo:
curl www.google.com.br
O esperado ai é que você veja na tela todo o código HTML da página solicitada, entretanto muitas vezes você não verá nada e por vários motivos. E é ai que a brincadeira começa.
Lidando com redirecionamentos
Vamos pegar como exemplo o que ocorre se você tentar acessar o portal único do Governo Federal no Brasil.
Se eu não escolhi o exemplo errado nem os mantenedores do site mudaram sua estrutura, você verá uma mensagem (com o código de formatação em HTML) dizendo que a página foi movida para “https://www.gov.br”, como se vê abaixo.
curl www.gov.br
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://www.gov.br/">here</a>.</p>
</body></html>
Este é o caso de um redirect com código 302, forçando usuário a ser levado para a versão https do site, que tem sido cada vez mais importante em razão das inúmeras regras de privacidades cobradas pela sociedade.
Se você fizesse este mesmo acesso pelo seu navegador Google Chrome, Mozilla Firefox etc. Certamente você seria redirecionado imediatamente e sem notar este detalhe que muitas vezes pode ser crucial e a fonte de alguns problemas.
Antes de prosseguirmos vamos pedir ao Curl para ser mais “verboso” e ver alguns cabeçalhos da conexão http.
curl -v www.gov.br
* Trying 161.148.164.31:80...
* TCP_NODELAY set
* Connected to www.gov.br (161.148.164.31) port 80 (#0)
> GET / HTTP/1.1
> Host: www.gov.br
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Date: Wed, 24 Nov 2021 13:55:22 GMT
< Server: Apache
< Location: https://www.gov.br/
< Content-Length: 203
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="https://www.gov.br/">here</a>.</p>
</body></html>
* Connection #0 to host www.gov.br left intact
Observe as linhas que antecedem o cabeçalho “DOCTYPE”, mas atente em especial para linha com o cabeçalho onde diz “HTTP/1.1 302 Found”, confirmando o que estava na saída da página HTML. Isso ocorre porque o servidor WEB informou que a página página foi movida e encontrou um redirecionamento para o local certo.
Este tem sido um procedimento normal hoje em dia, e se o seu site não estiver fazendo este redirecionamento, nem o navegador dos usuários estiver preparado para tentar acessar a versão HTTPS, talvez eles estejam vendo uma página de erro 503 (acesso negado) ou mesmo 404 (página não encontrada).
Por isso a importância de uma ferramenta mais “bruta” para testes onde você consegue perceber o que está acontecendo sem automações e sem “mágicas”.
Ainda usando o curl no site do governo vamos atender ao redirect e acessar por HTTPS, como se espera.
curl https://www.gov.br
* Trying 161.148.164.31:443...
* TCP_NODELAY set
* Connected to www.gov.br (161.148.164.31) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: C=BR; ST=DISTRITO FEDERAL; L=BRASILIA; O=SERVICO FEDERAL DE PROCESSAMENTO DE DADOS (SERPRO); CN=gov.br
* start date: Jun 21 21:41:03 2021 GMT
* expire date: Jul 23 21:41:03 2022 GMT
* subjectAltName: host "www.gov.br" matched cert's "www.gov.br"
* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: www.gov.br
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Date: Wed, 24 Nov 2021 13:09:19 GMT
< Server: Zope/(2.13.28, python 2.7.16, linux2) ZServer/1.1
< Content-Length: 0
< X-Cache-Operation: plone.app.caching.moderateCaching
< Expires: Sun, 27 Nov 2011 12:09:19 GMT
< Vary: X-Anonymous
< Location: https://www.gov.br/pt-br
< X-Frame-Options: DENY
< X-Cache-Rule: moved_permanently
< cache-control: max-age=10, s-maxage=0, must-revalidate
< X-Varnish: 1071587300 1054028012
< Age: 2772
< Via: 1.1 varnish (Varnish/6.0)
< X-Cache: HIT
< Set-Cookie: I18N_LANGUAGE="pt-br"; Path=/
< Via: 1.1 www.gov.br
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1
< Referrer-Policy: strict-origin-when-cross-origin
< Strict-Transport-Security: max-age=31536000
< Content-Security-Policy: frame-ancestors 'self';
<
* Connection #0 to host www.gov.br left intact
Observe que houve todo o “handshake” de validação da cadeia de certificados para o acesso HTTPS, mas logo em seguida recebemos um “HTTP/1.1 301 Moved Permanently”.
Ao contrário do exemplo anterior onde o problema foi o redirect do HTTP para o HTTPS, o negócio agora é que este endereço não é um servidor WEB com conteúdo. Ele é apenas um balanceador de carga, proxy ou serviço de cache (observe mais abaixo a linha que aponta para o Varnish (Via: 1.1 varnish (Varnish/6.0)) e que confirma isso.
Agora que sabemos o que ocorre e batemos na porta do site, podemos “pedir com jeitinho” que ele nos deixa seguir os redirects e entrar.
curl --location https://www.gov.br
[...]Aqui haveria uma página HTML bem grande[...]
O parâmetro “–location/-L” faz o Curl agir como os navegadores comuns e sempre que houver um redirect ele tentará seguir.
Eu suprimi a listagem da página HTML pois ela é bem grande, mas se você executar o comando em sua máquina, poderá ver todo o seu conteúdo.
Mas só a título de curiosidade vou repetir o mesmo comando, mas com o parâmetro “-v” para termos o detalhamento da conexão e vermos o que ocorreu.
curl -v --location https://www.gov.br
* Trying 161.148.164.31:443...
* TCP_NODELAY set
* Connected to www.gov.br (161.148.164.31) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: C=BR; ST=DISTRITO FEDERAL; L=BRASILIA; O=SERVICO FEDERAL DE PROCESSAMENTO DE DADOS (SERPRO); CN=gov.br
* start date: Jun 21 21:41:03 2021 GMT
* expire date: Jul 23 21:41:03 2022 GMT
* subjectAltName: host "www.gov.br" matched cert's "www.gov.br"
* issuer: C=BE; O=GlobalSign nv-sa; CN=GlobalSign RSA OV SSL CA 2018
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: www.gov.br
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Date: Wed, 24 Nov 2021 14:10:58 GMT
< Server: Zope/(2.13.28, python 2.7.16, linux2) ZServer/1.1
< Content-Length: 0
< X-Cache-Operation: plone.app.caching.moderateCaching
< Expires: Sun, 27 Nov 2011 13:10:58 GMT
< Vary: X-Anonymous
< Location: https://www.gov.br/pt-br
< X-Frame-Options: DENY
< X-Cache-Rule: moved_permanently
< cache-control: max-age=10, s-maxage=0, must-revalidate
< X-Varnish: 262145219 270776946
< Age: 256
< Via: 1.1 varnish (Varnish/6.0)
< X-Cache: HIT
< Set-Cookie: I18N_LANGUAGE="pt-br"; Path=/
< Via: 1.1 www.gov.br
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1
< Referrer-Policy: strict-origin-when-cross-origin
< Strict-Transport-Security: max-age=31536000
< Content-Security-Policy: frame-ancestors 'self';
<
* Connection #0 to host www.gov.br left intact
* Issue another request to this URL: 'https://www.gov.br/pt-br'
* Found bundle for host www.gov.br: 0x5615bc8ecb50 [serially]
* Can not multiplex, even if we wanted to!
* Re-using existing connection! (#0) with host www.gov.br
* Connected to www.gov.br (161.148.164.31) port 443 (#0)
> GET /pt-br HTTP/1.1
> Host: www.gov.br
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Wed, 24 Nov 2021 14:04:01 GMT
< Server: Zope/(2.13.28, python 2.7.16, linux2) ZServer/1.1
< Content-Length: 167567
< X-Tile-Url: https://www.gov.br/pt-br/pagina-inicial/@@govbr.tile.canais/bf1d4e45-75d1-45ed-aeeb-13240c9bb1c2
< X-Cache-Operation: plone.app.caching.moderateCaching
< Expires: Sun, 27 Nov 2011 13:04:01 GMT
< Vary: X-Anonymous,Accept-Encoding
< X-Cache-Rule: plone.content.folderView
< X-Robots-Tag: noimageindex
< Content-Language: pt-br
< X-Ua-Compatible: IE=edge
< X-Frame-Options: DENY
< Content-Type: text/html;charset=utf-8
< cache-control: max-age=10, s-maxage=0, must-revalidate
< X-Varnish: 950470098 971645772
< Via: 1.1 varnish (Varnish/6.0)
< X-Cache: HIT
< X-Varnish-Age: 673
< Age: 0
< Accept-Ranges: bytes
< Via: 1.1 www.gov.br
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1
< Referrer-Policy: strict-origin-when-cross-origin
< Strict-Transport-Security: max-age=31536000
< Content-Security-Policy: frame-ancestors 'self';
<
[...]Aqui haveria uma página HTML bem grande[...]
Perceba que após o redirect 301 (HTTP/1.1 301 Moved Permanently) gerado pelo Varnish, tivemos a resposta bem sucedida do servidor WEB (HTTP/1.1 200 OK), onde é possível inclusive identificar alguns dados do servidor que roda o Plone, plataforma onde roda esta aplicação.
O que podemos concluir destes testes?
1 – O servidor que provê o site faz um redirect temporário (302) de HTTP para HTTPS;
2 – O site está atrás de um sistema de cache que por sua vez faz o redireciomento para outra(s) máquina(s) quem de fato entregam a página;
3 – Usuarios que reclamem de não estar conseguindo ver um conteúdo atualizado, muito provavelmente estão obtendo uma versão desatualizada do cache;
4 – Usuarios que que não estejam vendo a página, ou vendo mensagens de página não encontrada ou erro de redirect, podem estar com problemas no navegador, ou você pode forçar o redirect de HTTP para HTTPS de outra forma.
Sites com certificado inválido
Um outro caso importante quando se refere a acesso WEB é aquele relacionado a sites com cadeia de certificados inválidos.
Para este exemplo, eu vou ignroar os testes em HTTP e vou direto para tentativa de acesso por HTTPS. É provável que ao tentar os mesmos exemplos que eu, o problema já tenha sido corrigido (assim espero), então você terá que tentar outro site para ver na prática.
curl -v https://prefeituradeatibaia.com.br
* Trying 179.188.46.13:443...
* TCP_NODELAY set
* Connected to www.prefeituradeatibaia.com.br (179.188.46.13) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=*.websiteseguro.com
* start date: Mar 3 18:26:00 2021 GMT
* expire date: Apr 4 18:26:00 2022 GMT
* subjectAltName does not match www.prefeituradeatibaia.com.br
* SSL: no alternative certificate subject name matches target host name 'www.prefeituradeatibaia.com.br'
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, close notify (256):
curl: (60) SSL: no alternative certificate subject name matches target host name 'www.prefeituradeatibaia.com.br'
More details here: https://curl.haxx.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
Observe que ocorreu todo o processo de negociação da cadeia de certificados, mas o nome do proprietário do certificado (webseguro.com) não confere com o endereço que estamos acessando (prefeituradeatibaia.com.br).
Se você observar o cabeçalho “subject” verá que ele inicia com um * (*.webseguro.com) Isso significa que o certificado é válido para qualquer nome que você coloque no lugar do “*”. Por exemplo poderia ser até mesmo “prefeituradeatibaia.webseguro.com”, ou “atibaia.webseguro.com” e eles seriam válidos e aceitos, mas nunca será aceito como “www.prefeituradeatibaia.com.br”.
Nos navegadores comuns, você veria uma mensagem dizendo que há risco de segurança e caberia ao usuário decidir se aceita e continua ou desiste. Veja na imagem abaixo.
Imagine todas as vezes que seu cliente/usuário acessar o seu site ter que ficar acietando seu certificado inválido? Além de não ser nada profissional, e fazer com que se olhe com desconfiança para o site e para o seu trabalho, o Google e outros buscadores ranqueam sua página com baixa pontuação
Tal como nos outros navegadores, no Curl, você pode forçar o carregamento da página indicando o parâmetro “–insecure / -k”.
curl -v --insecure https://www.prefeituradeatibaia.com.br
* Trying 179.188.46.13:443...
* TCP_NODELAY set
* Connected to www.prefeituradeatibaia.com.br (179.188.46.13) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=*.websiteseguro.com
* start date: Mar 3 18:26:00 2021 GMT
* expire date: Apr 4 18:26:00 2022 GMT
* issuer: C=BE; O=GlobalSign nv-sa; CN=AlphaSSL CA - SHA256 - G2
* SSL certificate verify ok.
> GET / HTTP/1.1
> Host: www.prefeituradeatibaia.com.br
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Wed, 24 Nov 2021 18:06:03 GMT
< Server: Apache
< X-Powered-By: PHP/7.4.11
< Link: <https://www.prefeituradeatibaia.com.br/wp-json/>; rel="https://api.w.org/", <https://www.prefeituradeatibaia.com.br/>; rel=shortlink
< Vary: Accept-Encoding
< Content-Length: 115737
< Content-Type: text/html; charset=UTF-8
<
[...]Aqui haveria uma página HTML bem grande[...]
Compare a seção “Server Certificate” em ambos os exemplos e observe que a parte que reclamava do proprietário do certificado foi completamente ignorada pelo Curl e a página HTML será completamente carregada na tela.
Como já disse não é legal deixar que seus usarios olhem com desconfiança para o seu site, então qual é a solução aqui!? (Já dando uma consultoria grátis para a prefeitura). A solução é emitir um certificado válido para o seu prório domínio.
Há quatro possibilidades para isso:
(1) O certificado Let’s Encrypt que é gratuito e válido por 3 meses; mas requer que você tenha o controle sobre sua hospedagem para implanta-lo;
(2) Comprar um certificado simples que custa cerca de R$400 atualmente e tem validade de 1 ano.
(3) Comprar um certificado Wildcard que permitiria validar tanto “www.prefeitura…” quando “prefeitura…”. Este certificado é um pouco mais caro (cerca de 1500 a 3000 Reais), mas se você tiver dezenas de serviços no seu domínio principal, você acaba economizando bastante;
(4) Usar um serviço de hospedagem que forneça o certificado gratuitamente para o seu domínio; geralmente provedores de Cloud que permitem usar um serviço de cache ou balanceador de carga, dão esta opção, mas embora você tenha o benefício de não ter que se preocupar com implantações e renovações, o custo deste serviço extra pode acabar sendo equivalente ao do certificado avulso.
Download com o Curl
O Curl pode ser usado para download de arquivos também. Digamos que eu queira baixar a o cliente Java do popular Minecraft no enedereço https://launcher.mojang.com/download/Minecraft.deb
curl https://launcher.mojang.com/download/Minecraft.deb --output minecraft.deb
Observe a necessidade do parâmetro “–output / -o” para informar em qual arquivo local o conteúdo será salvo.
Automações com o Curl
Alguns desenvolvedores fornecem APIs acessíveis pela WEB ou mesmo necessitam que determinadas rotinas sejam executadas periodicamente.
É o caso do WordPress, NextCloud e ownCloud que possuem um arquivo chamado “cron.php” que deve ser acessado periodicamente para automatizar certas tarefas.
Mais barato que contratar alguém para abrir esta página no navegado e telcar F5 a cada minuto, é você usar o Cron do seu servidor para invocar curl acessando a URL necessária. Veja o exemplo abaixo para o WordPress.
curl --silent http://seusite.com/wp-cron.php
O parâmetro “–silent” é para que o Curl não exiba mensagens desnecessárias, já que esta linha de comandos será executada a partir do Cron do sistema.
Se necessário verifique a necessidade do “–location” para que os devidos redirects sejam executados, ou mesmo use o “–insecure” até que se resolva problemas com os certificados SSL do seu site.
Outro bom exemplo seria para retorno de dados que alguma página possa fornecer informações relevantes, como o o Ipecho que retorna o seu IP público, no exemplo abaixo, ou alguma página que retorne um JSON ou qualquer outro dado a ser processado depois.
curl https://ipecho.net/plain
175.111.19.61
Outras ferramentas
Há algumas outras ferramentas que podem ser usadas com o mesmo propósito do curl. A mais conhecida é o WGET, que respeitadas as diferenças entre os seus parâmetros permitem alcançar os mesmos resultados ou bem próximo, com alguma variação.
O que apresentei aqui é somente o básico do que se pode fazer com o curl. Embora uma ferramenta bem simples ela permite fazer testes bastante sofisticados como por exemplo acessar uma página que requer autenticação, fazer envio de dados para formulários entre muitas possiblidades, mas nada que a leitura do manual não permita resolver.