【问题标题】:How to make sure a remote image downloaded to server is actually an image with PHP?如何确保下载到服务器的远程图像实际上是 PHP 的图像?
【发布时间】:2015-06-26 05:37:22
【问题描述】:

我正在构建一个 PHP 应用程序,它将作为 SugarCRM 模块分发给成百上千的用户。我正在开发的功能允许用户从远程 URL 上传图片。

StackOverflow 具有相同的功能,如下图所示。

我提到这是在其他服务器上,因为我的上传功能需要在许多服务器配置和网络主机上非常可靠!

为了帮助更可靠地获取和下载远程图像,我在fetch_image($image_url) 函数中进行了一些检查,例如...

ini_get('allow_url_fopen') 看看他们是否允许file_get_contents() 使用 URL 而不是文件路径。

我使用function_exists('curl_init')查看是否安装了CURL。

除了使用多种方法获取远程图像。我现在还需要确保从远程服务器返回或构建的文件实际上是合法的图像文件,而不是某种恶意文件!

大多数服务器至少安装了GD image processor,所以也许它可以以某种方式在我的图像上使用以确保它是图像?

到目前为止我的代码如下...

在检查以确保图像是图像方面的任何帮助表示赞赏!

sockets 方法似乎实际上生成了一个保存在服务器临时文件夹中的文件。其他方法只返回图片的字符串。

<?php

class GrabAndSave {

    public $imageName;
    public $imageFolderPath = 'remote-uploads/'; // Folder to Cache Amazon Images in
    public $remote_image_url;
    public $local_image_url;
    public $temp_file  = '';
    public $temp_file_prefix = 'tmp';

    public function __construct(){
        //
    }

    public function fetch_image($image_url) {

        // check if CURL is installed
        if (function_exists('curl_init')){
            return $this->curl_fetch_image($image_url);
        // Check if PHP allows file_get_contents to use URL instead of file paths
        }elseif(ini_get('allow_url_fopen')){
            return $this->fopen_fetch_image($image_url);
        // Try Sockets
        }else{
            return $this->sockets_fetch_image($image_url);
        }
    }



    public function curl_fetch_image($image_url) {

        if (function_exists('curl_init')) {
             //Initialize a new resource for curl
             $ch = curl_init();

             //Set the url the retrieve
             curl_setopt($ch, CURLOPT_URL, $image_url);

             //Return the value instead of outputting to the browser
             curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

             $image = curl_exec($ch);
             curl_close($ch);

             if ($image) {
                  //Do stuff with the image
                  return $image;
             } else {
                  //Show error message
             }

        }else{
            die('cURL is not enabled on this server.');
        }
    }


    public function fopen_fetch_image($url) {
        $image = file_get_contents($url, false, $context);
        return $image;
    }


    public function sockets_fetch_image($image_url)
    {
        if($this->temp_file)
        {
            throw new Exception('Resource has been downloaded already.');
        }

        $this->temp_file = tempnam(sys_get_temp_dir(), $this->temp_file_prefix);

        $srcResource = fopen($image_url, 'r');
        $destResource = fopen($this->temp_file, 'w+');

        stream_copy_to_stream($srcResource, $destResource);

        return $this->temp_file;
    }


    public function save_image($image_filename, $raw_image_string){
            $local_image_file = fopen($this->imageFolderPath . $image_filename, 'w+');
            chmod($this->imageFolderPath . $image_filename, 0755);
            fwrite($local_image_file, $raw_image_string);
            fclose($local_image_file);
    }

}

使用远程 URL 图片上传预览 StackOverflow 图片对话框...

【问题讨论】:

  • 任何图像文件或某些类型(png、jpg)?
  • @lxg 我认为通常的 png、jpg、gif
  • 好的,接下来看看我的回答。 :)

标签: php gd


【解决方案1】:

一个简单有效的方法是在文件上使用getimagesize。合法文件将为许多常见的图像文件类型提供一组图像元数据。 (顺便说一句,这也可以用来强制执行其他约束,例如图像尺寸。)

请注意,即使是合法图像也可能包含恶意代码,因为过去各种图像查看器都暴露了图像数据流的安全问题。为了保护客户端,它可能会增加一层安全性以在图像传递之前在内部对其进行转换。

【讨论】:

    【解决方案2】:

    你可以

    • 检查响应标头,尤其是“Content-Type”标头
    • 检查文件 mime 类型
    • 检查实际文件签名
    • 尝试从文件中实际创建图像资源
    • 其他...

    【讨论】:

    • Content-Type 和 mime 类型基本相同,很容易伪造。
    • 当然,但是例如“Content-Type”标头可用于在实际将远程文件下载到服务器之前进行检查。可以先发送 HEAD 请求,如果“Content-Type”标头与图像匹配,则通过检查“Content-Length”标头检查文件大小,如果正常,则发送请求下载实际文件并继续其他检查/validations 下载完成后。对于非伪造响应,它将有助于防止下载实际上不是图像或太大的文件。例如,想想当有人插入一个 100 MB 或几 GB 文件的链接时的情况。
    • 1.我们是这里的服务器,所以我们不能“发送请求”。 2. Content-Length 也很容易被操纵。 3.max_upload_size是你想限制上传大小的朋友。
    • 如果您确实阅读了问题的标题(“如何确保下载到服务器的远程图像实际上是使用 PHP 生成的图像?”),以及问题本身,您会注意到他们为用户提供了输入/插入某些图像 url 的选项,并且他们的服务器将获取并处理远程文件。因此,正如您在有问题的源代码中看到的那样,他们在他们的服务器上使用curlfile_get_contentssockets 来“发送请求”到远程 url 并将文件提取到他们的服务器,在处理之前。所以,在这种情况下,max_upload_size 没有帮助;)
    • 好吧,对不起,你说得对。我被上传对话框激怒了。
    猜你喜欢
    • 2015-01-01
    • 2014-12-01
    • 1970-01-01
    • 2013-03-04
    • 1970-01-01
    • 2016-03-16
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    相关资源
    最近更新 更多