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

零 Java教程评论86字数 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

复制代码
/**
 * 文件类型魔数枚举
 * 使用场景:用于判断文件类型
 * 使用方法:FileUtils.isFileType(new FileInputStream(file), FileTypeEnum.XLSX)
 *
 * @author bamboo panda
 * @version 1.0
 * @date 2024/5/23 19:37
 */
public enum FileTypeEnum {
    /**
     * JPEG
     */
    JPEG("JPEG", "FFD8FF"),

    /**
     * PNG
     */
    PNG("PNG", "89504E47"),

    /**
     * GIF
     */
    GIF("GIF", "47494638"),

    /**
     * TIFF
     */
    TIFF("TIFF", "49492A00"),

    /**
     * Windows bitmap
     */
    BMP("BMP", "424D"),

    /**
     * CAD
     */
    DWG("DWG", "41433130"),

    /**
     * Adobe photoshop
     */
    PSD("PSD", "38425053"),

    /**
     * Rich Text Format
     */
    RTF("RTF", "7B5C727466"),

    /**
     * XML
     */
    XML("XML", "3C3F786D6C"),

    /**
     * HTML
     */
    HTML("HTML", "68746D6C3E"),

    /**
     * Outlook Express
     */
    DBX("DBX", "CFAD12FEC5FD746F "),

    /**
     * Outlook
     */
    PST("PST", "2142444E"),

    /**
     * doc;xls;dot;ppt;xla;ppa;pps;pot;msi;sdw;db
     */
    OLE2("OLE2", "0xD0CF11E0A1B11AE1"),

    /**
     * Microsoft Word/Excel
     */
    XLS_DOC("XLS_DOC", "D0CF11E0"),

    /**
     * Microsoft Access
     */
    MDB("MDB", "5374616E64617264204A"),

    /**
     * Word Perfect
     */
    WPB("WPB", "FF575043"),

    /**
     * Postscript
     */
    EPS_PS("EPS_PS", "252150532D41646F6265"),

    /**
     * Adobe Acrobat
     */
    PDF("PDF", "255044462D312E"),

    /**
     * Windows Password
     */
    PWL("PWL", "E3828596"),

    /**
     * ZIP Archive
     */
    ZIP("ZIP", "504B0304"),

    /**
     * ARAR Archive
     */
    RAR("RAR", "52617221"),

    /**
     * WAVE
     */
    WAV("WAV", "57415645"),

    /**
     * AVI
     */
    AVI("AVI", "41564920"),

    /**
     * Real Audio
     */
    RAM("RAM", "2E7261FD"),

    /**
     * Real Media
     */
    RM("RM", "2E524D46"),

    /**
     * Quicktime
     */
    MOV("MOV", "6D6F6F76"),

    /**
     * Windows Media
     */
    ASF("ASF", "3026B2758E66CF11"),

    /**
     * MIDI
     */
    MID("MID", "4D546864"),
    /**
     * xlsx
     */
    XLSX("XLSX", "504B0304"),
    /**
     * xls
     */
    XLS("XLS", "D0CF11E0A1B11AE1");

    private String key;
    private String value;

    FileTypeEnum(String key, String value) {
        this.key = key;
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public String getKey() {
        return key;
    }
}

3.1.2 文件类型判断工具类

java

复制代码
import java.io.IOException;
import java.io.InputStream;

/**
 * 文件类型判断工具类
 *
 * @author bamboo panda
 * @version 1.0
 * @date 2024/5/23 19:38
 */
public class FileTypeUtils {

    /**
     * 获取文件头
     *
     * @param inputStream 输入流
     * @return 16 进制的文件投信息
     * @throws IOException io异常
     */
    private static String getFileHeader(InputStream inputStream) throws IOException {
        byte[] b = new byte[28];
        inputStream.read(b, 0, 28);
        inputStream.close();
        return bytes2hex(b);
    }

    /**
     * 将字节数组转换成16进制字符串
     *
     * @param src 文件字节数组
     * @return 16进制字符串
     */
    private static String bytes2hex(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (byte b : src) {
            int v = b & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 判断指定输入流是否是指定文件格式
     *
     * @param inputStream  输入流
     * @param fileTypeEnum 文件格式枚举
     * @return true 是; false 否
     * @throws IOException io异常
     */
    public static boolean isFileType(InputStream inputStream, FileTypeEnum fileTypeEnum) throws IOException {
        if (null == inputStream) {
            return false;
        }
        String fileHeader = getFileHeader(inputStream);
        return fileHeader.toUpperCase().startsWith(fileTypeEnum.getValue());
    }

}

3.1.3 测试方法

java

复制代码
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * 测试:判断文件是否是excel
 *
 * @author bamboo panda
 * @version 1.0
 * @date 2024/5/23 19:33
 */
public class Test {

    public static void main(String[] args) {
        File file = new File("C:\Users\Admin\Desktop\temp\Import file.xlsx");
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            if (!FileTypeUtils.isFileType(fileInputStream, FileTypeEnum.XLSX) || !FileTypeUtils.isFileType(fileInputStream, FileTypeEnum.XLS)) {
                System.out.println(true);
            } else {
                System.out.println(false);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

四、其它注意事项

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

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
    本社区资源仅供用于学习和交流,请勿用于商业用途
    未经允许不得进行转载/复制/分享

发表评论