您的位置:首页 > 编程语言 > Java开发

Java判断文件类型 (转)

2014-02-10 11:41 253 查看
通常,在WEB系统中,上传文件时都需要做文件的类型校验,大致有如下几种方法:

1. 通过后缀名,如exe,jpg,bmp,rar,zip等等。

2. 通过读取文件,获取文件的Content-type来判断。

3. 通过读取文件流,根据文件流中特定的一些字节标识来区分不同类型的文件。

4. 若是图片,则通过缩放来判断,可以缩放的为图片,不可以的则不是。

然而,在安全性较高的业务场景中,1,2两种方法的校验会被轻易绕过。

1. 伪造后缀名,如图片的,非常容易修改。

2. 伪造文件的Content-type,这个稍微复杂点,为了直观,截图如下:





3.较安全,但是要读取文件,并有16进制转换等操作,性能稍差,但能满足一定条件下对安全的要求,所以建议使用。

  但是文件头的信息也可以伪造,截图如下,对于图片可以采用图片缩放或者获取图片宽高的方法避免伪造头信息漏洞。





                                              被伪装成gif的恶意图片文件

对应的Java代码如下:

import java.awt.image.BufferedImage;  

import java.io.File;  

import java.io.FileInputStream;  

import java.io.FileNotFoundException;  

import java.io.IOException;  

import java.io.InputStream;  

import java.util.HashMap;  

import java.util.Iterator;  

import java.util.Map;  

import java.util.Map.Entry;  

import javax.imageio.ImageIO;  

import javax.imageio.ImageReader;  

import javax.imageio.stream.ImageInputStream;
 

    

public class FileTypeTest    

{    

    public
final static Map FILE_TYPE_MAP = new HashMap();
   

        

    private
FileTypeTest(){}    

    static{
   

        getAllFileType();
 //初始化文件类型信息
   

    }
   

        

       

    private
static void getAllFileType()    

    {
   

        FILE_TYPE_MAP.put("jpg",
"FFD8FF"); //JPEG (jpg)    

        FILE_TYPE_MAP.put("png",
"89504E47");  //PNG (png)
   

        FILE_TYPE_MAP.put("gif",
"47494638");  //GIF (gif)
   

        FILE_TYPE_MAP.put("tif",
"49492A00");  //TIFF (tif)
   

        FILE_TYPE_MAP.put("bmp",
"424D"); //Windows Bitmap (bmp)
   

        FILE_TYPE_MAP.put("dwg",
"41433130"); //CAD (dwg)    

        FILE_TYPE_MAP.put("html",
"68746D6C3E");  //HTML (html)
   

        FILE_TYPE_MAP.put("rtf",
"7B5C727466");  //Rich Text Format (rtf)
   

        FILE_TYPE_MAP.put("xml",
"3C3F786D6C");    

        FILE_TYPE_MAP.put("zip",
"504B0304");    

        FILE_TYPE_MAP.put("rar",
"52617221");    

        FILE_TYPE_MAP.put("psd",
"38425053");  //Photoshop (psd)
   

        FILE_TYPE_MAP.put("eml",
"44656C69766572792D646174653A");
 //Email [thorough only] (eml)
  

        FILE_TYPE_MAP.put("dbx",
"CFAD12FEC5FD746F");  //Outlook Express
(dbx)    

        FILE_TYPE_MAP.put("pst",
"2142444E");  //Outlook (pst)
   

        FILE_TYPE_MAP.put("xls",
"D0CF11E0");  //MS Word
   

        FILE_TYPE_MAP.put("doc",
"D0CF11E0");  //MS Excel 注意:word 和
excel的文件头一样    

        FILE_TYPE_MAP.put("mdb",
"5374616E64617264204A");  //MS Access (mdb)
   

        FILE_TYPE_MAP.put("wpd",
"FF575043"); //WordPerfect (wpd)
    

        FILE_TYPE_MAP.put("eps",
"252150532D41646F6265");    

        FILE_TYPE_MAP.put("ps",
"252150532D41646F6265");    

        FILE_TYPE_MAP.put("pdf",
"255044462D312E");  //Adobe Acrobat (pdf)
   

        FILE_TYPE_MAP.put("qdf",
"AC9EBD8F");  //Quicken (qdf)
   

        FILE_TYPE_MAP.put("pwl",
"E3828596");  //Windows Password (pwl)
   

        FILE_TYPE_MAP.put("wav",
"57415645");  //Wave (wav)
   

        FILE_TYPE_MAP.put("avi",
"41564920");    

        FILE_TYPE_MAP.put("ram",
"2E7261FD");  //Real Audio (ram)
   

        FILE_TYPE_MAP.put("rm",
"2E524D46");  //Real Media (rm)
   

        FILE_TYPE_MAP.put("mpg",
"000001BA");  //
   

        FILE_TYPE_MAP.put("mov",
"6D6F6F76");  //Quicktime (mov)
   

        FILE_TYPE_MAP.put("asf",
"3026B2758E66CF11"); //Windows Media (asf)
   

        FILE_TYPE_MAP.put("mid",
"4D546864");  //MIDI (mid)
   

    }
   

    

