Java 通过魔数判断上传文件的类型

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

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

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

import org.apache.commons.lang3.StringUtils; 
  
/** 
 * 文件类型与对应的文件魔数枚举类  
 * 
 */ 
public enum FileTypeEnum {  
     
	/** JPEG  (jpg)*/ 
    JPEG("JPG", "FFD8FF"), 
  
    /** PNG */ 
    PNG("PNG", "89504E47"), 
  
    /** GIF */ 
    GIF("GIF", "47494638"), 
   
    /** TIFF (tif)  */ 
    TIFF("TIF", "49492A00"), 
  
    /** Windows bitmap (bmp) */ 
    BMP("BMP","424D"), 
     
    BMP_16("BMP","424D228C010000000000"), //16色位图(bmp)  
     
	BMP_24("BMP","424D8240090000000000"), //24位位图(bmp)   
	 
	BMP_256("BMP","424D8E1B030000000000"), //256色位图(bmp)      
  
    /** CAD  (dwg) */ 
    DWG("DWG", "41433130"), 
  
    /** Adobe photoshop  (psd)*/ 
    PSD("PSD", "38425053"), 
  
    /** Rich Text Format  (rtf)*/ 
    RTF("RTF", "7B5C727466"), 
  
    /** XML */ 
    XML("XML", "3C3F786D6C"), 
  
    /** HTML (html)*/ 
    HTML("HTML", "68746D6C3E"), 
     
    /** Email [thorough only] (eml)*/ 
    EML("EML", "44656C69766572792D646174653A"),   
	 
    /** Outlook Express (dbx) */ 
    DBX("DBX", "CFAD12FEC5FD746F "), 
  
    /** Outlook (pst)*/ 
    PST("", "2142444E"), 
  
    /** doc;xls;dot;ppt;xla;ppa;pps;pot;msi;sdw;db */ 
    OLE2("OLE2", "0xD0CF11E0A1B11AE1"), 
  
    /** Microsoft Word/Excel 注意:word 和 excel的文件头一样 */ 
    XLS("XLS", "D0CF11E0"), 
     
    /** Microsoft Word/Excel 注意:word 和 excel的文件头一样 */ 
    DOC("DOC", "D0CF11E0"), 
     
    /** Microsoft Word/Excel 2007以上版本文件 注意:word 和 excel的文件头一样 */ 
    DOCX("DOCX", "504B0304"),   
  
    /** Microsoft Word/Excel 2007以上版本文件 注意:word 和 excel的文件头一样 504B030414000600080000002100*/ 
    XLSX("XLSX", "504B0304"),  
  
    /** Microsoft Access (mdb)*/ 
    MDB("MDB", "5374616E64617264204A"), 
  
    /** Word Perfect (wpd)*/ 
    WPB("WPB", "FF575043"), 
  
    /** Postscript */ 
    EPS("EPS", "252150532D41646F6265"), 
  
    /** Postscript */ 
    PS("PS", "252150532D41646F6265"), 
  
    /** Adobe Acrobat (pdf)  */ 
    PDF("PDF", "255044462D312E"), 
     
    /** Quicken (qdf) */ 
    QDF("qdf", "AC9EBD8F"), 
     
    /** QuickBooks Backup (qdb) */ 
    QDB("qbb", "458600000600"), 
     
    /** Windows Password  (pwl)*/ 
    PWL("PWL", "E3828596"), 
  
    /** ZIP Archive */ 
    ZIP("", "504B0304"), 
  
    /** ARAR Archive */ 
    RAR("", "52617221"), 
  
    /** WAVE (wav) */ 
    WAV("WAV", "57415645"), 
  
    /** AVI */ 
    AVI("AVI", "41564920"), 
  
    /** Real Audio (ram)*/ 
    RAM("RAM", "2E7261FD"), 
  
    /** Real Media (rm) rmvb/rm相同  */ 
    RM("RM", "2E524D46"), 
     
    /** Real Media (rm) rmvb/rm相同  */ 
    RMVB("RMVB", "2E524D46000000120001"),   
     
    /** MPEG (mpg)  */ 
    MPG("MPG", "000001BA"), 
	 
    /** Quicktime  (mov)*/ 
    MOV("MOV", "6D6F6F76"), 
  
    /** Windows Media (asf) */ 
    ASF("ASF", "3026B2758E66CF11"), 
  
    /** ARJ Archive */ 
    ARJ("ARJ", "60EA"), 
  
    /** MIDI (mid) */ 
    MID("MID", "4D546864"), 
     
    /** MP4 */   
    MP4("MP4", "00000020667479706D70"),  
     
    /** MP3 */   
    MP3("MP3", "49443303000000002176"),   
     
    /** FLV */   
    FLV("FLV", "464C5601050000000900"),  
	 
    /** 1F8B0800000000000000 */ 
    GZ("GZ", "1F8B08"), 
     
    /** CSS */  
    CSS("CSS", "48544D4C207B0D0A0942"), 
     
    /**  JS */   
    JS("JS", "696B2E71623D696B2E71"),  
	  
