php中的安全图像上传


Secure image upload in php

我正在制作一个图像上传功能,我可以在代码中重复使用,它必须100%安全。请告诉我,如果你能发现和安全漏洞在我的初始代码;

function Upload($file)
{
    list($width,$height,$type,$attr) = getimagesize($file);
    $mime = image_type_to_mime_type($type);
    
    if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png")) 
    {
        return 'Error3: Upload file type un-recognized. Only .JPG or .PNG images allowed';
    }else{
        $Newname = md5('sillysalt'.time());
        if (move_uploaded_file($file, 'images/'.$Newname.$type)) 
        {
            return 'Uploaded!';
        }else{
            return 'Server Error!';
        } 
    }
}

更新这是我在您的帮助和一些研究下取得的进展,请告诉我您的想法。我不太介意速度,对我来说,这一切都是为了100%安全,或者接近

function Upload($file)
{
    list($width,$height,$type,$attr) = getimagesize($file);
    $mime = image_type_to_mime_type($type);
    $folder = 'images/';
    
    // mime checks add a layer of security that keeps out less sophisticated attackers 
    if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png")) 
    {
        return 'Error3: Upload file type un-recognized. Only .JPG or .PNG images allowed';
    }else{
        // If the file has no width its not a valid image
        if(!$width)
        {
            $Newname = md5('sillysalt'.time());
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mime2 = finfo_file($finfo, $folder.$Newname);
            
            // Should I remove this second mime check? since the info comes form the same spoofable source in the image
            if(($mime != "image/jpeg") && ($mime != "image/pjpeg") && ($mime != "image/png")) 
            {
                $fileType = exif_imagetype($file);
                $allowed = array(IMAGETYPE_JPEG, IMAGETYPE_PNG);
                if(!in_array($fileType, $allowed))
                {
                    // don't overwrite an existing file
                    $i = 0;
                    $parts = pathinfo($file);
                    while(file_exists($folder . $name))
                    {
                        $i++;
                        $name = $Newname."-".$i.".".$parts["extension"];
                    }
    
                    if(move_uploaded_file($file, $folder.$name)) 
                    {
                        // set good permissions for the file
                        chmod($name, 0644);
                        return 'Uploaded!';
                    }else{
                        return 'Server Error!';
                    } 
                }
            }
        }
    }
}

