`

表示不同文件类型的魔术数字

 
阅读更多

这里所说的表示不同文件类型的魔术数字,指的是文件的最开头的几个用于唯一区别其它文件类型的字节,有了这些魔术数字,我们就可以很方便的区别不同的文件,这也使得编程变得更加容易,因为我减少了我们用于区别一个文件的文件类型所要花费的时间。

比如,一个JPEG文件,它开头的一些字节可能是类似这样的”ffd8 ffe0 0010 4a46 4946 0001 0101 0047 ……JFIF…..G“,这里”ffd8“就表示了这个文件是一个JPEG类型的文件,”ffe0“表示这是JFIF类型结构。

以下例出的是一些我们常见的文件类型,以及它用于判断这种文件的类型的几个开始字节及所对尖的ASCII数字:

图片文件

文件类型 扩展名 16进制数字
xx这里表示变量 Ascii数字
. = 不是Ascii字符
Bitmap format .bmp 42 4d BM
FITS format .fits 53 49 4d 50 4c 45 SIMPLE
GIF format .gif 47 49 46 38 GIF8
Graphics Kernel System .gks 47 4b 53 4d GKSM
IRIS rgb format .rgb 01 da ..
ITC (CMU WM) format .itc f1 00 40 bb ….
JPEG File Interchange Format .jpg ff d8 ff e0 ….
NIFF (Navy TIFF) .nif 49 49 4e 31 IIN1
PM format .pm 56 49 45 57 VIEW
PNG format .png 89 50 4e 47 .PNG
Postscript format .[e]ps 25 21 %!
Sun Rasterfile .ras 59 a6 6a 95 Y.j.
Targa format .tga xx xx xx
TIFF format (Motorola – big endian) .tif 4d 4d 00 2a MM.*
TIFF format (Intel – little endian) .tif 49 49 2a 00 II*.
X11 Bitmap format .xbm xx xx  
XCF Gimp file structure .xcf 67 69 6d 70 20 78 63 66 20 76 gimp xcf
Xfig format .fig 23 46 49 47 #FIG
XPM format .xpm 2f 2a 20 58 50 4d 20 2a 2f /* XPM */

压缩文件

文件类型 扩展名 16进制数字
xx这里表示变量 Ascii数字
. = 不是Ascii字符
Bzip .bz 42 5a BZ
Compress .Z 1f 9d ..
gzip format .gz 1f 8b ..
pkzip format .zip 50 4b 03 04 PK..

存档文件

文件类型 扩展名 16进制数字
xx这里表示变量 Ascii数字
. = 不是Ascii字符
TAR (pre-POSIX) .tar xx xx (a filename)
TAR (POSIX) .tar 75 73 74 61 72 ustar (offset by 257 bytes)

可执行文件

文件类型 扩展名 16进制数字
xx这里表示变量 Ascii数字
. = 不是Ascii字符
MS-DOS, OS/2 or MS Windows   4d 5a MZ
Unix elf   7f 45 4c 46 .ELF

其它文件

 

 

文件类型 扩展名 16进制数字
xx这里表示变量 Ascii数字
. = 不是Ascii字符
pgp public ring   99 00 ..
pgp security ring   95 01 ..
pgp security ring   95 00 ..
pgp encrypted data   a6 00 ¦.

 

-------------------------------

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

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

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

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

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

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

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

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

 

 

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

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

 

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

对应的Java代码如下:

 

[java] view plain copy
 
 
 
 
  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信息来攻击的因素。但是性能与安全永远是无法同时完美的,安全的同时付出了读取文件的代价。本人建议可采用后缀名与读取文件的方式结合校验,毕竟攻击是少数,后缀名的校验能排除大多数用户,在后缀名获取不到时再通过获取文件真实类型校验,这样来适当提高性能。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics