29

我正在构建一个应用程序,允许用户使用POSTHTML5 画布数据,然后以 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>标签,如果没有,可以做些什么来进一步验证图像数据?

4

8 回答 8

31

这样做的一种方法是从 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;
}

?>
于 2012-09-30T04:55:07.123 回答
5

如果您使用的是 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;
}
于 2017-01-04T15:52:19.823 回答
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;
}
于 2014-07-18T10:29:00.763 回答
3

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

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

此代码假定您从以下位置实现类和流包装器: ​​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;
}

?>

我希望这可以帮助别人。

于 2016-06-29T23:52:15.203 回答
3
$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';
}
于 2017-10-21T12:50:26.647 回答
3

大家好,您可以使用 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代码的任何文件源替换任何文件

于 2018-08-18T17:56:38.847 回答
2

我有同样的需要,就这样做了。这消除了通过直接读取字符串并使用 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;
    }

于 2020-12-28T14:43:19.543 回答
0

如果没有要安装的 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;
}
于 2020-03-13T09:37:15.803 回答