【问题标题】:Validating base64 encoded images验证 base64 编码图像
【发布时间】:2012-09-30 04:31:48
【问题描述】:

我正在构建一个应用程序,允许用户POST HTML5 画布数据,然后以 base64 编码并显示给所有用户。我正在考虑将数据解析为实际的 .png 文件并存储在服务器上,但是 base64 路由允许我将图像存储在数据库中并最小化请求。图片独特,很少,页面不会经常刷新。

一点 jQuery 将获取画布数据 data:image/png;base64,iVBORw... 并将其传递给 PHP 脚本,该脚本如下所示:<img src="$data"></img>

但是,安全性是基石,需要验证 base64 画布数据,以防止在 POST 请求中传递恶意数据。我主要关心的是防止外部 URL 被注入 <img> 标记并在页面加载时被请求。

我目前有这样的设置:

$data = (isset($_POST['canvas']) && is_string($_POST['canvas'])) ? $_POST['canvas'] : null;
$base = str_replace('data:image/png;base64,', '', $data);
$regx = '~^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$~'

if ((substr($data, 0, 22)) !== 'data:image/png;base64,')
{
  // Obviously fake, doesn't contain the expected first 22 characters.
  return false;
}

if ((base64_encode(base64_decode($base64, true))) !== $base64)
{
  // Decoding and re-encoding the data fails, something is wrong
  return false;
}

if ((preg_match($regx, $base64)) !== 1) 
{
  // The data doesn't match the regular expression, discard
  return false;
}

return true;

我想确保我当前的设置足够安全,以防止将外部 URL 插入到 <img> 标记中,如果没有,可以做些什么来进一步验证图像数据?

【问题讨论】:

标签: php security validation base64


【解决方案1】:

这样做的一种方法是从 base64 数据实际创建一个图像文件,然后使用 PHP 验证图像本身。可能有一种更简单的方法可以做到这一点,但这种方法肯定行得通。

请记住,这仅适用于 PNG,如果您计划允许更多文件类型(GIF、JPG),则需要添加一些逻辑。

<?

$base64 = "[insert base64 code here]";
if (check_base64_image($base64)) {
    print 'Image!';
} else {
    print 'Not an image!';
}

function check_base64_image($base64) {
    $img = imagecreatefromstring(base64_decode($base64));
    if (!$img) {
        return false;
    }

    imagepng($img, 'tmp.png');
    $info = getimagesize('tmp.png');

    unlink('tmp.png');

    if ($info[0] > 0 && $info[1] > 0 && $info['mime']) {
        return true;
    }

    return false;
}

?>

【讨论】:

  • 这很棒!公认。如果图像数据无效,imagepng 将抛出 supplied argument is not a valid Image resource,因此我将该函数包装在 if 语句中以在失败时捕获它。
  • 可以使用imagecreatefrompng($base64),如果返回false,表示不是图片
  • 请注意,您必须删除“data:image/png;”在使用此函数之前从您的 base64 代码开始。
  • 我在进行代码审查时偶然发现了这个答案。请不要在生产环境中使用它,因为它非常不安全。您应该从不对未知数据使用imagecreate 函数之一,因为它会为整个图像分配位图缓冲区。创建 100+MP 的小 png 文件非常容易,这会毁了你的一天。相反,将图像写入临时文件并在其上调用getimagesize。 (注意事项:php.net/getimagesize)如果必须分配图像,则只需使用imagesximagesy 直接查询尺寸。
【解决方案2】:

如果您使用的是 php 5.4+,我将上面的内容修改得更简洁。

function check_base64_image($data, $valid_mime) {
    $img = imagecreatefromstring($data);

    if (!$img) {
        return false;
    }

    $size = getimagesizefromstring($data);

    if (!$size || $size[0] == 0 || $size[1] == 0 || !$size['mime']) {
        return false;
    }

    return true;
}

【讨论】:

  • 喜欢它。几个注意事项... A)$valid_mimeargument 似乎是多余的,B)这需要安装 GD 库 - 在 Ubuntu 上:sudo apt-get install php5-gd &amp;&amp; sudo service apache2 restart
