1. 程式人生 > >表示不同檔案型別的魔術數字【整理】

表示不同檔案型別的魔術數字【整理】

這裡所說的表示不同檔案型別的魔術數字,指定是檔案的最開頭的幾個用於唯一區別其它檔案型別的位元組,有了這些魔術數字,我們就可以很方便的區別不同的檔案,這也使得程式設計變得更加容易,因為我減少了我們用於區別一個檔案的檔案型別所要花費的時間。

比如,一個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程式碼如下:

package apistudy;    
    
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<String, String> FILE_TYPE_MAP = new HashMap<String, String>();    
        
    private FileTypeTest(){}    
    static{    
        getAllFileType();  //初始化檔案型別資訊    
    }    
        
    /**  
     * Created on 2010-7-1   
     * <p>Discription:[getAllFileType,常見檔案頭資訊]</p>  
     * @author:[[email protected]]  
     */    
    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);    
        }    
    }    
    
    /**  
     * Created on 2010-7-1   
     * <p>Discription:[getImageFileType,獲取圖片檔案實際型別,若不是圖片則返回null]</p>  
     * @param File  
     * @return fileType  
     * @author:[[email protected]]  
     */    
    public final static String getImageFileType(File f)    
    {    
        if (isImage(f))  
        {  
            try  
            {  
                ImageInputStream iis = ImageIO.createImageInputStream(f);  
                Iterator<ImageReader> 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;  
    }    
    
    /**  
     * Created on 2010-7-1   
     * <p>Discription:[getFileByFile,獲取檔案型別,包括圖片,若格式不是已配置的,則返回null]</p>  
     * @param file  
     * @return fileType  
     * @author:[[email protected]]  
     */    
    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;    
    }    
        
    /**  
     * Created on 2010-7-1   
     * <p>Discription:[getFileTypeByStream]</p>  
     * @param b  
     * @return fileType  
     * @author:[[email protected]]  
     */    
    public final static String getFileTypeByStream(byte[] b)    
    {    
        String filetypeHex = String.valueOf(getFileHexString(b));    
        Iterator<Entry<String, String>> entryiterator = FILE_TYPE_MAP.entrySet().iterator();    
        while (entryiterator.hasNext()) {    
            Entry<String,String> entry =  entryiterator.next();    
            String fileTypeHexValue = entry.getValue();    
            if (filetypeHex.toUpperCase().startsWith(fileTypeHexValue)) {    
                return entry.getKey();    
            }    
        }    
        return null;    
    }    
        
    /** 
     * Created on 2010-7-2  
     * <p>Discription:[isImage,判斷檔案是否為圖片]</p> 
     * @param file 
     * @return true 是 | false 否 
     * @author:[[email protected]] 
     */  
    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;  
    }  
      
    /**  
     * Created on 2010-7-1   
     * <p>Discription:[getFileHexString]</p>  
     * @param b  
     * @return fileTypeHex  
     * @author:[[email protected]]  
     */    
    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資訊來攻擊的因素。但是效能與安全永遠是無法同時完美的,安全的同時付出了讀取檔案的代價。本人建議可採用字尾名與讀取檔案的方式結合校驗,畢竟攻擊是少數,字尾名的校驗能排除大多數使用者,在後綴名獲取不到時再通過獲取檔案真實型別校驗,這樣來適當提高效能。

以上摘錄自:http://blog.csdn.net/fenglibing/article/details/7733496

http://blog.csdn.net/shixing_11/article/details/5708145