【Java教程】不要仅通过文件后缀来判断文件类型,了解一下文件的魔数头吧

零 Java教程评论131字数 5588阅读18分37秒阅读模式

引言

最近我们公司进行了整改,引进了安全测试。安全测试的同事创建了一个危险文件,偷偷给它加上了.png后缀,并将其上传到了我们的生产服务器。这种大胆的行为,难以置信吧?

无论你是否相信,这件事确实发生了,让人不寒而栗。文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

这也揭示了一个严重的问题:我们的开发团队仅通过文件后缀来判断文件类型,导致了安全漏洞。现在,让我们来探讨一种更科学的文件识别方法。文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

一、认识魔数

魔数:也被称为签名或文件签名,是一种用于识别文件类型和格式的短序列字节。它们通常位于文件的开头,并被设计为易于识别,以便软件可以快速确定文件是否是它所支持的格式。文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

魔数是一种简单的识别机制,它由一系列字节组成,这些字节在特定的文件格式中是唯一的。当一个程序打开一个文件时,它会检查文件的开始处是否包含这些特定的字节。如果找到,程序就认为文件是该格式的,并按照相应的规则进行解析和处理。文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

二、文件类型检测的原理

文件类型检测通常基于文件的“魔数”(magic number),也称为签名或文件签名。魔数是文件开头的字节序列,用于标识文件格式。以下是文件类型检测的原理和步骤:文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

2.1 文件头的读取方法

image.png文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

打开文件:首先,需要以二进制模式打开文件,以便能够读取文件的原始字节。文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

读取字节:接着,读取文件开头的一定数量的字节(通常是前几个字节)。文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

关闭文件:读取完成后,关闭文件以释放资源。文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

2.2 如何通过文件头识别文件类型

image.png文章源自灵鲨社区-https://www.0s52.com/bcjc/javajc/15228.html

比较魔数:将读取的字节与已知的文件类型魔数进行比较。

匹配类型:如果字节序列与某个文件类型的魔数匹配,则可以确定文件类型。

处理异常:如果字节序列与任何已知魔数都不匹配,可能需要进一步的分析或返回未知文件类型。

三、Java实现文件类型检测

当需要通过文件头(魔数头)判断文件类型时,可以按照以下文字描述的流程进行实现:

打开文件
读取文件头
判断文件类型
比较文件头
输出文件类型
  1. 打开文件:使用文件输入流(FileInputStream)打开待判断类型的文件。
  2. 读取文件头:从文件中读取一定长度的字节数据作为文件头。通常,文件头的长度为固定的几个字节,一般是 2-8 个字节。
  3. 判断文件类型:根据不同文件类型的魔数头进行判断。魔数头是文件中特定位置的字节序列,用于标识文件类型。每种文件类型都有不同的魔数头。
  4. 比较文件头:将读取到的文件头与已知文件类型的魔数头进行比较。如果匹配成功,则确定文件类型。
  5. 输出文件类型:根据匹配的文件类型,输出相应的文件类型描述信息。

3.1 具体实现方法

3.1.1 定义枚举类

java

