在介绍文件上传漏洞时,介绍了针对输入的文件需要判断:文件大小、文件扩展名、文件名称还有文件类型。其中关键的就是检测文件的内容的类型,如果避免一些不符合实际需求的内容上传就是其中最关键的,探测准确的内容类型,可以预防上传恶意代码或者恶意文件。
针对文件类型的获取,java提供了几种方法获取,本篇文章就概括起来介绍一下:
第一种方法,通过Files.probeContentType直接获取文件的类型,示例代码如下:
Path path = Files.createTempFile("myprofile", ".txt");
String mimeType = Files.probeContentType(path);
Files.probeContentType会使用系统安装的FileTypeDetector探测文件类型,如果没有安装的FileTypeDetector ,就是用系统默认的,相信信息在 Java如何判断一个上传文件的内容类型 有具体介绍。
缺点是:如果文件不再文件系统上存在或者文件没有扩展名,都不能成功获取文件的类型。
第二种方法,URLConnection
URLConnection提供了几个API来获取文件的MIME类型,主要由以下几个API:
1)API getContentType
示例代码如下:
File myProcileFile = new File("myprofile.png");
URLConnection connection = myProcileFile.toURL().openConnection();
String mimeType = connection.getContentType();
这个APi获取文件类型的速度很慢。
2) guessContentTypeFromName
示例代码如下:
File myProcileFile = new File("myprofile.png");
String mimeType = URLConnection.guessContentTypeFromName(myProcileFile.getName());
这个方法是通过FileNameMap通过扩展名来获取文件的MIME类型。
3) getFileNameMap
示例代码如下:
File myProcileFile = new File("myprofile.png");
FileNameMap fileNameMap = URLConnection.getFileNameMap();
String mimeType = fileNameMap.getContentTypeFor(myProcileFile.getName());
这种方法是根据一个MIME类型的表来获取文件类型的,内嵌的MIME类型是有限的,默认使用的是JRE_HOME/lib路径下的content-types.properties。当然,也可以通过自定义的表格来进行扩展。
System.setProperty("content.types.user.table","my-table-file");
第三种方法, MimeTypesFileTypeMap
示例代码如下:
File file = new File("myprofile.png");
MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();
String mimeType = mimetypesFileTypeMap.getContentType(file.getName());
MimeTypesFileTypeMap使用的是文件的扩展名获取文件类型。获取类型的参数既可以传递文件名也可以是File实例,传递文件实例,内部实现都一样,都是通过文件名来判断文件类型。获取文件类型的是通过获取一个文件mime.type的内容实现的,查找mime.type的顺序如下:
第四种方法, jMimeMagic
jMimeMagic是一个可以用于获取mime类型的库,不过license比较严格,License的类型是GNU Library or Lesser General Public License version 2.0 (LGPLv2)。不过,根据https://mvnrepository.com/artifact/net.sf.jmimemagic/jmimemagic,目前依然停留在2017年的0.1.5版本,之后就再也没有更新,对于这种没有人维护的库,使用需谨慎。
实例代码如下:
File myProcileFile = new File("myprofile.png");
Magic magic = new Magic();
MagicMatch magicMatch = magic.getMagicMatch(myProcileFile, false);
String mimeType =magicMatch.getMimeType();
也有方法可以接受stream参数,如果是stream,就可以不需要再本地文件系统有具体文件。
第五种方法,Apache Tika
Apache Tika是一个检测或者获取文件Meta数据的工具集,也提供了丰富的接口用于探测文件的MIME类型,他实现的基本原理也是根据stream里的Magic标示来实现的。如果不适用stream的接口,则是根据文件的扩展名来实现。具体说明和实例代码可以参考:Java如何判断一个上传文件的内容类型_java判断上传文件类型_jimmyleeee的博客-CSDN博客
总之,如果要获取文件内容类型的比较准确的话,可以使用Apache Tika和 jMimeMagic这种以识别文件内容里的Magic标识来实现的库。其他的API实际返回的MIME类型可能就不准确了,除非扩展自己的实现。