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

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

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

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

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

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

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

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

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

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

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

对应的Java代码如下:

  1. package apistudy;
  2. import java.awt.image.BufferedImage;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileNotFoundException;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.util.HashMap;
  9. import java.util.Iterator;
  10. import java.util.Map;
  11. import java.util.Map.Entry;
  12. import javax.imageio.ImageIO;
  13. import javax.imageio.ImageReader;
  14. import javax.imageio.stream.ImageInputStream;
  15. public class FileTypeTest
  16. {
  17. public final static Map<String, String> FILE_TYPE_MAP = new HashMap<String, String>();
  18. private FileTypeTest(){}
  19. static{
  20. getAllFileType();  //初始化文件类型信息
  21. }
  22. /**
  23. * Created on 2010-7-1
  24. * <p>Discription:[getAllFileType,常见文件头信息]</p>
  25. * @author:[shixing_11@sina.com]
  26. */
  27. private static void getAllFileType()
  28. {
  29. FILE_TYPE_MAP.put("jpg", "FFD8FF"); //JPEG (jpg)
  30. FILE_TYPE_MAP.put("png", "89504E47");  //PNG (png)
  31. FILE_TYPE_MAP.put("gif", "47494638");  //GIF (gif)
  32. FILE_TYPE_MAP.put("tif", "49492A00");  //TIFF (tif)
  33. FILE_TYPE_MAP.put("bmp", "424D"); //Windows Bitmap (bmp)
  34. FILE_TYPE_MAP.put("dwg", "41433130"); //CAD (dwg)
  35. FILE_TYPE_MAP.put("html", "68746D6C3E");  //HTML (html)
  36. FILE_TYPE_MAP.put("rtf", "7B5C727466");  //Rich Text Format (rtf)
  37. FILE_TYPE_MAP.put("xml", "3C3F786D6C");
  38. FILE_TYPE_MAP.put("zip", "504B0304");
  39. FILE_TYPE_MAP.put("rar", "52617221");
  40. FILE_TYPE_MAP.put("psd", "38425053");  //Photoshop (psd)
  41. FILE_TYPE_MAP.put("eml", "44656C69766572792D646174653A");  //Email [thorough only] (eml)
  42. FILE_TYPE_MAP.put("dbx", "CFAD12FEC5FD746F");  //Outlook Express (dbx)
  43. FILE_TYPE_MAP.put("pst", "2142444E");  //Outlook (pst)
  44. FILE_TYPE_MAP.put("xls", "D0CF11E0");  //MS Word
  45. FILE_TYPE_MAP.put("doc", "D0CF11E0");  //MS Excel 注意:word 和 excel的文件头一样
  46. FILE_TYPE_MAP.put("mdb", "5374616E64617264204A");  //MS Access (mdb)
  47. FILE_TYPE_MAP.put("wpd", "FF575043"); //WordPerfect (wpd)
  48. FILE_TYPE_MAP.put("eps", "252150532D41646F6265");
  49. FILE_TYPE_MAP.put("ps", "252150532D41646F6265");
  50. FILE_TYPE_MAP.put("pdf", "255044462D312E");  //Adobe Acrobat (pdf)
  51. FILE_TYPE_MAP.put("qdf", "AC9EBD8F");  //Quicken (qdf)
  52. FILE_TYPE_MAP.put("pwl", "E3828596");  //Windows Password (pwl)
  53. FILE_TYPE_MAP.put("wav", "57415645");  //Wave (wav)
  54. FILE_TYPE_MAP.put("avi", "41564920");
  55. FILE_TYPE_MAP.put("ram", "2E7261FD");  //Real Audio (ram)
  56. FILE_TYPE_MAP.put("rm", "2E524D46");  //Real Media (rm)
  57. FILE_TYPE_MAP.put("mpg", "000001BA");  //
  58. FILE_TYPE_MAP.put("mov", "6D6F6F76");  //Quicktime (mov)
  59. FILE_TYPE_MAP.put("asf", "3026B2758E66CF11"); //Windows Media (asf)
  60. FILE_TYPE_MAP.put("mid", "4D546864");  //MIDI (mid)
  61. }
  62. public static void main(String[] args) throws Exception
  63. {
  64. File f = new File("c://aaa.gif");
  65. if (f.exists())
  66. {
  67. String filetype1 = getImageFileType(f);
  68. System.out.println(filetype1);
  69. String filetype2 = getFileByFile(f);
  70. System.out.println(filetype2);
  71. }
  72. }
  73. /**
  74. * Created on 2010-7-1
  75. * <p>Discription:[getImageFileType,获取图片文件实际类型,若不是图片则返回null]</p>
  76. * @param File
  77. * @return fileType
  78. * @author:[shixing_11@sina.com]
  79. */
  80. public final static String getImageFileType(File f)
  81. {
  82. if (isImage(f))
  83. {
  84. try
  85. {
  86. ImageInputStream iis = ImageIO.createImageInputStream(f);
  87. Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
  88. if (!iter.hasNext())
  89. {
  90. return null;
  91. }
  92. ImageReader reader = iter.next();
  93. iis.close();
  94. return reader.getFormatName();
  95. }
  96. catch (IOException e)
  97. {
  98. return null;
  99. }
  100. catch (Exception e)
  101. {
  102. return null;
  103. }
  104. }
  105. return null;
  106. }
  107. /**
  108. * Created on 2010-7-1
  109. * <p>Discription:[getFileByFile,获取文件类型,包括图片,若格式不是已配置的,则返回null]</p>
  110. * @param file
  111. * @return fileType
  112. * @author:[shixing_11@sina.com]
  113. */
  114. public final static String getFileByFile(File file)
  115. {
  116. String filetype = null;
  117. byte[] b = new byte[50];
  118. try
  119. {
  120. InputStream is = new FileInputStream(file);
  121. is.read(b);
  122. filetype = getFileTypeByStream(b);
  123. is.close();
  124. }
  125. catch (FileNotFoundException e)
  126. {
  127. e.printStackTrace();
  128. }
  129. catch (IOException e)
  130. {
  131. e.printStackTrace();
  132. }
  133. return filetype;
  134. }
  135. /**
  136. * Created on 2010-7-1
  137. * <p>Discription:[getFileTypeByStream]</p>
  138. * @param b
  139. * @return fileType
  140. * @author:[shixing_11@sina.com]
  141. */
  142. public final static String getFileTypeByStream(byte[] b)
  143. {
  144. String filetypeHex = String.valueOf(getFileHexString(b));
  145. Iterator<Entry<String, String>> entryiterator = FILE_TYPE_MAP.entrySet().iterator();
  146. while (entryiterator.hasNext()) {
  147. Entry<String,String> entry =  entryiterator.next();
  148. String fileTypeHexValue = entry.getValue();
  149. if (filetypeHex.toUpperCase().startsWith(fileTypeHexValue)) {
  150. return entry.getKey();
  151. }
  152. }
  153. return null;
  154. }
  155. /**
  156. * Created on 2010-7-2
  157. * <p>Discription:[isImage,判断文件是否为图片]</p>
  158. * @param file
  159. * @return true 是 | false 否
  160. * @author:[shixing_11@sina.com]
  161. */
  162. public static final boolean isImage(File file){
  163. boolean flag = false;
  164. try
  165. {
  166. BufferedImage bufreader = ImageIO.read(file);
  167. int width = bufreader.getWidth();
  168. int height = bufreader.getHeight();
  169. if(width==0 || height==0){
  170. flag = false;
  171. }else {
  172. flag = true;
  173. }
  174. }
  175. catch (IOException e)
  176. {
  177. flag = false;
  178. }catch (Exception e) {
  179. flag = false;
  180. }
  181. return flag;
  182. }
  183. /**
  184. * Created on 2010-7-1
  185. * <p>Discription:[getFileHexString]</p>
  186. * @param b
  187. * @return fileTypeHex
  188. * @author:[shixing_11@sina.com]
  189. */
  190. public final static String getFileHexString(byte[] b)
  191. {
  192. StringBuilder stringBuilder = new StringBuilder();
  193. if (b == null || b.length <= 0)
  194. {
  195. return null;
  196. }
  197. for (int i = 0; i < b.length; i++)
  198. {
  199. int v = b[i] & 0xFF;
  200. String hv = Integer.toHexString(v);
  201. if (hv.length() < 2)
  202. {
  203. stringBuilder.append(0);
  204. }
  205. stringBuilder.append(hv);
  206. }
  207. return stringBuilder.toString();
  208. }
  209. }

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

