php 验证 base64 编码的图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12658661/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 03:59:18  来源:igfitidea点击:

Validating base64 encoded images

phpsecurityvalidationbase64

提问by ssh2ksh

I'm building an application that allows the user to POSTHTML5 canvas data that is then encoded in base64 and displayed to all users. I am considering parsing the data into an actual .png file and storing on the server, but the base64 route allows me to store the images in a database and minimize requests. The images are unique, few, and the page won't be refreshed often.

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

A bit of jQuery will take the canvas data, data:image/png;base64,iVBORw...and passes it along to a PHP script that wraps it like so: <img src="$data"></img>

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

However, security is cornerstone and need to validate the base64 canvas data to prevent passing malicious data in the POSTrequest. My primary concern is to prevent external URLs from being injected into the <img>tag and being requested on page load.

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

I currently have a setup like this:

我目前有这样的设置:

$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;

I want to make sure my current setup is safe enough to prevent external URLs from being inserted into the <img>tag, and if not, what can be done to further validate the image data?

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

回答by thewebguy

One way of doing this would be to actually create an image file from the base64 data, then verify the image itself with PHP. There might be a simpler way of doing this, but this way should certainly work.

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

Keep in mind that this only really works for PNGs, you'll need to add some logic if you're planning on allowing more file types (GIF, JPG).

请记住,这仅适用于 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;
}

?>

回答by curiosity26

If you're using php 5.4+, I've revised the above to be a bit more concise.

如果您使用的是 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;
}

回答by Amritpal Singh Boparai

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;
}

回答by user3376563

Since I don't have enough points to comment, I am posting an updated version of thewebguy's code. This is for people hosting on services such as Heroku where you can't store images.

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

The credit for pointing out stream wrapper to Pekka (Pekka's answer)

向 Pekka 指出流包装器的功劳(Pekka 的回答

This code assumes you implement the class and stream wrapper from: PHP Example on Stream Wrapper

此代码假设您实现了来自以下内容的类和流包装器: 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;
}

?>

I hope this helps someone.

我希望这可以帮助别人。

回答by Bashirpour

$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';
}

回答by yashpal singh

hello guys you can validate base64 encode image code using getimagesize() function just use the given below code:

大家好,您可以使用 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" ;
}
?>

*replace any file with any file source that of you want base64_encode code

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

回答by Sylvester

This is a quick solution if do not have the GD library to be installed, and you do not want to install it.

如果没有要安装的 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;
}