只要不使用FileInfo(http://www.php.net/manual/en/ref.fileinfo.php)从php扩展来检查mime类型,您的功能根本不安全(稍后您会想上传pdf、excellent等)
此外,md5比md5只会增加碰撞的机会。

L.E:应该用下面这样简单的方法:

function getExtensionToMimeTypeMapping() {
    return array(
        'ai'=>'application/postscript',
        'aif'=>'audio/x-aiff',
        'aifc'=>'audio/x-aiff',
        'aiff'=>'audio/x-aiff',
        'anx'=>'application/annodex',
        'asc'=>'text/plain',
        'au'=>'audio/basic',
        'avi'=>'video/x-msvideo',
        'axa'=>'audio/annodex',
        'axv'=>'video/annodex',
        'bcpio'=>'application/x-bcpio',
        'bin'=>'application/octet-stream',
        'bmp'=>'image/bmp',
        'c'=>'text/plain',
        'cc'=>'text/plain',
        'ccad'=>'application/clariscad',
        'cdf'=>'application/x-netcdf',
        'class'=>'application/octet-stream',
        'cpio'=>'application/x-cpio',
        'cpt'=>'application/mac-compactpro',
        'csh'=>'application/x-csh',
        'css'=>'text/css',
        'csv'=>'text/csv',
        'dcr'=>'application/x-director',
        'dir'=>'application/x-director',
        'dms'=>'application/octet-stream',
        'doc'=>'application/msword',
        'drw'=>'application/drafting',
        'dvi'=>'application/x-dvi',
        'dwg'=>'application/acad',
        'dxf'=>'application/dxf',
        'dxr'=>'application/x-director',
        'eps'=>'application/postscript',
        'etx'=>'text/x-setext',
        'exe'=>'application/octet-stream',
        'ez'=>'application/andrew-inset',
        'f'=>'text/plain',
        'f90'=>'text/plain',
        'flac'=>'audio/flac',
        'fli'=>'video/x-fli',
        'flv'=>'video/x-flv',
        'gif'=>'image/gif',
        'gtar'=>'application/x-gtar',
        'gz'=>'application/x-gzip',
        'h'=>'text/plain',
        'hdf'=>'application/x-hdf',
        'hh'=>'text/plain',
        'hqx'=>'application/mac-binhex40',
        'htm'=>'text/html',
        'html'=>'text/html',
        'ice'=>'x-conference/x-cooltalk',
        'ief'=>'image/ief',
        'iges'=>'model/iges',
        'igs'=>'model/iges',
        'ips'=>'application/x-ipscript',
        'ipx'=>'application/x-ipix',
        'jpe'=>'image/jpeg',
        'jpeg'=>'image/jpeg',
        'jpg'=>'image/jpeg',
        'js'=>'application/x-javascript',
        'kar'=>'audio/midi',
        'latex'=>'application/x-latex',
        'lha'=>'application/octet-stream',
        'lsp'=>'application/x-lisp',
        'lzh'=>'application/octet-stream',
        'm'=>'text/plain',
        'man'=>'application/x-troff-man',
        'me'=>'application/x-troff-me',
        'mesh'=>'model/mesh',
        'mid'=>'audio/midi',
        'midi'=>'audio/midi',
        'mif'=>'application/vnd.mif',
        'mime'=>'www/mime',
        'mov'=>'video/quicktime',
        'movie'=>'video/x-sgi-movie',
        'mp2'=>'audio/mpeg',
        'mp3'=>'audio/mpeg',
        'mpe'=>'video/mpeg',
        'mpeg'=>'video/mpeg',
        'mpg'=>'video/mpeg',
        'mpga'=>'audio/mpeg',
        'ms'=>'application/x-troff-ms',
        'msh'=>'model/mesh',
        'nc'=>'application/x-netcdf',
        'oga'=>'audio/ogg',
        'ogg'=>'audio/ogg',
        'ogv'=>'video/ogg',
        'ogx'=>'application/ogg',
        'oda'=>'application/oda',
        'pbm'=>'image/x-portable-bitmap',
        'pdb'=>'chemical/x-pdb',
        'pdf'=>'application/pdf',
        'pgm'=>'image/x-portable-graymap',
        'pgn'=>'application/x-chess-pgn',
        'png'=>'image/png',
        'pnm'=>'image/x-portable-anymap',
        'pot'=>'application/mspowerpoint',
        'ppm'=>'image/x-portable-pixmap',
        'pps'=>'application/mspowerpoint',
        'ppt'=>'application/mspowerpoint',
        'ppz'=>'application/mspowerpoint',
        'pre'=>'application/x-freelance',
        'prt'=>'application/pro_eng',
        'ps'=>'application/postscript',
        'qt'=>'video/quicktime',
        'ra'=>'audio/x-realaudio',
        'ram'=>'audio/x-pn-realaudio',
        'ras'=>'image/cmu-raster',
        'rgb'=>'image/x-rgb',
        'rm'=>'audio/x-pn-realaudio',
        'roff'=>'application/x-troff',
        'rpm'=>'audio/x-pn-realaudio-plugin',
        'rtf'=>'text/rtf',
        'rtx'=>'text/richtext',
        'scm'=>'application/x-lotusscreencam',
        'set'=>'application/set',
        'sgm'=>'text/sgml',
        'sgml'=>'text/sgml',
        'sh'=>'application/x-sh',
        'shar'=>'application/x-shar',
        'silo'=>'model/mesh',
        'sit'=>'application/x-stuffit',
        'skd'=>'application/x-koan',
        'skm'=>'application/x-koan',
        'skp'=>'application/x-koan',
        'skt'=>'application/x-koan',
        'smi'=>'application/smil',
        'smil'=>'application/smil',
        'snd'=>'audio/basic',
        'sol'=>'application/solids',
        'spl'=>'application/x-futuresplash',
        'spx'=>'audio/ogg',
        'src'=>'application/x-wais-source',
        'step'=>'application/STEP',
        'stl'=>'application/SLA',
        'stp'=>'application/STEP',
        'sv4cpio'=>'application/x-sv4cpio',
        'sv4crc'=>'application/x-sv4crc',
        'swf'=>'application/x-shockwave-flash',
        't'=>'application/x-troff',
        'tar'=>'application/x-tar',
        'tcl'=>'application/x-tcl',
        'tex'=>'application/x-tex',
        'texi'=>'application/x-texinfo',
        'texinfo'=>'application/x-texinfo',
        'tif'=>'image/tiff',
        'tiff'=>'image/tiff',
        'tr'=>'application/x-troff',
        'tsi'=>'audio/TSP-audio',
        'tsp'=>'application/dsptype',
        'tsv'=>'text/tab-separated-values',
        'txt'=>'text/plain',
        'unv'=>'application/i-deas',
        'ustar'=>'application/x-ustar',
        'vcd'=>'application/x-cdlink',
        'vda'=>'application/vda',
        'viv'=>'video/vnd.vivo',
        'vivo'=>'video/vnd.vivo',
        'vrml'=>'model/vrml',
        'wav'=>'audio/x-wav',
        'wrl'=>'model/vrml',
        'xbm'=>'image/x-xbitmap',
        'xlc'=>'application/vnd.ms-excel',
        'xll'=>'application/vnd.ms-excel',
        'xlm'=>'application/vnd.ms-excel',
        'xls'=>'application/vnd.ms-excel',
        'xlw'=>'application/vnd.ms-excel',
        'xml'=>'application/xml',
        'xpm'=>'image/x-xpixmap',
        'xspf'=>'application/xspf+xml',
        'xwd'=>'image/x-xwindowdump',
        'xyz'=>'chemical/x-pdb',
        'zip'=>'application/zip',
    );
}
function getMimeType($filePath) {
    if (!is_file($filePath)) {
        return false;
    }
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $filePath);
    finfo_close($finfo);
    return $mime;
}
function upload($filePath, $destinationDir = 'images', array $allowedMimes = array()) {
    if (!is_file($filePath) || !is_dir($destinationDir)) {
        return false;
    }
    if (!($mime = getMimeType($filePath))) {
        return false;
    }
    if (!in_array($mime, $allowedMimes)) {
        return false;
    }
    $ext = null;
    $extMapping = getExtensionToMimeTypeMapping();
    foreach ($extMapping as $extension => $mimeType) {
        if ($mimeType == $mime) {
            $ext = $extension;
            break;
        }
    }
    if (empty($ext)) {
        $ext = pathinfo($filePath, PATHINFO_EXTENSION);
    }
    if (empty($ext)) {
        return false;
    }
    $fileName = md5(uniqid(rand(0, time()), true)) . '.' . $ext;
    $newFilePath = $destinationDir.'/'.$fileName;
    if(!rename($filePath, $newFilePath)) {
        return false;
    }
    return $fileName;
}
// use it 
if (isset($_FILES['something']['tmp_name'])) {
    $file = $_FILES['something']['tmp_name'];
    $storagePath = 'images'; // this is relative to this script, better use absolute path.
    $allowedMimes = array('image/png', 'image/jpg', 'image/gif', 'image/pjpeg');
    $fileName = upload($file, $storagePath, $allowedMimes);
    if (!$fileName) {
        exit ('Your file type is not allowed.');
    } else {
         // check if file is image, optional, in case you allow multiple types of files.
         // $imageInfo = @getimagesize($storagePath.'/'.$fileName);
        exit ("Your uploaded file is {$fileName} and can be found at {$storagePath}/{$fileName}");
    }
}

停止按mime类型过滤它是不安全的!

客户端可以发送具有不同文件扩展名的不同mime类型。因此,您需要检查文件扩展名。

编辑:

我想我被误解了,我写的答案告诉我们,检查mime类型来确定文件类型不是一个好方法,确定文件类型的最好方法是检查文件扩展名。所以,我并不是说检查文件扩展名就足够了。只检查文件扩展名或mime类型是不安全的。

该怎么办?1-检查mime类型

2-检查文件扩展名

3-解码文件名

4-检查文件内容的一致性(如果可能)

5-重新生成文件内容(如果可能)

我知道攻击者可以通过使用"空字节破解"answers"mime类型绕过"绕过第一种和第二种方法

因此,3、4和5对安全性非常重要。