    /**  Visio */   
    VSD("VSD", "d0cf11e0a1b11ae10000"), 
     
    /** WPS文字wps、表格et、演示dps都是一样的 */   
    WPS("WPS", "d0cf11e0a1b11ae10000"), 
     
    /** torrent */   
    TORRENT("TORRENT", "6431303A637265617465"),  
     
    /** JSP Archive */   
    JSP("JSP", "3C2540207061676520"),   
     
    /** JAVA Archive */   
    JAVA("JAVA", "7061636B61676520"),  
     
    /** CLASS Archive */   
    CLASS("CLASS", "CAFEBABE0000002E00"),  
     
    /** JAR Archive */   
    JAR("JAR", "504B03040A000000"),  
     
    /** MF Archive */   
    MF("MF", "4D616E69666573742D56"), 
 
    /** EXE Archive */   
    EXE("EXE", "4D5A9000030000000400"),  
 
    /** ELF Executable */   
    ELF("ELF", "7F454C4601010100"),  
 
    /** Lotus 123 v1 */   
    WK1("WK1", "2000604060"), 
     
    /** Lotus 123 v3 */   
    WK3("WK3", "00001A0000100400"), 
     
    /** Lotus 123 v5 */   
    WK4("WK4", "00001A0002100400"),  
 
    /** Lotus WordPro v9 */   
    LWP("LWP", "576F726450726F"),  
     
    /** Sage(sly.or.srt.or.slt;sly;srt;slt) */   
    SLY("SLY", "53520100"),  
     
    /** CHM Archive */   
   /* CHM("CHM", "49545346030000006000"),   
    INI("INI", "235468697320636F6E66"),  
    SQL("SQL", "494E5345525420494E54"),  
    BAT("BAT", "406563686F206f66660D"),   
    PROPERTIES("", "6C6F67346A2E726F6F74"),  
    MXP("", "04000000010000001300"),  */ 
     
	NOT_EXITS_ENUM("", ""); 
	 
	//文件类型对应的名称 
	private String fileTypeName; 
	 
	//文件类型对应的魔数 
	private String magicNumberCode; 
	 
	private FileTypeEnum(String fileTypeName, String magicNumberCode) { 
		this.fileTypeName = fileTypeName; 
		this.magicNumberCode = magicNumberCode; 
	}  
	  
	public String getFileTypeName() { 
		return fileTypeName; 
	}  
  
	public String getMagicNumberCode() { 
		return magicNumberCode; 
	} 
  
  
	/** 
	 * 根据文件类型获取文件类型魔数编码 
	 * 默认返回标准件 
	 * @param magicNumberCode - 文件类型魔数编码 
	 * @return 
	 */ 
	public static FileTypeEnum getByMagicNumberCode(String magicNumberCode) { 
		if (StringUtils.isNotBlank(magicNumberCode)) { 
			for (FileTypeEnum type : values()) {  
				if (magicNumberCode.toUpperCase().startsWith(type.getMagicNumberCode())) { 
					return type;  
				}  
			}  
		} 
		 
		return FileTypeEnum.NOT_EXITS_ENUM; 
	} 
	 
	/** 
	 * 根据文件类型后缀名获取枚举 
	 *  
	 * @param fileTypeName - 文件类型后缀名 
	 * @return 
	 */ 
	public static FileTypeEnum getByFileTypeName(String fileTypeName) {  
		if (StringUtils.isNotBlank(fileTypeName)) { 
			for (FileTypeEnum type : values()) { 
				if (type.getFileTypeName().equals(fileTypeName)) { 
					return type;  
				}  
			} 
		} 
		return FileTypeEnum.NOT_EXITS_ENUM; 
	} 
   
} 

或者

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.Iterator; 
 
import javax.imageio.ImageIO; 
import javax.imageio.ImageReader; 
import javax.imageio.stream.ImageInputStream; 
 
import org.apache.commons.lang3.StringUtils; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 
   
/** 
 * 通过文件魔数来判断文件类型 
 * 可以最大量避免通过后缀名来判断文件类型的漏洞 
 *  
 * @author 000125 
 * 
 */ 
public class FileTypeUtils {    
	private static final Logger LOGGER = LoggerFactory.getLogger(FileTypeUtils.class); 
	 
