Dúvida: Como realizar download de arquivos com segurança

Como eu posso realizar download de arquivos, com “segurança” via socket, eu sei fazer o algoritmo via socket.

  • Minha dúvida é, caso houver um pico na Internet ou bugar, o arquivo irá vir corrompido?

  • Posso prevenir isso?

Vamos la

o seu algoritmo de socket vai estar em uma camada de rede. para fazer o download de um arquivo vc vai precisar de uma camada de aplicação, que vai entender que vc quer o arquivo x. essa camada pode ser, por exemplo, HTTP ou FTP.

como vc fala download eu assumo que vc quer trabalhar com HTTP. o que tem de diferente é que dentro do seu socket vc vai receber uma mensagem HTTP que é composta de duas partes: header e body

A cabeça do seu request tem diversas informações, no formato Chave: valores

por exemplo ele pode conter um redirecionamento para outro recurso ( http code 301 ou 302 ), pode dizer que não encontrou ( 404 ), que vc não tem permissão ( 403 ) ou que houve um erro interno ( 500 ). Se o http status code for 200 ( ou da familia 200 ) significa que no body pode conter o que vc quer.

vc precisa verificar algumas coisas no cabeçalho, geralmente ele mostra quantos bytes estão vindo, o mime-type e outras informações. por isso que geralmente vc usa uma biblioteca capaz de lidar com requests HTTP ( é bastante coisa pra fazer na mão ).

Por exemplo, vc quer baixar este arquivo:

veja um exemplo de request HTTP para este conteudo

$ curl -vs http://65.media.tumblr.com/tumblr_m9pdbpGWWe1qid2nw.gif
> GET /tumblr_m9pdbpGWWe1qid2nw.gif HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 65.media.tumblr.com
> Accept: */*

nesse exemplo vc abriu um socket na porta 80 do host 65.media.tumblr.com

e escreveu as linhas acima ( ignore o > , isso é para mostrar que vc esta enviando). eu fiz este request com a ferramenta curl que é um browser na linha de comando comum no mundo unix.

veja a resposta

< HTTP/1.1 200 OK
< Content-Type: image/gif
< Content-Length: 364
< Connection: keep-alive
< Date: Fri, 02 Sep 2016 08:30:02 GMT
< Last-Modified: Sun, 02 Sep 2012 03:21:26 GMT
< ETag: "53470c3dbc6da71ce589aad9f174ee2e"
< Cache-Control: max-age=31536000
< x-amz-version-id: 5A9q9.PAtIL81ZkcNenGjL1GLXDwpDfD
< Accept-Ranges: bytes
< Server: AmazonS3
< X-Cache: Miss from cloudfront
< Via: 1.1 ae50827a2579f65f0bcebe69c0c512a4.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: JicQBBHUZdceiBrSRNGxCrh_1RYw_6c9UjC9jfAE2cd3A8zXCYKLGA==
< 
GIF89a�{������������������!�(!�
                                   NETSCAPE2.0,hh���B�i\��
J��}�t�G�Q��� �k
�4��ܮe�~-�oH\]L@`�|
�p��z���.�2�2ɇ�e0N.��msCe"l
	!�,�{������������������������q�	��c̍l���uh�%W�&F��i�Y*���s�^����ٮW1��f2
u"��l�Y�
��|�B�ZU�1@R����.x�����Tl�h�P�TG/slv�I+{e�e;****

perceba que vc tem header Content-Length: 364 ou seja este arquivo tem 364 bytes.

e perceba que vc tem um header ETag: "53470c3dbc6da71ce589aad9f174ee2e", que nem sempre aparece, que é o hash md5 do arquivo.

então como detectar que o arquivo “corrompeu”?

duas formas:

  1. se o tamanho dele é diferente do Content-Length, então falta coisa. em geral vc pode fazer o mesmo request pedindo o Range de bytes que falta se o http server suportar e concatenar com o que vc ja salvou.

  2. se vc tiver um ETAG, veja se o md5 sum no final é o mesmo. as vezes algumas paginas fornecem o md5, tipo quando vc vai baixar uma imagem de uma ISO de linux. o md5 é um hash que se vc errar um byte, vai dar um valor totalmente diferente.

alguns arquivos possuem um formato bem definido e vc pode verificar o seu conteudo. o formato zip se nao me engano pode verificar a propria integridade, etc, mas ai depende do arquivo.

isso que não falamos de HTTPS, nem cookies/autenticação. e outros protocolos serão bem especificos.

por fim: é claro que vc pode usar uma solução que simplesmente envia um arquivo via socket. o netcat nc sabe fazer isso. mas ele só é indicado dentro de uma rede local, onde todos confiam que não havera problemas, e não existe nenhuma garantia sobre o tamanho.

vc pode criar um mini-protocolo onde os 4 primeiros bytes do seu stream são o tamanho do arquivo, e o resto é o arquivo em si. a interface protocol buffers do Riak faz algo parecido. nada impede que vc crie um protocolo que ajude a sua vida e é um excelente exercicio para praticar.

pro exemplo, se vc quer ficar fera em sockets e arquivos, vc pode imaginar um protocolo onde vc conecta e envia o nome do arquivo. a resposta vai ser algo como

1 byte com a resposta ( 0 pode ser sucesso, 1 pode ser not found, 2 pode ser falta de permissão )
4 byte para o tamanho do arquivo ( se tiver algo)
o arquivo em si
+16 bytes para o hash md5 ( se tiver algo )

o seu cliente vai ler o primeiro byte, depois os 4 proximos bytes ( se for ok ) e então pode ler o arquivo por meio de um buffer de 1024 bytes ( por exemplo, uma vez que ler todo o arquivo em memoria e depois escrever pode acabar com seus recursos, vc pode ler um pouco, escreve um pouco, etc ). e no final vc pode verificar a integridade.

vc poderia adicionar ai o conceito de paginas, onde o arquivo pode ser quebrado em paginas de tamanho fixo, ou o conceito de range entre a posiçao x e y ( y for -1 vc assume que é ate ao final). e pode inclusive tentar baixar paginas/ranges diferentes em paralelo.

no lugar do md5 vc pode usar um SHA1 mais forte, com mais bits. esse hash pode ser criptografado de forma que se vc tem a senha, vc pode garantir que não rolou ataque main-in-the-middle. isso é bem legal.

boa sorte.

1 curtida

peczenyj, obrigado pelas dicas e informações, agradeço muito, caso houver mais dicas ou exemplos, fico agradecido.

Ajudou bastante, clareou minha mente, não sabia nada sobre isso.