赞
踩
在文章应用安全系列之二十四:文件上传_jimmyleeee的博客-CSDN博客描述了上传文件时需要判断文件的几个方面,其中一个就是文件的类型,当然,如果要根据文件的后缀判断,也能满足大部分需求,如果确实需要判断文件的类型,或者某些时刻文件没有扩展名时,就会遇到麻烦。
文件类型检测分为两种:
Java提供了集中方法来判断一个文件的类型,其中比较常用的方法是:Files.probeContentType,但是,它有一个缺点就是,当文件没有扩展名时,返回的结果是null,下面是一个使用这个方法的代码:
- public class TestApp {
-
- public static String identifyFileType(final String fileName) throws IOException
- {
- final File file = new File(fileName);
- return Files.probeContentType(file.toPath());
- }
-
- public static void main(String[] args) {
-
- String fileNames[] = {"F:\\filetype\\1.pdf",
- "F:\\filetype\\2.zip",
- "F:\\filetype\\3.docx",
- "F:\\filetype\\4.xls",
- "F:\\filetype\\5.jpg",
- "F:\\filetype\\6.csv",
- "F:\\filetype\\7.txt",
- "F:\\filetype\\8.PNG",
- "F:\\filetype\\9.mp4",
- "F:\\filetype\\10.xml",
- "F:\\filetype\\11.xml",
- "F:\\filetype\\noextpng",
- "F:\\filetype\\csvnoext",
- "F:\\filetype\\mp4noext",
- "F:\\filetype\\pngwithtxt.txt",
- };
-
- // Test Files.probeContentType
- System.out.println("Test Files.probeContentType Begin");
- try {
- for (String fileName : fileNames) {
-
- System.out.println(identifyFileType(fileName));
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- System.out.println("Test Files.probeContentType End");
-
- }
- }
打印结果如下:
- Test Files.probeContentType Begin
- application/pdf
- application/x-zip-compressed
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
- application/vnd.ms-excel
- image/jpeg
- application/vnd.ms-excel
- text/plain
- image/png
- video/mp4
- text/xml
- application/json
- null
- null
- null
- text/plain
- Test Files.probeContentType End
可以看到,Files.probeContentType是根据扩展名来判断文件类型的,没有扩展名时,返回的是null,当把一个图片改成txt结尾,返回的类型和txt的类型一样。说明如果要根据Files.probeContentType准确判断一个文件的类型是不准确的。
Java还提供了其他几个方法获取文件的类型:
类与方法名 | 说明 |
MimetypesFileTypeMap::getContentType | 只有jpg和txt返回正常,其他全部返回application/octet-stream |
URLConnection::getContentType | 和Files.probeContentType类似。不同的是,它识别的XML文件的类型是application/xml,而Files.probeContentType识别的XML是text/xml. |
URLConnection.guessContentTypeFromName | 测试了除了pdf、zip、jpg、txt、png、xml返回的值正常,其它全部返回null |
综上所述,目前Java提供的几种方法都不能很好地准确地判断文件的类型,不过,幸运的是Apache提供了Tika,下面是使用默认的Tika的功能的代码:
- public static String getFileTypeByDefaultTika(final String fileName)
- {
- Tika defaultTika = new Tika();
- String fileType;
- try
- {
- final File file = new File(fileName);
- fileType = defaultTika.detect(file);
- }
- catch (IOException ioEx)
- {
- fileType = "Unknown";
- }
- return fileType;
- }
使用本文开头的示例代码打印信息如下:
- Test DefaultTikaForFile Begin
- application/pdf
- application/zip
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
- application/vnd.ms-excel
- image/jpeg
- text/csv
- text/plain
- image/png
- video/mp4
- application/xml
- application/json
- image/png
- text/plain
- video/quicktime
- image/png
- Test DefaultTikaForFile End
可以看到,所有的文件类型都识别出来了,没有扩展名的文件也是别出来,最为重要的是,我把图片改成txt后缀,它也是别出来这是一个png。
Tika还提供了其他实现,下面表格是将集中的实现结果的比较:
Tika实现方式 | 示例代码 | 说明 |
默认 | Tika defaultTika = new Tika(); | 根据上述代码打印结果,完全符合预期,效果最好; |
MIME | Tika mimeTika = new Tika(new MimeTypes()); | 除了txt文件正常输出text/plain,其他全部是application/octet-stream; |
TypeDetector | Tika typeTika = new Tika(new TypeDetector()); | 全部输出为application/octet-stream; |
综上所述,如果需要比较准确地判断一个文件到底是什么类型,不管扩展名是什么,需要使用Tika的默认实现方式;如果只需要根据扩展名判断文件类型就够了,你可以使用Files.probeContentType和URLConnection::getContentType。
关于PHP也有几种获取文件类型的方法,总结如下表:
类与方法名 | 说明 |
pathinfo ( $file , PATHINFO_EXTENSION) | 仅仅是根据文件的扩展名返回文件类型 |
$_FILES[‘uploadfile’][‘type’] | 仅仅是根据文件的扩展名返回文件类型 |
finfo_file( resource $finfo ,string $file_name = null ,int $options = FILEINFO_NONE,resource $context = null ): string | 本函数用来获取一个文件的信息。该方法即便是原文件被改过后缀,已然可以读到原文件类型。 |
总之,在处理文件上传时需要谨记需要做好输入验证,不管是文件名、大小、类型,还是内容等,只有做好了防范,才能预防由文件上传带来的攻击。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。