Programa De Envio De Imagens Totalmente Seguro

Não sei se isto vai acontecer, mas vou tentar.

Durante a última hora pesquisei sobre a segurança do upload de imagens. Aprendi que há muitas funções para testar o upload.

No meu projecto, preciso de estar seguro com imagens carregadas. Também pode haver uma grande quantidade dele e pode exigir muita largura de banda, então comprar uma API não é uma opção.

Então eu decidi obter um script PHP completo para upload de imagem realmente segura. Também acho que vai ajudar muitos dos pessoas lá fora, porque é impossível encontrar uma realmente segura. Mas eu não sou perito em php, assim que é realmente dor de cabeça para mim adicionar algumas funções, assim que eu pedirei para esta ajuda da comunidade para criar um script completo de upload de imagem realmente seguro.

Realmente grandes temas sobre os que estão aqui (no entanto, eles estão apenas dizendo o que é necessário para fazer o truque, mas não como fazer isso, e como eu disse, eu não sou um mestre em PHP, então eu não sou capaz de fazer tudo por mim mesmo): Envio da imagem de PHP lista de verificação de segurança https://security.stackexchange.com/questions/32852/risks-of-a-php-image-upload-form

em resumo, eles estão dizendo que isso é o que é necessário para o envio da imagem de segurança (vou citar das páginas acima):

  • desactiva o PHP de correr dentro da pasta de envio .httaccess.
  • não permitir o envio se o nome do ficheiro contiver o texto "php".
  • permitir apenas extensões: jpg,jpeg, gif e png.
  • permitir apenas o tipo de ficheiro de imagem.
  • desactiva a imagem com dois tipos de ficheiros.
  • muda o nome da imagem. Enviar para uma sub-pasta e não para a pasta de topo.

