Java根据字节数据判断文件类型

通常,在WEB系统中,上传文件时都需要做文件的类型校验,大致有如下几种方法:

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

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

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

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

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

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

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

 

 

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

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

 

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

对应的Java代码如下:

 

  1. package apistudy;    
  2.     
  3. import java.awt.image.BufferedImage;  
  4. import java.io.File;  
  5. import java.io.FileInputStream;  
  6. import java.io.FileNotFoundException;  
  7. import java.io.IOException;  
  8. import java.io.InputStream;  
  9. import java.util.HashMap;  
  10. import java.util.Iterator;  
  11. import java.util.Map;  
  12. import java.util.Map.Entry;  
  13. import javax.imageio.ImageIO;  
  14. import javax.imageio.ImageReader;  
  15. import javax.imageio.stream.ImageInputStream;  
  16.     
  17. public class FileTypeTest    
  18. {    
  19.     public final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>();    
  20.         
  21.     private FileTypeTest(){}    
  22.     static{    
  23.         getAllFileType();  //初始化文件类型信息    
  24.     }    
  25.         
  26.     /**  
  27.      * Created on 2010-7-1   
  28.      * <p>Discription:[getAllFileType,常见文件头信息]</p>  
  29.      * @author:[shixing_11@sina.com]  
  30.      */    
  31.     private static void getAllFileType()    
  32.     {    
  33.         FILE_TYPE_MAP.put("jpg", "FFD8FF"); //JPEG (jpg)    
  34.         FILE_TYPE_MAP.put("png", "89504E47");  //PNG (png)    
  35.         FILE_TYPE_MAP.put("gif", "47494638");  //GIF (gif)    
  36.         FILE_TYPE_MAP.put("tif", "49492A00");  //TIFF (tif)    
  37.         FILE_TYPE_MAP.put("bmp", "424D"); //Windows Bitmap (bmp)    
  38.         FILE_TYPE_MAP.put("dwg", "41433130"); //CAD (dwg)    
  39.         FILE_TYPE_MAP.put("html", "68746D6C3E");  //HTML (html)    
  40.         FILE_TYPE_MAP.put("rtf", "7B5C727466");  //Rich Text Format (rtf)    
  41.         FILE_TYPE_MAP.put("xml", "3C3F786D6C");    
  42.         FILE_TYPE_MAP.put("zip", "504B0304");    
  43.         FILE_TYPE_MAP.put("rar", "52617221");    
  44.         FILE_TYPE_MAP.put("psd", "38425053");  //Photoshop (psd)    
  45.         FILE_TYPE_MAP.put("eml", "44656C69766572792D646174653A");  //Email [thorough only] (eml)    
  46.         FILE_TYPE_MAP.put("dbx", "CFAD12FEC5FD746F");  //Outlook Express (dbx)    
  47.         FILE_TYPE_MAP.put("pst", "2142444E");  //Outlook (pst)    
  48.         FILE_TYPE_MAP.put("xls", "D0CF11E0");  //MS Word    
  49.         FILE_TYPE_MAP.put("doc", "D0CF11E0");  //MS Excel 注意:word 和 excel的文件头一样    
  50.         FILE_TYPE_MAP.put("mdb", "5374616E64617264204A");  //MS Access (mdb)    
  51.         FILE_TYPE_MAP.put("wpd", "FF575043"); //WordPerfect (wpd)     
  52.         FILE_TYPE_MAP.put("eps", "252150532D41646F6265");    
  53.         FILE_TYPE_MAP.put("ps", "252150532D41646F6265");    
  54.         FILE_TYPE_MAP.put("pdf", "255044462D312E");  //Adobe Acrobat (pdf)    
  55.         FILE_TYPE_MAP.put("qdf", "AC9EBD8F");  //Quicken (qdf)    
  56.         FILE_TYPE_MAP.put("pwl", "E3828596");  //Windows Password (pwl)    
  57.         FILE_TYPE_MAP.put("wav", "57415645");  //Wave (wav)    
  58.         FILE_TYPE_MAP.put("avi", "41564920");    
  59.         FILE_TYPE_MAP.put("ram", "2E7261FD");  //Real Audio (ram)    
  60.         FILE_TYPE_MAP.put("rm", "2E524D46");  //Real Media (rm)    
  61.         FILE_TYPE_MAP.put("mpg", "000001BA");  //    
  62.         FILE_TYPE_MAP.put("mov", "6D6F6F76");  //Quicktime (mov)    
  63.         FILE_TYPE_MAP.put("asf", "3026B2758E66CF11"); //Windows Media (asf)    
  64.         FILE_TYPE_MAP.put("mid", "4D546864");  //MIDI (mid)    
  65.     }    
  66.     
  67.     public static void main(String[] args) throws Exception    
  68.     {    
  69.         File f = new File("c://aaa.gif");    
  70.         if (f.exists())    
  71.         {    
  72.             String filetype1 = getImageFileType(f);    
  73.             System.out.println(filetype1);    
  74.             String filetype2 = getFileByFile(f);    
  75.             System.out.println(filetype2);    
  76.         }    
  77.     }    
  78.     
  79.     /**  
  80.      * Created on 2010-7-1   
  81.      * <p>Discription:[getImageFileType,获取图片文件实际类型,若不是图片则返回null]</p>  
  82.      * @param File  
  83.      * @return fileType  
  84.      * @author:[shixing_11@sina.com]  
  85.      */    
  86.     public final static String getImageFileType(File f)    
  87.     {    
  88.         if (isImage(f))  
  89.         {  
  90.             try  
  91.             {  
  92.                 ImageInputStream iis = ImageIO.createImageInputStream(f);  
  93.                 Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);  
  94.                 if (!iter.hasNext())  
  95.                 {  
  96.                     return null;  
  97.                 }  
  98.                 ImageReader reader = iter.next();  
  99.                 iis.close();  
  100.                 return reader.getFormatName();  
  101.             }  
  102.             catch (IOException e)  
  103.             {  
  104.                 return null;  
  105.             }  
  106.             catch (Exception e)  
  107.             {  
  108.                 return null;  
  109.             }  
  110.         }  
  111.         return null;  
  112.     }    
  113.     
  114.     /**  
  115.      * Created on 2010-7-1   
  116.      * <p>Discription:[getFileByFile,获取文件类型,包括图片,若格式不是已配置的,则返回null]</p>  
  117.      * @param file  
  118.      * @return fileType  
  119.      * @author:[shixing_11@sina.com]  
  120.      */    
  121.     public final static String getFileByFile(File file)    
  122.     {    
  123.         String filetype = null;    
  124.         byte[] b = new byte[50];    
  125.         try    
  126.         {    
  127.             InputStream is = new FileInputStream(file);    
  128.             is.read(b);    
  129.             filetype = getFileTypeByStream(b);    
  130.             is.close();    
  131.         }    
  132.         catch (FileNotFoundException e)    
  133.         {    
  134.             e.printStackTrace();    
  135.         }    
  136.         catch (IOException e)    
  137.         {    
  138.             e.printStackTrace();    
  139.         }    
  140.         return filetype;    
  141.     }    
  142.         
  143.     /**  
  144.      * Created on 2010-7-1   
  145.      * <p>Discription:[getFileTypeByStream]</p>  
  146.      * @param b  
  147.      * @return fileType  
  148.      * @author:[shixing_11@sina.com]  
  149.      */    
  150.     public final static String getFileTypeByStream(byte[] b)    
  151.     {    
  152.         String filetypeHex = String.valueOf(getFileHexString(b));    
  153.         Iterator<Entry<String, String>> entryiterator = FILE_TYPE_MAP.entrySet().iterator();    
  154.         while (entryiterator.hasNext()) {    
  155.             Entry<String,String> entry =  entryiterator.next();    
  156.             String fileTypeHexValue = entry.getValue();    
  157.             if (filetypeHex.toUpperCase().startsWith(fileTypeHexValue)) {    
  158.                 return entry.getKey();    
  159.             }    
  160.         }    
  161.         return null;    
  162.     }    
  163.         
  164.     /** 
  165.      * Created on 2010-7-2  
  166.      * <p>Discription:[isImage,判断文件是否为图片]</p> 
  167.      * @param file 
  168.      * @return true 是 | false 否 
  169.      * @author:[shixing_11@sina.com] 
  170.      */  
  171.     public static final boolean isImage(File file){  
  172.         boolean flag = false;  
  173.         try  
  174.         {  
  175.             BufferedImage bufreader = ImageIO.read(file);  
  176.             int width = bufreader.getWidth();  
  177.             int height = bufreader.getHeight();  
  178.             if(width==0 || height==0){  
  179.                 flag = false;  
  180.             }else {  
  181.                 flag = true;  
  182.             }  
  183.         }  
  184.         catch (IOException e)  
  185.         {  
  186.             flag = false;  
  187.         }catch (Exception e) {  
  188.             flag = false;  
  189.         }  
  190.         return flag;  
  191.     }  
  192.       
  193.     /**  
  194.      * Created on 2010-7-1   
  195.      * <p>Discription:[getFileHexString]</p>  
  196.      * @param b  
  197.      * @return fileTypeHex  
  198.      * @author:[shixing_11@sina.com]  
  199.      */    
  200.     public final static String getFileHexString(byte[] b)    
  201.     {    
  202.         StringBuilder stringBuilder = new StringBuilder();    
  203.         if (b == null || b.length <= 0)    
  204.         {    
  205.             return null;    
  206.         }    
  207.         for (int i = 0; i < b.length; i++)    
  208.         {    
  209.             int v = b[i] & 0xFF;    
  210.             String hv = Integer.toHexString(v);    
  211.             if (hv.length() < 2)    
  212.             {    
  213.                 stringBuilder.append(0);    
  214.             }    
  215.             stringBuilder.append(hv);    
  216.         }    
  217.         return stringBuilder.toString();    
  218.     }    
  219. }  

 

 