原文出处:

[1] shixing_11, Java判断文件类型 ,http://blog.csdn.net/shixing_11/article/details/5708145

最新文章

  1. 面试题:return和finally执行
  2. php实现转换html格式为文本格式的方法
  3. PHP cookie
  4. table常用功能总结
  5. ArcGIS Js/Flex等前端API(Query(StatisticDefinition)时)针对SDE的SHAPE.AREA/SHAPE.LEN知道查询无效,而对GDB的SHAPE_Area/SHAPE_Length有效探索。
  6. poj1006-Biorhythms(中国剩余定理)
  7. 将html页改成jsp的两种方式
  8. Android 根据屏幕分辨率自动调整字体大小
  9. Linux将端口设置进防火墙的白名单
  10. WebSocket群聊与单聊
  11. pip无法正常使用卸载并重新安装
  12. java深copy (伪深copy)【原】
  13. oracle修改字符集方法
  14. Splash go() 方法
  15. 20145304 网络对抗技术 逆向与Bof基础
  16. Log4j在Java工程中使用方法
  17. jQuery基础(样式篇,DOM对象,选择器,属性样式)
  18. 浅谈js设计模式之单例模式
  19. Effective C++ —— 继承与面向对象设计(六)
  20. Educational Codeforces Round 27

热门文章

  1. Codeforces Global Round 3
  2. Redux-saga-整理
  3. 异步编程的上下文与操作符--await/async generator/yield
  4. Lisp : (quote) code is data (eval) data as code
  5. 微服务架构 ------ Ubuntu下Docker的安装
  6. Java代码中可以优化性能的小细节
  7. xss学习
  8. IOS—— strong weak retain assign 学习
  9. ASP.NET Core 2.2 和之前版本区别: 可以在IIS上进行ASP.NET核心进程托管 (翻译)
  10. 5.如何保证 redis 的高并发和高可用?redis 的主从复制原理能介绍一下么?redis 的哨兵原理能介绍一下么?