复制代码
  1. /**
  2. * 文件类型魔数枚举
  3. * 使用场景:用于判断文件类型
  4. * 使用方法:FileUtils.isFileType(new FileInputStream(file), FileTypeEnum.XLSX)
  5. *
  6. * @author bamboo panda
  7. * @version 1.0
  8. * @date 2024/5/23 19:37
  9. */
  10. public enum FileTypeEnum {
  11. /**
  12. * JPEG
  13. */
  14. JPEG("JPEG", "FFD8FF"),
  15. /**
  16. * PNG
  17. */
  18. PNG("PNG", "89504E47"),
  19. /**
  20. * GIF
  21. */
  22. GIF("GIF", "47494638"),
  23. /**
  24. * TIFF
  25. */
  26. TIFF("TIFF", "49492A00"),
  27. /**
  28. * Windows bitmap
  29. */
  30. BMP("BMP", "424D"),
  31. /**
  32. * CAD
  33. */
  34. DWG("DWG", "41433130"),
  35. /**
  36. * Adobe photoshop
  37. */
  38. PSD("PSD", "38425053"),
  39. /**
  40. * Rich Text Format
  41. */
  42. RTF("RTF", "7B5C727466"),
  43. /**
  44. * XML
  45. */
  46. XML("XML", "3C3F786D6C"),
  47. /**
  48. * HTML
  49. */
  50. HTML("HTML", "68746D6C3E"),
  51. /**
  52. * Outlook Express
  53. */
  54. DBX("DBX", "CFAD12FEC5FD746F "),
  55. /**
  56. * Outlook
  57. */
  58. PST("PST", "2142444E"),
  59. /**
  60. * doc;xls;dot;ppt;xla;ppa;pps;pot;msi;sdw;db
  61. */
  62. OLE2("OLE2", "0xD0CF11E0A1B11AE1"),
  63. /**
  64. * Microsoft Word/Excel
  65. */
  66. XLS_DOC("XLS_DOC", "D0CF11E0"),
  67. /**
  68. * Microsoft Access
  69. */
  70. MDB("MDB", "5374616E64617264204A"),
  71. /**
  72. * Word Perfect
  73. */
  74. WPB("WPB", "FF575043"),
  75. /**
  76. * Postscript
  77. */
  78. EPS_PS("EPS_PS", "252150532D41646F6265"),
  79. /**
  80. * Adobe Acrobat
  81. */
  82. PDF("PDF", "255044462D312E"),
  83. /**
  84. * Windows Password
  85. */
  86. PWL("PWL", "E3828596"),
  87. /**
  88. * ZIP Archive
  89. */
  90. ZIP("ZIP", "504B0304"),
  91. /**
  92. * ARAR Archive
  93. */
  94. RAR("RAR", "52617221"),
  95. /**
  96. * WAVE
  97. */
  98. WAV("WAV", "57415645"),
  99. /**
  100. * AVI
  101. */
  102. AVI("AVI", "41564920"),
  103. /**
  104. * Real Audio
  105. */
  106. RAM("RAM", "2E7261FD"),
  107. /**
  108. * Real Media
  109. */
  110. RM("RM", "2E524D46"),
  111. /**
  112. * Quicktime
  113. */
  114. MOV("MOV", "6D6F6F76"),
  115. /**
  116. * Windows Media
  117. */
  118. ASF("ASF", "3026B2758E66CF11"),
  119. /**
  120. * MIDI
  121. */
  122. MID("MID", "4D546864"),
  123. /**
  124. * xlsx
  125. */
  126. XLSX("XLSX", "504B0304"),
  127. /**
  128. * xls
  129. */
  130. XLS("XLS", "D0CF11E0A1B11AE1");
  131. private String key;
  132. private String value;
  133. FileTypeEnum(String key, String value) {
  134. this.key = key;
  135. this.value = value;
  136. }
  137. public String getValue() {
  138. return value;
  139. }
  140. public String getKey() {
  141. return key;
  142. }
  143. }

3.1.2 文件类型判断工具类

java

复制代码
  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. /**
  4. * 文件类型判断工具类
  5. *
  6. * @author bamboo panda
  7. * @version 1.0
  8. * @date 2024/5/23 19:38
  9. */
  10. public class FileTypeUtils {
  11. /**
  12. * 获取文件头
  13. *
  14. * @param inputStream 输入流
  15. * @return 16 进制的文件投信息
  16. * @throws IOException io异常
  17. */
  18. private static String getFileHeader(InputStream inputStream) throws IOException {
  19. byte[] b = new byte[28];
  20. inputStream.read(b, 0, 28);
  21. inputStream.close();
  22. return bytes2hex(b);
  23. }
  24. /**
  25. * 将字节数组转换成16进制字符串
  26. *
  27. * @param src 文件字节数组
  28. * @return 16进制字符串
  29. */
  30. private static String bytes2hex(byte[] src) {
  31. StringBuilder stringBuilder = new StringBuilder("");
  32. if (src == null || src.length <= 0) {
  33. return null;
  34. }
  35. for (byte b : src) {
  36. int v = b & 0xFF;
  37. String hv = Integer.toHexString(v);
  38. if (hv.length() < 2) {
  39. stringBuilder.append(0);
  40. }
  41. stringBuilder.append(hv);
  42. }
  43. return stringBuilder.toString();
  44. }
  45. /**
  46. * 判断指定输入流是否是指定文件格式
  47. *
  48. * @param inputStream 输入流
  49. * @param fileTypeEnum 文件格式枚举
  50. * @return true 是; false 否
  51. * @throws IOException io异常
  52. */
  53. public static boolean isFileType(InputStream inputStream, FileTypeEnum fileTypeEnum) throws IOException {
  54. if (null == inputStream) {
  55. return false;
  56. }
  57. String fileHeader = getFileHeader(inputStream);
  58. return fileHeader.toUpperCase().startsWith(fileTypeEnum.getValue());
  59. }
  60. }

3.1.3 测试方法

java