Também

  • Re-processe a imagem usando GD (ou Imagick) e salve a imagem processada. Todos os outros são divertidos, aborrecidos para os hackers.
  • como o rr indicou, use o move_ uploaded_ file () para qualquer envio"
  • A propósito, devias ser muito restritivo sobre o teu upload. pasta. Esses lugares são um dos cantos escuros onde muitas explorações
    acontecer. Isto é válido para qualquer tipo de envio e qualquer programação
    língua / servidor. Verifique
    https://www.owasp.org/index.php/Unrestricted_File_Upload
  • Nível 1: Verifique a extensão (a extensão do ficheiro termina com)
  • Nível 2: assinale o tipo MIME ($file_ info = getimagesize ($_files ['image_ file']; $file_mime = $file_ info ['mime'];)
  • Nível 3: Leia os primeiros 100 bytes e verificar se têm bytes no seguinte intervalo: ASCII 0-8, 12-31 (decimal).
  • Nível 4: Verificar os números mágicos no cabeçalho (os primeiros 10-20 bytes do ficheiro). Poderá encontrar alguns dos 'bytes' do cabeçalho dos ficheiros de toma.:
    http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Examples
  • Você pode querer executar "is_ uploaded_ file" nos $ _ files['my_ files']['tmp_name'] também. Veja
    http://php.net/manual/en/function.is-uploaded-file.php
Aqui está uma grande parte, mas ainda não é tudo. (Se você sabe algo mais que poderia ajudar a tornar o upload ainda mais seguro, por favor compartilhe.)

ISTO É O QUE TEMOS AGORA

  • PHP principal:

    function uploadFile ($file_field = null, $check_image = false, $random_name = false) {
    
    //Config Section    
    //Set file upload path
    $path = 'uploads/'; //with trailing slash
    //Set max file size in bytes
    $max_size = 1000000;
    //Set default file extension whitelist
    $whitelist_ext = array('jpeg','jpg','png','gif');
    //Set default file type whitelist
    $whitelist_type = array('image/jpeg', 'image/jpg', 'image/png','image/gif');
    
    //The Validation
    // Create an array to hold any output
    $out = array('error'=>null);
    
    if (!$file_field) {
      $out['error'][] = "Please specify a valid form field name";           
    }
    
    if (!$path) {
      $out['error'][] = "Please specify a valid upload path";               
    }
    
    if (count($out['error'])>0) {
      return $out;
    }
    
    //Make sure that there is a file
    if((!empty($_FILES[$file_field])) && ($_FILES[$file_field]['error'] == 0)) {
    
    // Get filename
    $file_info = pathinfo($_FILES[$file_field]['name']);
    $name = $file_info['filename'];
    $ext = $file_info['extension'];
    
    //Check file has the right extension           
    if (!in_array($ext, $whitelist_ext)) {
      $out['error'][] = "Invalid file Extension";
    }
    
    //Check that the file is of the right type
    if (!in_array($_FILES[$file_field]["type"], $whitelist_type)) {
      $out['error'][] = "Invalid file Type";
    }
    
    //Check that the file is not too big
    if ($_FILES[$file_field]["size"] > $max_size) {
      $out['error'][] = "File is too big";
    }
    
    //If $check image is set as true
    if ($check_image) {
      if (!getimagesize($_FILES[$file_field]['tmp_name'])) {
        $out['error'][] = "Uploaded file is not a valid image";
      }
    }
    
    //Create full filename including path
    if ($random_name) {
      // Generate random filename
      $tmp = str_replace(array('.',' '), array('',''), microtime());
    
      if (!$tmp || $tmp == '') {
        $out['error'][] = "File must have a name";
      }     
      $newname = $tmp.'.'.$ext;                                
    } else {
        $newname = $name.'.'.$ext;
    }
    
    //Check if file already exists on server
    if (file_exists($path.$newname)) {
      $out['error'][] = "A file with this name already exists";
    }
    
    if (count($out['error'])>0) {
      //The file has not correctly validated
      return $out;
    } 
    
    if (move_uploaded_file($_FILES[$file_field]['tmp_name'], $path.$newname)) {
      //Success
      $out['filepath'] = $path;
      $out['filename'] = $newname;
      return $out;
    } else {
      $out['error'][] = "Server Error!";
    }
    
     } else {
      $out['error'][] = "No file uploaded";
      return $out;
     }      
    }
    
    
    if (isset($_POST['submit'])) {
     $file = uploadFile('file', true, true);
     if (is_array($file['error'])) {
      $message = '';
      foreach ($file['error'] as $msg) {
      $message .= '<p>'.$msg.'</p>';    
     }
    } else {
     $message = "File uploaded successfully".$newname;
    }
     echo $message;
    }
    
  • e o Formulário:

    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data" name="form1" id="form1">
    <input name="file" type="file" id="imagee" />
    <input name="submit" type="submit" value="Upload" />
    </form>
    
Então, o que eu estou pedindo é para ajudar, postando trechos de códigos que me ajudarão (e todos os outros) para fazer este Script de Upload de imagem para tornar super seguro. Ou partilhando / criando um programa completo com todos os excertos adicionados.

Author: Community, 2016-07-21

2 answers

Quando começar a trabalhar num programa seguro de envio de imagens, há muitas coisas para o considder. Não sou nenhum perito nisto, mas pediram-me para desenvolver isto uma vez no passado. Vou passar por todo o processo que passei por aqui para que possas seguir em frente. Para isso vou começar com um formulário html muito básico e um script php que lida com os arquivos.

Formulário HTML:

<form name="upload" action="upload.php" method="POST" enctype="multipart/form-data">
    Select image to upload: <input type="file" name="image">
    <input type="submit" name="upload" value="upload">
</form>

Ficheiro PHP:

<?php
$uploaddir = 'uploads/';

$uploadfile = $uploaddir . basename($_FILES['image']['name']);

if (move_uploaded_file($_FILES['image']['tmp_name'], $uploadfile)) {
    echo "Image succesfully uploaded.";
} else {
    echo "Image uploading failed.";
}
?> 

Primeiro problema: ficheiro tipos
Os atacantes não têm que usar o formulário em seu site para enviar arquivos para o seu servidor. Os pedidos de correio podem ser interceptados de várias formas. Pense em acessórios de navegador, proxies, scripts Perl. Não importa o quanto tentemos, não podemos impedir um atacante de tentar fazer upload de algo (s)que ele não é suposto fazer. Por isso, toda a nossa segurança tem de ser feita em serverside.

O primeiro problema são os tipos de ficheiros. No script acima de um atacante pode enviar qualquer coisa que ele quer, como um php script por exemplo, e siga um link direto para executá-lo. Assim, para evitar isso, implementamos a verificação do tipo de Conteúdo :

<?php
if($_FILES['image']['type'] != "image/png") {
    echo "Only PNG images are allowed!";
    exit;
}

$uploaddir = 'uploads/';

$uploadfile = $uploaddir . basename($_FILES['image']['name']);

if (move_uploaded_file($_FILES['image']['tmp_name'], $uploadfile)) {
    echo "Image succesfully uploaded.";
} else {
    echo "Image uploading failed.";
}
?>
Infelizmente, isto não é suficiente. Como mencionei antes, o atacante tem controle total sobre o pedido. Nada o impedirá de modificar os cabeçalhos do pedido e simplesmente mudar o tipo de conteúdo para"image/png". Então, em vez de depender apenas do cabeçalho do tipo de conteúdo, seria melhor também validar o conteúdo do arquivo carregado. Aqui é onde a biblioteca php GD vem a calhar. Usando getimagesize(), vamos processar a imagem com a biblioteca GD. Se não for uma imagem, isto irá falhar e por isso todo o upload irá falhar:
<?php
$verifyimg = getimagesize($_FILES['image']['tmp_name']);

if($verifyimg['mime'] != 'image/png') {
    echo "Only PNG images are allowed!";
    exit;
}

$uploaddir = 'uploads/';

$uploadfile = $uploaddir . basename($_FILES['image']['name']);

if (move_uploaded_file($_FILES['image']['tmp_name'], $uploadfile)) {
    echo "Image succesfully uploaded.";
} else {
    echo "Image uploading failed.";
}
?>
Ainda não chegámos lá. A maioria dos tipos de arquivos de imagem permitem Comentários de texto adicionados a eles. Mais uma vez, nada impede o atacante de adicionar algum código php como um comentário. A biblioteca GD avaliará isso como uma imagem perfeitamente válida. O interpretador de PHP iria ignorar completamente a imagem e execute o código php no comentário. É verdade que depende da configuração php que Extensões de arquivo são processadas pelo interpretador php e que não, mas como existem muitos desenvolvedores lá fora que não têm controle sobre esta configuração devido ao uso de um VPS, não podemos assumir que o interpretador php não vai processar a imagem. É por isso que adicionar uma extensão de arquivo white list também não é seguro o suficiente.

A solução para isto seria armazenar as imagens num local onde um o atacante não pode aceder directamente ao ficheiro. Isto pode estar fora da raiz do documento ou em um diretório protegido por A.ficheiro htaccess:

order deny,allow
deny from all
allow from 127.0.0.1

Editar: depois de falar com alguns outros programadores de PHP, eu sugiro altamente usar uma pasta fora da raiz do documento, porque o htaccess nem sempre é confiável.

Ainda precisamos que o usuário ou qualquer outro Visitante seja capaz de ver a imagem. Então vamos usar o php para recuperar a imagem para eles:
<?php
$uploaddir = 'uploads/';
$name = $_GET['name']; // Assuming the file name is in the URL for this example
readfile($uploaddir.$name);
?>

Segundo problema: Local ataques de inclusão de ficheiros
Embora nosso script esteja razoavelmente seguro agora, não podemos assumir que o servidor não sofre de outras vulnerabilidades. Uma vulnerabilidade de segurança comum é conhecida como inclusão de ficheiros locais . Para explicar isso, preciso adicionar um código de exemplo:

<?php
if(isset($_COOKIE['lang'])) {
   $lang = $_COOKIE['lang'];
} elseif (isset($_GET['lang'])) {
   $lang = $_GET['lang'];
} else {
   $lang = 'english';
}

include("language/$lang.php");
?>
Neste exemplo, estamos falando de um site em várias línguas. A linguagem dos sites não é algo considerado como informação de "alto risco". Nós tentamos obter a língua prefered dos visitantes através de um cookie ou um pedido GET e incluir o arquivo necessário com base nele. Agora considder o que vai acontecer quando o atacante entrar no seguinte url:

Www.example.com/index.php?lang=../uploads/my_evil_image.jpg

O PHP incluirá o ficheiro carregado pelo atacante ignorando o facto de que ele não pode aceder directamente ao ficheiro e estamos de volta à estaca zero.

A solução para este problema é garantir que o utilizador não sabe o nome do ficheiro no servidor. Em vez disso, alterar o nome do ficheiro e até mesmo a extensão usando uma base de dados para manter o seu registo:

CREATE TABLE `uploads` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(64) NOT NULL,
    `original_name` VARCHAR(64) NOT NULL,
    `mime_type` VARCHAR(20) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;


<?php

if(!empty($_POST['upload']) && !empty($_FILES['image']) && $_FILES['image']['error'] == 0)) {

    $uploaddir = 'uploads/';

    /* Generates random filename and extension */
    function tempnam_sfx($path, $suffix){
        do {
            $file = $path."/".mt_rand().$suffix;
            $fp = @fopen($file, 'x');
        }
        while(!$fp);

        fclose($fp);
        return $file;
    }

    /* Process image with GD library */
    $verifyimg = getimagesize($_FILES['image']['tmp_name']);

    /* Make sure the MIME type is an image */
    $pattern = "#^(image/)[^\s\n<]+$#i";

    if(!preg_match($pattern, $verifyimg['mime']){
        die("Only image files are allowed!");
    }

    /* Rename both the image and the extension */
    $uploadfile = tempnam_sfx($uploaddir, ".tmp");

    /* Upload the file to a secure directory with the new name and extension */
    if (move_uploaded_file($_FILES['image']['tmp_name'], $uploadfile)) {

        /* Setup a database connection with PDO */
        $dbhost = "localhost";
        $dbuser = "";
        $dbpass = "";
        $dbname = "";

        // Set DSN
        $dsn = 'mysql:host='.$dbhost.';dbname='.$dbname;

        // Set options
        $options = array(
            PDO::ATTR_PERSISTENT    => true,
            PDO::ATTR_ERRMODE       => PDO::ERRMODE_EXCEPTION
        );

        try {
            $db = new PDO($dsn, $dbuser, $dbpass, $options);
        }
        catch(PDOException $e){
            die("Error!: " . $e->getMessage());
        }

        /* Setup query */
        $query = 'INSERT INTO uploads (name, original_name, mime_type) VALUES (:name, :oriname, :mime)';

        /* Prepare query */
        $db->prepare($query);

        /* Bind parameters */
        $db->bindParam(':name', basename($uploadfile));
        $db->bindParam(':oriname', basename($_FILES['image']['name']));
        $db->bindParam(':mime', $_FILES['image']['type']);

        /* Execute query */
        try {
            $db->execute();
        }
        catch(PDOException $e){
            // Remove the uploaded file
            unlink($uploadfile);

            die("Error!: " . $e->getMessage());
        }
    } else {
        die("Image upload failed!");
    }
}
?>
Então agora fizemos o seguinte:
    Criámos um lugar seguro para salvar as imagens. Processámos a imagem com a biblioteca GD.
  • verificámos o tipo MIME da imagem
  • mudamos o nome do ficheiro e mudamos a extensão
  • Salvámos o nome do Ficheiro Novo e original na nossa base de dados. Nós também gravei o tipo MIME na nossa base de dados
Ainda precisamos de poder mostrar a imagem aos visitantes. Nós simplesmente usamos a coluna id da nossa base de dados para fazer isso:
<?php

$uploaddir = 'uploads/';
$id = 1;

/* Setup a database connection with PDO */
$dbhost = "localhost";
$dbuser = "";
$dbpass = "";
$dbname = "";

// Set DSN
$dsn = 'mysql:host='.$dbhost.';dbname='.$dbname;

// Set options
$options = array(
    PDO::ATTR_PERSISTENT    => true,
    PDO::ATTR_ERRMODE       => PDO::ERRMODE_EXCEPTION
);

try {
    $db = new PDO($dsn, $dbuser, $dbpass, $options);
}
catch(PDOException $e){
    die("Error!: " . $e->getMessage());
}

/* Setup query */
$query = 'SELECT name, original_name, mime_type FROM uploads WHERE id=:id';

/* Prepare query */
$db->prepare($query);

/* Bind parameters */
$db->bindParam(':id', $id);

/* Execute query */
try {
    $db->execute();
    $result = $db->fetch(PDO::FETCH_ASSOC);
}
catch(PDOException $e){
    die("Error!: " . $e->getMessage());
}

/* Get the original filename */
$newfile = $result['original_name'];

/* Send headers and file to visitor */
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename='.basename($newfile));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($uploaddir.$result['name']));
header("Content-Type: " . $result['mime_type']);
readfile($uploaddir.$result['name']);
?>

Graças a este script o visitante será capaz de ver a imagem ou baixá-la com o seu nome de arquivo original. No entanto, (s)ele não pode acessar o arquivo em seu servidor diretamente nem vai (s)ser capaz de enganar o seu servidor para acessar o arquivo para ele/ela como (s)ele não tem nenhuma maneira de saber qual arquivo é. (S)ele não pode bruto force o seu diretório de upload ou como ele simplesmente não permite que ninguém acesse o diretório exceto o próprio servidor.

E isso conclui o meu programa seguro de envio de imagens. Eu gostaria de acrescentar que eu não incluí um tamanho máximo de arquivo neste script, mas você deve facilmente ser capaz de fazer isso você mesmo.

Classe De Carregamento Das Imagens
Devido à alta demanda deste script, eu tenho uma aula de ImageUpload que deve torná-lo muito mais fácil para todos vocês manuseie de forma segura as imagens enviadas pelos visitantes do seu site. A classe pode lidar com arquivos individuais e múltiplos ao mesmo tempo, e fornece-lhe com recursos adicionais como exibição, download e exclusão de imagens.

Uma vez que o código é simplesmente grande para postar aqui, você pode baixar a classe A partir de MEGA aqui:

Transferir A Classe De Carregamento Das Imagens

Lê o README.txt e siga as instruções.

A Abrir O Código
A Imagem Segura o projecto de classe também está agora disponível no meu perfil Github. Isto para que os outros(você?) pode contribuir para o projeto e fazer desta uma grande biblioteca para todos. (actualmente sob escuta. Por favor, use o download acima até ser corrigido).

 57
Author: icecub, 2018-05-16 21:07:29
Bem, fazer upload de arquivos em PHP é muito fácil e seguro. Eu recomendo lear sobre:

Para carregar o ficheiro em PHP você tem dois métodos, PUT e POST(talvez mais..). Para usar o método POST com necessidade HTML activar o enctype num formulário como este:

<form action="" method="post" enctype="multipart/form-data">
  <input type="file" name="file">
  <input type="submit" value="Upload">
</form>

Então, no seu PHP, precisa de apanhar o seu ficheiro de upload com $_FICHEIROS Assim:

$_FILES['file']

Então precisa de sair do temp("enviar") com o move_ uploaded_ file:

if (move_uploaded_file($_FILES['file']['tmp_name'], YOU_PATH)) {
   // ...
}

E depois de enviar o ficheiro, precisa de verificar a extensão e a melhor e melhor maneira é usar pathinfo Assim:

$extension = pathinfo($_FILES['file']['tmp_name'],PATHINFO_EXTENSION);

Mas a extensão não é segura porque, você pode enviar o arquivo com extensão .jpg mas com o tipo MIME text/php e isto é um traseira. Então, eu recomendo verificar o tipo MIME real com finfo_open Assim:

$mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $_FILES['file']['tmp_name']);

E, não uses $_FILES['file']['type'] porque às vezes e dependendo do navegador e do so cliente, você pode receber application/octet-stream, e este tipo MIME não é um verdadeiro o tipo MIME do seu ficheiro carregado.

Acho que com isto podes enviar o ficheiro com a segurança. Desculpa o meu inglês, adeus!
 2
Author: Olaf Erlandsen, 2016-07-29 12:12:00