    /**   
     * 获取图片文件实际类型,若不是图片则返回null] 
     * @param file  
     * @return fileType   
     */   
    public final static String getImageFileType(File file) {   
        if (isImage(file)) { 
            try { 
                ImageInputStream iis = ImageIO.createImageInputStream(file); 
                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; 
    }   
   
    /**    
     * 获取文件类型,包括图片,若格式不是已配置的,则返回null 
     * @param file  
     * @return fileType   
     */   
    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;   
    }   
       
    /**  
     * 通过数据流(二进制数据)判断文件类型 
     * @param b  
     * @return fileType   
     */   
    public final static String getFileTypeByStream(byte[] b) {   
        String magicNumberCode = String.valueOf(getFileHexString(b));   
         
        if (StringUtils.isBlank(magicNumberCode)) {  
            return FileTypeEnum.getByMagicNumberCode(magicNumberCode.toUpperCase()).getFileTypeName(); 
             
        } 
        return FileTypeEnum.NOT_EXITS_ENUM.getFileTypeName();   
    }   
       
    /**  
     * isImage,判断文件是否为图片 
     * @param file 
     * @return true 是 | false 否  
     */ 
    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; 
    } 
     
    
    /** 
     * 通过文件路径判断文件类型 
     * @param path 
     * @return 
     * @throws IOException 
     */ 
    public static FileTypeEnum getFileTypeByPath(String path) { 
        // 获取文件头 
        String magicNumberCode = null; 
		try { 
			magicNumberCode = getFileHeader(path); 
		} catch (Exception e) {  
			e.printStackTrace(); 
			return FileTypeEnum.NOT_EXITS_ENUM; 
		} 
  
        if (StringUtils.isBlank(magicNumberCode)) {  
            return FileTypeEnum.getByMagicNumberCode(magicNumberCode.toUpperCase()); 
             
        } 
  
        return FileTypeEnum.NOT_EXITS_ENUM; 
    } 
 
     
    /** 
     * 通过文件路径获取文件头(即文件魔数) 
     * @param path 
     * @return 
     * @throws IOException 
     */ 
    public static String getFileHeader(String path) throws Exception { 
        byte[] b = new byte[28]; 
        InputStream inputStream = null; 
  
        try { 
            inputStream = new FileInputStream(path); 
            inputStream.read(b, 0, 28); 
        } finally { 
            if (inputStream != null) { 
                inputStream.close(); 
            } 
        } 
  
        return getFileHexString(b); 
    } 
     
    /**    
     * 把文件二进制流转换成十六进制数据 
     * @param b  
     * @return fileTypeHex   
     */   
    public final static String getFileHexString(byte[] b) {   
        StringBuilder builder = 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) {   
            	builder.append(0);   
            }   
            builder.append(hv);   
        }   
        return builder.toString();   
    }   
} 
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,可以通过以下方法来判断上传文件类型: 1. 通过文件名后缀名判断 可以通过文件名中的后缀名来判断文件类型,例如: ```java String fileName = "example.jpg"; if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) { // 文件类型为jpg或jpeg } else if (fileName.endsWith(".png")) { // 文件类型为png } else { // 其他文件类型 } ``` 但需要注意的是,文件名后缀可以被篡改,因此此方法不是十分可靠。 2. 通过文件的MIME类型判断 可以通过文件的MIME类型判断文件类型,例如: ```java import javax.activation.MimetypesFileTypeMap; MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); String mimeType = mimeTypesMap.getContentType(filePath); if (mimeType.startsWith("image/")) { // 文件类型为图片 } else if (mimeType.startsWith("video/")) { // 文件类型为视频 } else if (mimeType.startsWith("text/")) { // 文件类型为文本文件 } else { // 其他文件类型 } ``` 需要注意的是,MIME类型也可以被篡改。 3. 通过文件内容判断 可以通过读取文件的内容来判断文件类型,例如: ```java import java.io.*; public static String getFileType(String filePath) throws IOException { InputStream inputStream = new FileInputStream(filePath); byte[] bytes = new byte[4]; inputStream.read(bytes); inputStream.close(); String type = ""; String fileHead = bytesToHexString(bytes); if (fileHead == null || fileHead.length() == 0) { return null; } fileHead = fileHead.toUpperCase(); FileType[] fileTypes = FileType.values(); for (FileType fileType : fileTypes) { if (fileHead.startsWith(fileType.getValue())) { type = fileType.name(); break; } } return type; } public static String bytesToHexString(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); if (bytes == null || bytes.length <= 0) { return null; } for (int i = 0; i < bytes.length; i++) { int v = bytes[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } enum FileType { JPEG("FFD8FF"), // JPEG (jpg) PNG("89504E47"), // PNG (png) GIF("47494638"), // GIF (gif) TIFF("49492A00"), // TIFF (tif) BMP("424D"), // Windows Bitmap (bmp) DWG("41433130"), // CAD (dwg) PSD("38425053"), // Adobe Photoshop (psd) RTF("7B5C727466"), // Rich Text Format (rtf) XML("3C3F786D6C"), // XML (xml) HTML("68746D6C3E"), // HTML (html) PDF("255044462D312E"), // Adobe PDF (pdf) ZIP("504B0304"), // ZIP Archive (zip) RAR("52617221"), // RAR Archive (rar) WAV("57415645"), // Wave (wav) AVI("41564920"), // AVI (avi) MP4("00000020667479706D70"), // MPEG-4 (mp4) MPG("000001BA"), // MPEG (mpg) WMV("3026B2758E66CF11"), // Windows Media Video (wmv) MID("4D546864"); // MIDI (mid) private String value = ""; private FileType(String value) { this.value = value; } public String getValue() { return value; } } ``` 此方法可以通过读取文件的前几个字节来判断文件类型,比较可靠,但需要注意的是,不同类型文件前几个字节可能是相同的,因此有一定的误判率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值