复制代码
  1. import java.io.File;
  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5. /**
  6. * 测试:判断文件是否是excel
  7. *
  8. * @author bamboo panda
  9. * @version 1.0
  10. * @date 2024/5/23 19:33
  11. */
  12. public class Test {
  13. public static void main(String[] args) {
  14. File file = new File("C:\Users\Admin\Desktop\temp\Import file.xlsx");
  15. try (FileInputStream fileInputStream = new FileInputStream(file)) {
  16. if (!FileTypeUtils.isFileType(fileInputStream, FileTypeEnum.XLSX) || !FileTypeUtils.isFileType(fileInputStream, FileTypeEnum.XLS)) {
  17. System.out.println(true);
  18. } else {
  19. System.out.println(false);
  20. }
  21. } catch (FileNotFoundException e) {
  22. e.printStackTrace();
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }

四、其它注意事项

在处理文件类型检测和数据保护时,安全性和隐私是两个非常重要的考虑因素。以下是一些相关的安全性问题和最佳实践:

4.1 魔数检测的安全性问题

文件类型判断
魔数检测的安全性问题
误报和漏报
恶意文件伪装
更新和维护
  1. 误报和漏报:魔数检测可能因为文件损坏或不完整而产生误报或漏报。一些恶意软件可能会模仿合法文件的魔数来逃避检测。
  2. 恶意文件伪装:攻击者可能故意在文件中嵌入合法的魔数,使得恶意文件看起来像是合法的文件类型。
  3. 更新和维护:随着新文件类型的出现和旧文件类型的淘汰,魔数列表需要定期更新,否则检测系统可能会变得不准确或过时。

4.2 数据保护和隐私的最佳实践

文件类型判断
数据保护和隐私的最佳实践
最小权限原则
数据加密
安全的数据传输
访问控制
定期审计和测试
数据最小化
  1. 最小权限原则:确保应用程序只请求执行其功能所必需的权限,不要求额外的权限。
  2. 数据加密:对敏感数据进行加密,无论是在传输中还是存储时,都应使用强加密标准。
  3. 安全的数据传输:使用安全的协议(如HTTPS)来保护数据在网络中的传输。
  4. 访问控制:实施严格的访问控制措施,确保只有授权用户才能访问敏感数据。
  5. 定期审计和测试:定期进行安全审计和渗透测试,以发现和修复潜在的安全漏洞。
  6. 数据最小化:只收集完成服务所必需的最少数据量,避免收集不必要的个人信息。

4.3 魔数的局限性和风险

魔数判断文件类型是一种常用的方法,但也存在一些局限性和风险,包括以下几点:

文件类型判断
魔数的局限性和风险
可伪造性
文件类型扩展性
文件损坏或篡改
多重文件类型
文件类型模糊性
  1. 可伪造性:魔数头是文件中的特定字节序列,攻击者可以通过修改文件的魔数头来伪装文件类型。这可能导致误判文件类型或绕过文件类型检测。
  2. 文件类型扩展性:随着新的文件类型的出现,魔数头的定义可能需要不断更新,以适应新的文件类型。如果应用程序不及时更新对新文件类型的判断逻辑,可能无法正确识别新的文件类型。
  3. 文件损坏或篡改:如果文件的魔数头部分被损坏或篡改,可能导致无法正确判断文件类型,或者将文件错误地归类为不正确的类型。
  4. 多重文件类型:某些文件可能具有多重文件类型,即使使用魔数头判断了其中一种类型,也可能存在其他类型。这可能导致文件类型的混淆和判断的不准确性。
  5. 文件类型模糊性:某些文件类型可能具有相似或相同的魔数头,这可能导致在这些类型之间进行区分时出现困难。这可能增加了误判文件类型的风险。

五、总结

好的,至此关于魔数的使用已经讲解清楚了。

魔数被广泛应用于文件类型的检测中。它是位于文件开头的一系列特定字节,用以帮助软件迅速识别文件格式。

然而,魔数检测也存在安全隐患,比如误报和恶意伪装等问题,因此需要定期更新魔数数据库。在使用魔数检测时,还需考虑到文件损坏、多重文件类型等限制,并根据实际情况采取相应的综合措施,例如数据加密、访问控制等,以确保安全性和准确性。

希望这篇文章对您有帮助。如果发现任何错误或有任何建议,请随时告知。

如果您认为这篇文章有用,请考虑点赞和收藏,您的支持将鼓励我不断改进,创作出更多有价值的内容。

感谢您的支持与理解!

零
  • 转载请务必保留本文链接:https://www.0s52.com/bcjc/javajc/15228.html
    本社区资源仅供用于学习和交流,请勿用于商业用途
    未经允许不得进行转载/复制/分享

发表评论