    public
static void main(String[] args) throws Exception
   

    {
   

        File
f = new File("c://aaa.gif");    

        if
(f.exists())    

        {
   

            String
filetype1 = getImageFileType(f);
   

            System.out.println(filetype1);
   

            String
filetype2 = getFileByFile(f);    

            System.out.println(filetype2);
   

        }
   

    }
   

    

       

    public
final static String getImageFileType(File f)
   

    {
   

        if
(isImage(f))  

        {
 

            try
 

            {
 

                ImageInputStream
iis = ImageIO.createImageInputStream(f);
 

                Iterator
iter = ImageIO.getImageReaders(iis);  

                if
(!iter.hasNext())  

                {
 

                    return
null;  

                }
 

                ImageReader
reader = iter.next();  

                iis.close();
 

                return
reader.getFormatName();  

            }
 

            catch
(IOException e)  

            {
 

                return
null;  

            }
 

            catch
(Exception e)  

            {
 

                return
null;  

            }
 

        }
 

        return
null;  

    }
   

    

       

    public
final static String getFileByFile(File file)
   

    {
   

        String
filetype = null;    

        byte[]
b = new byte[50];    

        try
   

        {
   

            InputStream
is = new FileInputStream(file);
   

            is.read(b);
   

            filetype
= getFileTypeByStream(b);    

            is.close();
   

        }
   

        catch
(FileNotFoundException e)    

        {
   

            e.printStackTrace();
   

        }
   

        catch
(IOException e)    

        {
   

            e.printStackTrace();
   

        }
   

        return
filetype;    

    }
   

        

       

    public
final static String getFileTypeByStream(byte[] b)
   

    {
   

        String
filetypeHex = String.valueOf(getFileHexString(b));
   

        Iterator>
entryiterator = FILE_TYPE_MAP.entrySet().iterator();
   

        while
(entryiterator.hasNext()) {    

            Entry
entry =  entryiterator.next();
   

            String
fileTypeHexValue = entry.getValue();
   

            if
(filetypeHex.toUpperCase().startsWith(fileTypeHexValue)) {
   

                return
entry.getKey();    

            }
   

        }
   

        return
null;    

    }
   

        

     

    public
static final boolean isImage(File file){
 

        boolean
flag = false;  

        try
 

        {
 

            BufferedImage
bufreader = ImageIO.read(file);  

            int
width = bufreader.getWidth();  

            int
height = bufreader.getHeight();  

            if(width==0
|| height==0){  

                flag
= false;  

            }else
{  

                flag
= true;  

            }
 

        }
 

        catch
(IOException e)  

        {
 

            flag
= false;  

        }catch
(Exception e) {  

            flag
= false;  

        }
 

        return
flag;  

    }
 

      

       

    public
final static String getFileHexString(byte[] b)
   

    {
   

        StringBuilder
stringBuilder = new StringBuilder();
   

        if
(b == null || b.length <= 0)
   

        {
   

            return
null;    

        }
   

        for
(int i = 0; i < b.length; i++)
   

        {
   

            int
v = b[i] & 0xFF;    

            String
hv = Integer.toHexString(v);    

            if
(hv.length() < 2)    

            {
   

                stringBuilder.append(0);
   

            }
   

            stringBuilder.append(hv);
   

        }
   

        return
stringBuilder.toString();    

    }
   

}  

这样,不管是传入的文件有后缀名,还是无后缀名,或者修改了后缀名,真正获取到的才是该文件的实际类型,这样避免了一些想通过修改后缀名或者Content-type信息来攻击的因素。但是性能与安全永远是无法同时完美的,安全的同时付出了读取文件的代价。本人建议可采用后缀名与读取文件的方式结合校验,毕竟攻击是少数,后缀名的校验能排除大多数用户,在后缀名获取不到时再通过获取文件真实类型校验,这样来适当提高性能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: