通常,在WEB系統中,上傳文件時都需要做文件的類型校驗,大致有如下幾種方法:
1. 通過后綴名,如exe,jpg,bmp,rar,zip等等。
2. 通過讀取文件,獲取文件的Content-type來判斷。
3. 通過讀取文件流,根據文件流中特定的一些字節標識來區分不同類型的文件。
4. 若是圖片,則通過縮放來判斷,可以縮放的為圖片,不可以的則不是。
然而,在安全性較高的業務場景中,1,2兩種方法的校驗會被輕易繞過。
1. 偽造后綴名,如圖片的,非常容易修改。
2. 偽造文件的Content-type,這個稍微復雜點,
3.較安全,但是要讀取文件,並有16進制轉換等操作,性能稍差,但能滿足一定條件下對安全的要求,所以建議使用。
但是文件頭的信息也可以偽造,截圖如下,對於圖片可以采用圖片縮放或者獲取圖片寬高的方法避免偽造頭信息漏洞。
- package com.nfschina.utils.file;
- import java.io.File;
- import java.io.FileInputStream;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Map.Entry;
- import com.nfschina.utils.BaseException;
- /***********************************************************************
- *
- * Description: 主要用於判斷文件的類型
- *
- ***********************************************************************/
- public class FileTools {
- public final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>();
- /*-----------------------------目前可以識別的類型----------------------------*/
- private static void getAllFileType()
- {
- FILE_TYPE_MAP.put("jpg", "FFD8FF"); //JPEG
- FILE_TYPE_MAP.put("png", "89504E47"); //PNG
- FILE_TYPE_MAP.put("gif", "47494638"); //GIF
- FILE_TYPE_MAP.put("tif", "49492A00"); //TIFF
- FILE_TYPE_MAP.put("bmp", "424D"); //Windows Bitmap
- FILE_TYPE_MAP.put("dwg", "41433130"); //CAD
- FILE_TYPE_MAP.put("html", "68746D6C3E"); //HTML
- FILE_TYPE_MAP.put("rtf", "7B5C727466"); //Rich Text Format
- 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
- FILE_TYPE_MAP.put("eml", "44656C69766572792D646174653A"); //Email [thorough only]
- FILE_TYPE_MAP.put("dbx", "CFAD12FEC5FD746F"); //Outlook Express
- FILE_TYPE_MAP.put("pst", "2142444E"); //Outlook
- FILE_TYPE_MAP.put("office", "D0CF11E0"); //office類型,包括doc、xls和ppt
- FILE_TYPE_MAP.put("mdb", "000100005374616E64617264204A"); //MS Access
- FILE_TYPE_MAP.put("wpd", "FF575043"); //WordPerfect
- FILE_TYPE_MAP.put("eps", "252150532D41646F6265");
- FILE_TYPE_MAP.put("ps", "252150532D41646F6265");
- FILE_TYPE_MAP.put("pdf", "255044462D312E"); //Adobe Acrobat
- FILE_TYPE_MAP.put("qdf", "AC9EBD8F"); //Quicken
- FILE_TYPE_MAP.put("pwl", "E3828596"); //Windows Password
- FILE_TYPE_MAP.put("wav", "57415645"); //Wave
- FILE_TYPE_MAP.put("avi", "41564920");
- FILE_TYPE_MAP.put("ram", "2E7261FD"); //Real Audio
- FILE_TYPE_MAP.put("rm", "2E524D46"); //Real Media
- FILE_TYPE_MAP.put("mpg", "000001BA"); //
- FILE_TYPE_MAP.put("mov", "6D6F6F76"); //Quicktime
- FILE_TYPE_MAP.put("asf", "3026B2758E66CF11"); //Windows Media
- FILE_TYPE_MAP.put("mid", "4D546864"); //MIDI (mid)
- }
- /**
- * 通過讀取文件頭部獲得文件類型
- * @param file
- * @return 文件類型
- * @throws BaseException
- */
- public static String getFileType(File file) throws BaseException{
- getAllFileType();
- String fileExtendName = null;
- FileInputStream is;
- try {
- is = new FileInputStream(file);
- byte[] b = new byte[16];
- is.read(b,0, b.length);
- String filetypeHex = String.valueOf(bytesToHexString(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)) {
- fileExtendName = entry.getKey();
- if(fileExtendName.equals("office")) {
- fileExtendName = getOfficeFileType(is);
- }
- is.close();
- break;
- }
- }
- // 如果不是上述類型,則判斷擴展名
- if(fileExtendName == null)
- {
- String fileName = file.getName();
- // 如果無擴展名,則直接返回空串
- if(-1 == fileName.indexOf("."))
- {
- return "";
- }
- // 如果有擴展名,則返回擴展名
- return fileName.substring(fileName.indexOf(".") + 1);
- }
- is.close();
- return fileExtendName;
- } catch (Exception exception) {
- throw new BaseException(exception.getMessage(), exception);
- }
- }
- /**
- * 判斷office文件的具體類型
- * @param fileInputStream
- * @return office文件具體類型
- * @throws BaseException
- */
- private static String getOfficeFileType(FileInputStream fileInputStream) throws BaseException{
- String officeFileType = "doc";
- byte[] b = new byte[512];
- try {
- fileInputStream.read(b, 0, b.length);
- String filetypeHex = String.valueOf(bytesToHexString(b));
- String flagString = filetypeHex.substring(992, filetypeHex.length());
- if(flagString.toLowerCase().startsWith("eca5c")){
- officeFileType = "doc";
- } else if(flagString.toLowerCase().startsWith("fdffffff09")){
- officeFileType = "xls";
- } else if(flagString.toLowerCase().startsWith("09081000000")){
- officeFileType = "xls";
- } else {
- officeFileType = "ppt";
- }
- return officeFileType;
- } catch (Exception exception) {
- throw new BaseException(exception.getMessage(), exception);
- }
- }
- /**
- * 獲得文件頭部字符串
- * @param src
- * @return
- */
- private static String bytesToHexString(byte[] src){
- StringBuilder stringBuilder = new StringBuilder();
- if (src == null || src.length <= 0) {
- return null;
- }
- for (int i = 0; i < src.length; i++) {
- int v = src[i] & 0xFF;
- String hv = Integer.toHexString(v);
- if (hv.length() < 2) {
- stringBuilder.append(0);
- }
- stringBuilder.append(hv);
- }
- return stringBuilder.toString();
- }
- public static void main(String[] args)
- {
- File file = new File("E:/新聞公告.pdm");
- FileInputStream is;
- try{
- is = new FileInputStream(file);
- byte[] b = new byte[16];
- is.read(b,0, b.length);
- // String filetypeHex = String.valueOf(bytesToHexString(b));
- String fileName = file.getName();
- System.out.println(fileName.substring(fileName.indexOf(".") + 1));
- }catch(Exception e)
- {
- e.printStackTrace();
- }
- }