如何验证数据URL方案图像

问题描述

过去,我制作了一个拖放式画布图像裁剪器,现在我需要在新项目中重复使用它。

回到过去我曾经打电话给 toBlob() 的日子里,使用Ajax请求发送Blob并使用可验证所有内容文件类处理文件服务器端,但是这次我必须将其作为普通表单发布请求进行处理。

这次,我基本上是用 toDataURL 创建一个DOM字符串,然后更新输入字段的值并使用该数据服务器端,对数据进行格式化而且我必须处理字符串

这是一个简短的代码段(是较大代码库的一部分),以显示我如何创建字符串并更新输入字段

var tempCanvas = document.createElement('canvas');
var tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = this.containerWidth;
tempCanvas.height = this.containerHeiht;
tempCtx.drawImage(
        this.ctx.canvas,this.cutoutWidth,this.containerWidth,this.containerWidth
        );

var dataurl = tempCanvas.toDataURL("image/png");
selectElems('cc-upload-blob__input','i').value = dataurl;

然后在服务器端,我像这样处理字符串

$picture = $post['cc-upload-blob'];
if (! empty($picture)) {
    if (preg_match('/^data:image\/(\w+);base64,/',$picture,$type)) {
        $picture = substr($picture,strpos($picture,',') + 1);

        $type = strtolower($type[1]); // jpg,png,gif

        if (! in_array($type,[ 'jpg','jpeg','gif','png' ])) {
            throw new \Exception('invalid image type');
        }
        $picture = str_replace( ' ','+',$picture );
        $picture = base64_decode($picture);

        if ($picture === false) {
            throw new \Exception('base64_decode Failed');
        }
    } else {
        throw new \Exception('did not match data URI with image data');
    }

    $pictureName = 'bla.{$type}';
    $picturePath = '/pictures/'.$pictureName;
    if (file_put_contents($picturePath,$picture)) {
        echo '<h1>Uploaded</h1>';
    } else {
        echo '<h1>Not Uploaded</h1>';
    }
}

此演示工作正常,它上传一个图像,PHP脚本删除了一些不必要的数据,从字符串中获取图像类型,对base64进行解码。.

我担心的是解码后的数据?我如何验证这些数据?我的意思是,这有任何安全性吗?是否可以仅对恶意代码进行编码,然后将其附加到类似于普通字符串的字符串中?然后解码的数据将是恶意的?

在将数据传递给 file_put_contents() 之前,我知道数据将使用有效的扩展名从数组中修复,但是仍然吗?有什么需要关注的吗?有没有一种方法可以像平常一样使用$ _FILES数据和PHP上传类来验证这一点?

解决方法

在类似的项目(<canvas>-> JS .toDataURL-> Ajax-> PHP ...)中,我在后端使用此逻辑进行 validate (至少部分)上传的图片是真实的PNG

function checkImage1($image_string) {
    $im = imagecreatefromstring($image_string);
    $valid = ($im != FALSE);
    imagedestroy($im);
    return $valid;
}

function checkImage2($path) {
    if (exif_imagetype($path) != IMAGETYPE_PNG) {
        return false;
    }
    //
    return true;
}

// $image (string) is the base64_decoded data from frontend
// $image_path (string) is where the image will be saved

$check1 = $check2 = false;

$check1 = checkImage1($image);
if ($check1) {
   if ( file_put_contents($image_path,$image) ) {
      $check2 = checkImage2($image_path);
      if (!$check2)
        unlink($image_path);
   }
}

if ($check1 && $check2) { /* all good */ } else { /* bad image */}

参考: