Em algum momento necessitamos disponibilizar um arquivo para download somente para usuários cadastrados em nosso site. E como sabemos, simplesmente colocar estes arquivos em um pasta que possa ser acessado por URL do site não é uma ideia muito segura, pois mesmo se você disponibilizar o link de download somente para seu usuário logado, nada impede de uma outra pessoa “adivinhar” o caminho de download. Um usuário pode acessar diretamente a pasta de downloads e se seu diretório não conter um arquivo index ou o servidor apache não estiver configurado para não listar os arquivos(a maioria lista) o usuário verá seus arquivos, mesmo se não for possível listar os arquivos, o usuário mal intencionado pode ainda ir tentando nomes de arquivos por força bruta até acertar. Se o arquivo estiver dentro da pasta publica do apache ele pode ser acessado e baixado pelo navegador, você pode dificultar bastante, mas se o usuário descobrir o nome ele pode baixar.

Para remediar isso podemos colocar os arquivos de download fora da pasta de arquivos públicos do apache, sendo assim não acessíveis via URL diretamente, mas nos possibilitando criar um script de download que verifique se o usuário esta logado ou se tem permissão de baixar, se estiver leia o arquivo do sistema de arquivo e envie para download para o usuário.

Para nosso exemplo iremos utilizar a seguinte estrutura de diretórios:

  • public_html
    • index.php
    • login.php
    • logout.php
    • download.php
  • downloads
    • putty.exe

Como você deve saber, na maioria das hospedagens os arquivos visíveis do nosso site são colocados na pata public_html ou www. Sendo assim os arquivos da pasta downloads não esta publicada na web mas nossos scripts podem acessá-los via sistema de arquivos do servidor.

Antes de vermos o arquivo download.php que é o que realmente importa, vamos ver os arquivos de login, logout e index, que são bem simples e servem somente para este exemplo, se você quiser um exemplo de sistema de login pode ver em outro post.

index.php
<?php
  session_start();
?><html>
  <head>
      <meta charset="UTF-8">
      <title>Teste protegendo download autenticado</title>
  </head>
 
 
  <body>
    <?php if ( $_SESSION['logado'] == "true" ){ ?>
    <p>Você está logado</p>
    <a href="logout.php">logout</a>
    <?php }else{ ?>
    Você <strong>não</strong> está logado!!!
    <a href="login.php">login</a>
    <?php } ?>
 
 
  <a href="download.php?download=putty.exe">Download putty.exe</a>
  </body>
</html>
login.php
<?php
  session_start();
 
  $_SESSION['logado'] = 'true';
 
  header('location: index.php');
?>
logout.php
<?php
  session_start();
 
  unset($_SESSION['logado']);
 
  header('location: index.php');
?>

Os arquivos acima são bem simples, acredito que não necessite de maiores explicações. Então vamos ao que interessa:

download.php
<?php
session_start();
 
if( $_SESSION['logado'] == 'true' ){
       
    $download = $_GET['download'];
    if( is_file( '/home/boteco/downloads/'.$download ) ){
        $filename = '/home/boteco/downloads/'.$download;
     
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $type = finfo_file($finfo, $filename);
        header('Content-type: '.$type);
        header('Content-Disposition: attachment; filename="'.$download.'"');
 
        readfile($filename);
    }else{
        echo "arquivo nao encontrado";
    }
 
}else{
    echo "Você não estaa logado. Faça o login para efetuar o download.";
}
 
?>

Na linha 2 chamamos a função session_start() para podermos ler e gravar informação na sessão.

Na linha 4 testamos se o usuário que acessou esta logado na sessão, se o usuário pode fazer o download.

Na linha 6 pegamos o nome do arquivo que recebemos via parâmetro GET da URL, como vimos no arquivo index.php na linha 18 para fazer o download chamamos download.php?download=putty.exe então queremos baixar o arquivo putty.exe.

Na linha 7 verificamos se o arquivo que recebemos por parâmetro existe e é um arquivo, você deve ter notado que utilizamos o caminho absoluto da pasta de download e concatenamos com o nome do arquivo recebido.

Na linha 8 armazenamos o nome absoluto do arquivo em uma variável para facilitar a manipulação.

Na linha 10 criamos um recurso fileinfo, para ser utilizado para ler o mime-type do arquivo.

Linha 11 lemos o MIME-TYPE do arquivo e armazenamos em uma variável, necessitamos pegar o MIME-TYPE do arquivo pois o nosso script de download possui o MIME-TYPE text/x-php e o comportamento do navegador para este MIME-TYPE é mostrá-lo e não baixá-lo, se deixarmos o MIME em text/x-php e jogarmos o contudo do arquivo para o navegador, veremos o código binário do programa, ou seja, um monte de caracteres estranhos.

Na linha 12 modificamos o MIME do nosso script para o MIME lido do arquivo.

Na linha 13 forçamos o arquivo ser baixado com o nome recebido por parâmetro, lembrando, se não forçarmos ele irá utilizar o nome do script o que não seria algo legal 🙂 .

Na linha 15 utilizamos a função redfile que lê um arquivo e exibe ele na tela, como já modificamos o header do arquivo para um outro MIME e para forçar o download o arquivo será lido do sistema de arquivo e enviado para o usuário.

OBS: Só lembrando, a função finfo_open só está disponível no PHP >= 5.3.0, se você utilizar uma versão anterior pode utilizar mime_content_type mas lembre-se de verificar se ela está disponível na sua hospedagem, na minha por exemplo não está e não estou com vontade de me incomodar com eles, fiquei somente com o teste local.

Bom era isso pessoal.

[edit]

Para baixar o exemplo clique aqui

[/edit]