【解决方案3】:
function RetrieveExtension($data){
    $imageContents = base64_decode($data);

    // If its not base64 end processing and return false
    if ($imageContents === false) {
        return false;
    }

    $validExtensions = ['png', 'jpeg', 'jpg', 'gif'];

    $tempFile = tmpfile();

    fwrite($tempFile, $imageContents);

    $contentType = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $tempFile);

    fclose($tempFile);

    if (substr($contentType, 0, 5) !== 'image') {
        return false;
    }

    $extension = ltrim($contentType, 'image/');

    if (!in_array(strtolower($extension), $validExtensions)) {
        return false;
    }

    return $extension;
}

【讨论】:

    【解决方案4】:

    由于我没有足够的评论点数,我发布了 webguy 代码的更新版本。这适用于托管在 Heroku 等无法存储图像的服务上的人。

    向 Pekka 指出流包装器的功劳 (Pekka's answer)

    此代码假定您从以下位置实现类和流包装器: PHP Example on Stream Wrapper

    <?
    
    $base64 = "[insert base64 code here]";
    if (check_base64_image($base64)) {
        print 'Image!';
    } else {
        print 'Not an image!';
    }
    
    function check_base64_image($base64) {
        $img = imagecreatefromstring(base64_decode($base64));
        if (!$img) {
            return false;
        }
    
        ob_start();
        if(!imagepng($img)) {
    
            return false;
        }
        $imageTemp = ob_get_contents(); 
        ob_end_clean();
    
        // Set a temporary global variable so it can be used as placeholder
        global $myImage; $myImage = "";
    
        $fp = fopen("var://myImage", "w");
        fwrite($fp, $imageTemp);
        fclose($fp);    
    
        $info = getimagesize("var://myImage");
        unset($myvar);
        unset($imageTemp);
    
        if ($info[0] > 0 && $info[1] > 0 && $info['mime']) {
            return true;
        }
    
        return false;
    }
    
    ?>
    

    我希望这对某人有所帮助。

    【讨论】:

      【解决方案5】:

      大家好,您可以使用 getimagesize() 函数验证 base64 编码图像代码,只需使用以下代码:

      <?php
      $array=getimagesize("data:image/gif; base64 , '.base64_encode('any file').'");
      $e=explode("/",$array['mime']);
      if($e[0]=="image")
      {
      echo "file is image file" ;
      }
      ?>
      

      *用你想要base64_encode代码的任何文件源替换任何文件

      【讨论】:

        【解决方案6】:
        $str = 'your  base64 code' ;
        
        if (base64_encode(base64_decode($str, true)) === $str && imagecreatefromstring(base64_decode($str))) {
            echo 'Success! The String entered match base64_decode and is Image';
        }
        

        【讨论】:

        • 请解释一下你的解决方案并解释一下,尤其是因为这个问题已经有 5 年历史了
        【解决方案7】:

        我也有同样的需要,所以就这么做了。这消除了通过直接读取字符串并使用 getimagesizefromstring 函数来保存文件的需要。

         public function validateImg($data)
            {
                try {
                    $binary = base64_decode(explode(',', $data)[1]);
                    $data = getimagesizefromstring($binary);
                } catch (\Exception $e) {
                  return false;
                }
        
                $allowed = ['image/jpeg', 'image/png', 'image/gif'];
        
                if (!$data) {
                    return false;
                }
        
                if (!empty($data[0]) && !empty($data[0]) && !empty($data['mime'])) {
                    if (in_array($data['mime'], $allowed)) {
                        return true;
                    }
                }
        
                return false;
            }

        【讨论】:

          【解决方案8】:

          如果没有要安装的 GD 库,并且您不想安装它,这是一个快速的解决方案。

              //Returns a boolean
              public function validateBase64Image($data) {
              //Decode Base 64 data
              $imgData = base64_decode($data);
          
              //Returns a magic database resource on success or FALSE on failure.
              $fileInfo = finfo_open();
              if(!$fileInfo) {
                  return false;
              }
          
              //Returns a textual description of the string argument, or FALSE if an error occurred.
              //In the case of an image: image/<image extension> e.g. image/jpeg
              $mimeType = finfo_buffer($fileInfo, $imgData, FILEINFO_MIME_TYPE);
              if(!$mimeType) {
                  return false;
              }
          
              //Gets an array
              $mimeArray=explode("/",$mimeType);
              //Validate the file is an image
              if($mimeArray[0]=="image") {
                  return true;
              }
              return false;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-04-01
            • 2016-12-26
            • 1970-01-01
            • 1970-01-01
            • 2012-12-26
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多