我有一个用例,我们允许用户上传文件。现在在后端java(从http请求中提取文件并进行检查的控制器)中,我想检测用户是否上传了任何可执行文件。如果他上传,我必须放弃那个文件。我在谷歌上搜索过,但找不到一个好的解决方案。有些人建议验证扩展名(.exe)。但我不确定它能过滤多少exe文件。我想完全阻止可执行文件上传。
如果你们中的任何人遇到过这种情况或对此有解决方案,请告诉我。我将感谢你。
如果你能给我指一下做这项工作的任何JAVA实现或JAVA API或算法,我会更高兴。
我怀疑,除了您已经提到的扩展检查方法之外,还没有办法捕获所有可能的情况。可执行文件最终是机器指令的序列,这使得它们在很大程度上与任何其他数据都无法区分。
尽管如此,在某些类型的可执行文件中还是可以找到一些东西。例如:
- Windows使用可移植可执行文件格式,该格式应始终以幻数
4d5a
(ASCII字符MZ
)开头 - Linux使用的ELF格式可执行文件从
7f454c46
开始 - Java类文件总是以
cafebabe
开头(这是十六进制,而不是ASCII!) - 据我所见,Mac OSX使用的Mach-O文件有一个神奇的数字
feedface
(又是十六进制)
我建议您创建一个FileInputStream
或类似的文件,读取文件的前几个字节,检查这些幻数。它不会检测到任何包含可执行代码的文件,但它应该阻止这些标准可执行格式的文件被允许,我认为这正是您所希望的。
例如:
public static boolean isExecutable(File file) {
byte[] firstBytes = new byte[4];
try {
FileInputStream input = new FileInputStream(file);
input.read(firstBytes);
// Check for Windows executable
if (firstBytes[0] == 0x4d && firstBytes[1] == 0x5a) {
return true;
}
return false;
}
catch (Exception e) {
e.printStackTrace();
}
}
还要注意,可能会出现误报,即拒绝不可执行的文件。我不知道你打算上传什么类型的文件,所以你应该考虑这种情况发生的可能性。
要完成devrobf的响应:每个可执行文件(我的意思是文件包含机器指令)都可以通过文件元数据中包含的幻数来识别。幻数由其大小(以字节为单位)和偏移量(根据文件类型的不同,偏移量可能不同)来识别。你可以在这里找到一个包含这些信息的数据库。
例如EXE文件:
Extension : EXE
Signature : 4D 5A
Description : Windows|DOS executable file
MZ (ASCII)
Sizet : 2 Bytes
Offset: 0 Bytes
正如您肯定会理解的那样,只检查扩展名并不能确定什么样的可执行文件。就像拟议中的克拉提卢斯。为什么?因为以下示例:
touch notAnExecutableWithExtensionExe.exe
这个命令只是创建扩展名为"exe"的文件,但它只是文件数据。
在Java中实现对任何类型的文件进行正确检查:
public enum ExecutableSignatures{
WINDOWS_EXE("Windows|DOS executable file", (byte) 0x00, (byte) 0x02,
new byte[]{(byte)0x4d, (byte)0x5a}),
JAVA_BYTECODE("Java Bytecode", (byte) 0x00, (byte) 0x04,
new byte[]{(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe});
/* Here more enumeration */
private String description;
private byte offset;
private byte size;
private byte[] magicNumber;
private ExecutableSignatures(String description, byte offset, byte size, byte [] magicNumber){
this.description = description;
this.offset = offset;
this.size = size;
this.magicNumber = magicNumber;
}
public String getDescription(){
return this.description;
}
public byte getOffset(){
return this.offset;
}
public byte getSize(){
return this.size;
}
public byte[] getMagicNumbers(){
return this.magicNumber;
}
在您可以使用apache库创建一个方法来进行此检查之后,请参阅此处请参阅@Filters-MagicNumberFilter。此构造函数可以接受2个参数;magicNumbers(字节数组)和偏移量(字节)。
/**
* Perform a check of what kind of executable is by checking the signature
* of file.
* If it's an executable that is enumerate then the attributes
* magicNumber and executableDescription are updated with their corresponding
* values.
* @return true if is an executable supported by the program otherwise false
*/
public boolean isExecutableFile(){
MagicNumberFileFilter mnff = null;
for(ExecutableSignatures es : EnumSet.allOf(ExecutableSignatures.class)){
mnff = new
MagicNumberFileFilter(es.getMagicNumbers(), es.getOffset());
if(mnff.accept(this.file)){
this.magicNumber = es.getMagicNumbers();
this.executableDescription = es.getDescription();
return true;
}
}
return false;
}
Windows可执行文件总是以MZ
幻数开头。也许你可以查一下这个。
据我所见,最常用的方法是验证扩展。例如,我注意到,如果将可执行文件重命名为zip或其他扩展名,邮件客户端通常会接受发送可执行文件
我相信这似乎足够了,因为安全问题是如果用户意外地运行了可执行文件。通过将文件重命名为未知/不同的扩展名,用户不会意外地这样做,因此在某种程度上"减轻"了危险
否则,我不知道有多可行/可移植/可靠
看看这里:
有没有一个好的方法来确定一个文件是否可以在Java 中执行
看起来这个命令可能会有所帮助:java.io.File.canExecute()
请注意,windows可执行文件不仅仅是.exe
文件,因此检查扩展名是不够的
如果你想要一些高级且难以愚弄的东西,你可以使用像File for Windows这样的第三方工具——这是一个从Linux移植而来的流行命令行工具。
例如,如果要检查某个文件program.exe
C:file -b "program.exe"
结果将类似
PE32 executable for MS Windows <GUI> Intel
您可以使用Runtime.getRuntime().exec()
从Java程序运行此工具
请参阅此问题,了解如何运行命令行程序并在Java 中获得输出
您还可以检查Apache Tika以从其内容中获取文件类型