上传文件如果不做好安全控制的话,攻击者很有可能上传一些恶意攻击脚本,然后再执行,达到不可告人的目的。于是我们需要判断文件的类型,通常情况下我们只是判断了文件的后缀名,根据文件的后缀名的白名单和黑名单来过滤。这种方式非常不可靠,因为后缀名完全可以伪造。例如exe的伪造成jpg。
通过文件内容的判断文件的类型是目前最可靠的,因为一般根据文件的开头一些字节的特征就能判断这个文件的类型,而不需要读取整个文件,所以可行性也比较高。下面给出一个例子。
1、首先定义文件类型的识别数据FileType.java
1 public enum FileType { 2 3 /** 4 * JEPG. 5 */ 6 JPEG("FFD8FF"), 7 8 9 /** 10 * PNG. 11 */ 12 PNG("89504E47"), 13 14 15 /** 16 * GIF. 17 */ 18 GIF("47494638"), 19 20 21 /** 22 * TIFF. 23 */ 24 TIFF("49492A00"), 25 /** 26 * RTF. 27 */ 28 RTF("7B5C727466"), 29 /** 30 * DOC 31 */ 32 DOC("D0CF11E0"), 33 /** 34 * XLS 35 */ 36 XLS("D0CF11E0"), 37 /** 38 * ACCESS 39 */ 40 MDB("5374616E64617264204A"), 41 42 43 /** 44 * Windows Bitmap. 45 */ 46 BMP("424D"), 47 48 49 /** 50 * CAD. 51 */ 52 DWG("41433130"), 53 54 55 /** 56 * Adobe Photoshop. 57 */ 58 PSD("38425053"), 59 60 61 /** 62 * XML. 63 */ 64 XML("3C3F786D6C"), 65 66 67 /** 68 * HTML. 69 */ 70 HTML("68746D6C3E"), 71 72 73 /** 74 * Adobe Acrobat. 75 */ 76 PDF("255044462D312E"), 77 78 79 /** 80 * ZIP Archive. 81 */ 82 ZIP("504B0304"), 83 84 85 /** 86 * RAR Archive. 87 */ 88 RAR("52617221"), 89 90 91 /** 92 * Wave. 93 */ 94 WAV("57415645"), 95 96 97 /** 98 * AVI. 99 */ 100 AVI("41564920"); 101 102 103 private String value = ""; 104 105 /** 106 * Constructor. 107 * 108 * @param type 109 */ 110 private FileType(String value) { 111 this.value = value; 112 } 113 114 public String getValue() { 115 return value; 116 } 117 118 public void setValue(String value) { 119 this.value = value; 120 } 121 }
然后读取文件的前面几个字节转化成十六进制
1 public enum FileType { 2 3 /** 4 * JEPG. 5 */ 6 JPEG("FFD8FF"), 7 8 9 /** 10 * PNG. 11 */ 12 PNG("89504E47"), 13 14 15 /** 16 * GIF. 17 */ 18 GIF("47494638"), 19 20 21 /** 22 * TIFF. 23 */ 24 TIFF("49492A00"), 25 /** 26 * RTF. 27 */ 28 RTF("7B5C727466"), 29 /** 30 * DOC 31 */ 32 DOC("D0CF11E0"), 33 /** 34 * XLS 35 */ 36 XLS("D0CF11E0"), 37 /** 38 * ACCESS 39 */ 40 MDB("5374616E64617264204A"), 41 42 43 /** 44 * Windows Bitmap. 45 */ 46 BMP("424D"), 47 48 49 /** 50 * CAD. 51 */ 52 DWG("41433130"), 53 54 55 /** 56 * Adobe Photoshop. 57 */ 58 PSD("38425053"), 59 60 61 /** 62 * XML. 63 */ 64 XML("3C3F786D6C"), 65 66 67 /** 68 * HTML. 69 */ 70 HTML("68746D6C3E"), 71 72 73 /** 74 * Adobe Acrobat. 75 */ 76 PDF("255044462D312E"), 77 78 79 /** 80 * ZIP Archive. 81 */ 82 ZIP("504B0304"), 83 84 85 /** 86 * RAR Archive. 87 */ 88 RAR("52617221"), 89 90 91 /** 92 * Wave. 93 */ 94 WAV("57415645"), 95 96 97 /** 98 * AVI. 99 */ 100 AVI("41564920"); 101 102 103 private String value = ""; 104 105 /** 106 * Constructor. 107 * 108 * @param type 109 */ 110 private FileType(String value) { 111 this.value = value; 112 } 113 114 public String getValue() { 115 return value; 116 } 117 118 public void setValue(String value) { 119 this.value = value; 120 } 121 }
2、通过枚举类,管理不同类型文件的文件头(具体每个文件的文件头内容,可以通过UltraEdit工具打开,查看其16进制内容)
1 package org.hyena.ats.util; 2 3 /** 4 * 文件类型 5 * @author zhihao.du 6 * 7 */ 8 public enum FileType { 9 /** 10 * JEPG. 11 */ 12 JPEG("FFD8FF"), 13 14 /** 15 * PNG. 16 */ 17 PNG("89504E47"), 18 19 /** 20 * GIF. 21 */ 22 GIF("47494638"), 23 24 /** 25 * TIFF. 26 */ 27 TIFF("49492A00"), 28 29 /** 30 * Windows Bitmap. 31 */ 32 BMP("424D"), 33 34 /** 35 * CAD. 36 */ 37 DWG("41433130"), 38 39 /** 40 * Adobe Photoshop. 41 */ 42 PSD("38425053"), 43 44 /** 45 * Rich Text Format. 46 */ 47 RTF("7B5C727466"), 48 49 /** 50 * XML. 51 */ 52 XML("3C3F786D6C"), 53 54 /** 55 * HTML. 56 */ 57 HTML("68746D6C3E"), 58 /** 59 * CSS. 60 */ 61 CSS("48544D4C207B0D0A0942"), 62 /** 63 * JS. 64 */ 65 JS("696B2E71623D696B2E71"), 66 /** 67 * Email [thorough only]. 68 */ 69 EML("44656C69766572792D646174653A"), 70 71 /** 72 * Outlook Express. 73 */ 74 DBX("CFAD12FEC5FD746F"), 75 76 /** 77 * Outlook (pst). 78 */ 79 PST("2142444E"), 80 81 /** 82 * MS Word/Excel. 83 * XLS_DOC:ppt,doc,xls 84 * XLSX_DOCX:xlsx 85 */ 86 XLS_DOC("D0CF11E0"), XLSX_DOCX("504B030414000600080000002100"), 87 /** 88 * Visio 89 */ 90 VSD("d0cf11e0a1b11ae10000"), 91 /** 92 * MS Access. 93 */ 94 MDB("5374616E64617264204A"), 95 /** 96 * WPS文字wps、表格et、演示dps都是一样的 97 */ 98 WPS("d0cf11e0a1b11ae10000"), 99 /** 100 * torrent 101 */ 102 TORRENT("6431303A637265617465"), 103 /** 104 * WordPerfect. 105 */ 106 WPD("FF575043"), 107 108 /** 109 * Postscript. 110 */ 111 EPS("252150532D41646F6265"), 112 113 /** 114 * Adobe Acrobat. 115 */ 116 PDF("255044462D312E"), 117 118 /** 119 * Quicken. 120 */ 121 QDF("AC9EBD8F"), 122 123 /** 124 * Windows Password. 125 */ 126 PWL("E3828596"), 127 128 /** 129 * ZIP Archive. 130 */ 131 ZIP("504B0304"), 132 133 /** 134 * RAR Archive. 135 */ 136 RAR("52617221"), 137 /** 138 * JSP Archive. 139 */ 140 JSP("3C2540207061676520"), 141 /** 142 * JAVA Archive. 143 */ 144 JAVA("7061636B61676520"), 145 /** 146 * CLASS Archive. 147 */ 148 CLASS("CAFEBABE0000002E00"), 149 /** 150 * JAR Archive. 151 */ 152 JAR("504B03040A000000"), 153 /** 154 * MF Archive. 155 */ 156 MF("4D616E69666573742D56"), 157 /** 158 *EXE Archive. 159 */ 160 EXE("4D5A9000030000000400"), 161 /** 162 *CHM Archive. 163 */ 164 CHM("49545346030000006000"), 165 /** 166 * Wave. 167 */ 168 WAV("57415645"), 169 170 /** 171 * AVI. 172 */ 173 AVI("41564920"), 174 175 /** 176 * Real Audio. 177 */ 178 RAM("2E7261FD"), 179 180 /** 181 * Real Media. 182 */ 183 RM("2E524D46"), 184 185 /** 186 * MPEG (mpg). 187 */ 188 MPG("000001BA"), 189 190 /** 191 * Quicktime. 192 */ 193 MOV("6D6F6F76"), 194 195 /** 196 * Windows Media. 197 */ 198 ASF("3026B2758E66CF11"), 199 200 /** 201 * MIDI. 202 */ 203 MID("4D546864"), 204 /** 205 * MP4. 206 */ 207 MP4("00000020667479706d70"), 208 /** 209 * MP3. 210 */ 211 MP3("49443303000000002176"), 212 /** 213 * FLV. 214 */ 215 FLV("464C5601050000000900"), 216 /** 217 * TXT:txt,docx 218 */ 219 TXT("0000000000000000000000000000"); 220 221 private String value = ""; 222 223 /** 224 * Constructor. 225 * 226 * @param type 227 */ 228 private FileType(String value) { 229 this.value = value; 230 } 231 232 public String getValue() { 233 return value; 234 } 235 236 public void setValue(String value) { 237 this.value = value; 238 } 239 240 }
操作枚举类:
1 package org.hyena.ats.util; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 6 /** 7 * 文件类型操作类 8 * @author zhihao.du 9 * 10 */ 11 public final class FileTypeJudge { 12 13 /** 14 * Constructor 15 */ 16 private FileTypeJudge() { 17 } 18 19 /** 20 * 将文件头转换成16进制字符串 21 * @param 原生byte 22 * @return 16进制字符串 23 */ 24 private static String bytesToHexString(byte[] src) { 25 StringBuilder stringBuilder = new StringBuilder(); 26 if (src == null || src.length <= 0) { 27 return null; 28 } 29 for (int i = 0; i < src.length; i++) { 30 int v = src[i] & 0xFF; 31 String hv = Integer.toHexString(v); 32 if (hv.length() < 2) { 33 stringBuilder.append(0); 34 } 35 stringBuilder.append(hv); 36 } 37 return stringBuilder.toString(); 38 } 39 40 /** 41 * 得到文件头 42 * @param filePath 文件路径 43 * @return 文件头 44 * @throws IOException 45 */ 46 private static String getFileContent(InputStream is) throws IOException { 47 byte[] b = new byte[28]; 48 InputStream inputStream = null; 49 try { 50 is.read(b, 0, 28); 51 } catch (IOException e) { 52 e.printStackTrace(); 53 throw e; 54 } finally { 55 if (inputStream != null) { 56 try { 57 inputStream.close(); 58 } catch (IOException e) { 59 e.printStackTrace(); 60 throw e; 61 } 62 } 63 } 64 return bytesToHexString(b); 65 } 66 67 /** 68 * 获取文件类型类 69 * @param filePath 文件路径 70 * @return 文件类型 71 */ 72 public static FileType getType(InputStream is) throws IOException { 73 String fileHead = getFileContent(is); 74 if (fileHead == null || fileHead.length() == 0) { 75 return null; 76 } 77 fileHead = fileHead.toUpperCase(); 78 FileType[] fileTypes = FileType.values(); 79 for (FileType type : fileTypes) { 80 if (fileHead.startsWith(type.getValue())) { 81 return type; 82 } 83 } 84 return null; 85 } 86 87 /** 88 * 获取文件类型 89 * @param is 90 * @return 91 * @throws Exception 92 */ 93 public static String getFileType(InputStream is) throws Exception{ 94 FileType fileType = getType(is); 95 if(fileType!=null){ 96 return fileType.getValue(); 97 } 98 return null; 99 } 100 }
判断接口
1 /** 2 * 判断文件格式 3 * @param file 4 * @return 5 */ 6 public static boolean checkFileFormat(MultipartFile file) { 7 if (file == null) { 8 return false; 9 } 10 try { 11 String type = FileTypeJudge.getFileType(file.getInputStream()); 12 if(FileType.TXT.getValue().equals(type)){//TXT,DOCX 13 return true; 14 } 15 if(FileType.XLS_DOC.getValue().equals(type)){//PPT,DOC,XLS 16 return true; 17 } 18 if(FileType.XLSX_DOCX.getValue().equals(type)){//XLSX 19 return true; 20 } 21 if(FileType.PDF.getValue().equals(type)){//PDF 22 return true; 23 } 24 if(FileType.PNG.getValue().equals(type)){//PNG 25 return true; 26 } 27 if(FileType.JPEG.getValue().equals(type)){//JPG 28 return true; 29 } 30 }catch (Exception e) { 31 e.printStackTrace(); 32 return false; 33 } 34 return false; 35 }