这样,不管是传入的文件有后缀名,还是无后缀名,或者修改了后缀名,真正获取到的才是该文件的实际类型,这样避免了一些想通过修改后缀名或者 Content-type信息来攻击的因素。但是性能与安全永远是无法同时完美的,安全的同时付出了读取文件的代价。本人建议可采用后缀名与读取文件的方 式结合校验,毕竟攻击是少数,后缀名的校验能排除大多数用户,在后缀名获取不到时再通过获取文件真实类型校验,这样来适当提高性能。

转载于:https://www.cnblogs.com/jiangyang/p/4881586.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.hexiang.utils; import java.io.*; /** * FileUtil. Simple file operation class. * * @author BeanSoft * */ public class FileUtil { /** * The buffer. */ protected static byte buf[] = new byte[1024]; /** * Read content from local file. FIXME How to judge UTF-8 and GBK, the * correct code should be: FileReader fr = new FileReader(new * InputStreamReader(fileName, "ENCODING")); Might let the user select the * encoding would be a better idea. While reading UTF-8 files, the content * is bad when saved out. * * @param fileName - * local file name to read * @return * @throws Exception */ public static String readFileAsString(String fileName) throws Exception { String content = new String(readFileBinary(fileName)); return content; } /** * 读取文件并返回为给定字符集的字符串. * @param fileName * @param encoding * @return * @throws Exception */ public static String readFileAsString(String fileName, String encoding) throws Exception { String content = new String(readFileBinary(fileName), encoding); return content; } /** * 读取文件并返回为给定字符集的字符串. * @param fileName * @param encoding * @return * @throws Exception */ public static String readFileAsString(InputStream in) throws Exception { String content = new String(readFileBinary(in)); return content; } /** * Read content from local file to binary byte array. * * @param fileName - * local file name to read * @return * @throws Exception */ public static byte[] readFileBinary(String fileName) throws Exception { FileInputStream fin = new FileInputStream(fileName); return readFileBinary(fin); } /** * 从输入流读取数据为二进制字节数组. * @param streamIn * @return * @throws IOException */ public static byte[] readFileBinary(InputStream streamIn) throws IOException { BufferedInputStream in = new BufferedInputStream(streamIn); ByteArrayOutputStream out = new ByteArrayOutputStream(10240); int len; while ((len

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值