我正在处理一个处理文件上传的 ASP 网页。仅允许上传特定类型的文件,例如 .XLS、.XML、.CSV、.TXT、.PDF、.PPT 等。
我必须确定一个文件是否真的与扩展名显示的类型相同。换句话说,如果将trojan.exe重命名为harmless.pdf并上传,应用程序必须能够发现上传的文件不是.PDF 文件。
您将使用什么技术来分析这些上传的文件?我在哪里可以获得有关这些文件格式的最佳信息?
我正在处理一个处理文件上传的 ASP 网页。仅允许上传特定类型的文件,例如 .XLS、.XML、.CSV、.TXT、.PDF、.PPT 等。
我必须确定一个文件是否真的与扩展名显示的类型相同。换句话说,如果将trojan.exe重命名为harmless.pdf并上传,应用程序必须能够发现上传的文件不是.PDF 文件。
您将使用什么技术来分析这些上传的文件?我在哪里可以获得有关这些文件格式的最佳信息?
一种方法是检查文件中的某些签名或幻数。此页面有一个方便的已知文件签名列表,并且似乎是最新的:
换句话说,如果将 trojan.exe 重命名为harmless.pdf 并上传,应用程序必须能够发现上传的文件不是.PDF 文件。
这不是一个真正的问题。如果 .exe 以 .pdf 格式上传,并且您正确地将其作为 application/pdf 提供给下载器,则下载器将得到的所有内容都将是损坏的 PDF。他们必须手动将其重新键入 .exe 才能受到伤害。
真正的问题是:
某些浏览器可能会嗅探文件的内容,并认为它们比您更了解文件的类型。IE 在这方面特别糟糕,如果它发现任何 HTML 标签潜伏在文件的开头附近,它倾向于将文件呈现为 HTML。这特别无益,因为这意味着脚本可以注入您的站点,可能会损害任何应用程序级别的安全性(cookie 窃取等)。解决方法包括始终使用 Content-Disposition 将文件作为附件提供,和/或从不同的主机名提供文件,因此它不能跨站点脚本回到您的主站点。
PDF 文件无论如何都不安全!它们可能充满了脚本,并且存在严重的安全漏洞。利用 PDF 阅读器浏览器插件中的漏洞是目前在网络上安装木马的最常见手段之一。而且您通常几乎无能为力来尝试检测这些漏洞,因为它们可能会被高度混淆。
获取“安全”文件类型的文件头 - 可执行文件总是有自己的头文件类型,您可能可以检测到这些文件头。但是,您必须熟悉您打算接受的每种格式。
我知道你说的是 C#,但这可能会被移植。此外,它还有一个 XML 文件,其中已经包含许多常见文件类型的描述符。
这是一个名为 JMimeMagic 的 Java 库。它在这里:http: //jmimemagic.sourceforge.net/
也许你可以从不同的方向来解决这个问题。与其识别所有上传的文件类型(仅 Excel 对我来说似乎一团糟,因为它现在有多种格式),为什么不通过病毒扫描程序运行所有上传?各种各样的文件都可能包含病毒和木马。它可能对您的服务器更有效,但它是最安全的解决方案。
然后由用户正确识别他们的文件类型,这似乎是合理的。添加大量代码(也需要测试)只是为了仔细检查您的用户似乎是一大步。如果我说它是 .pdf2 文件,您会将其重命名为 .pdf 吗?如果这是在公司环境中,那么期望用户对其文件具有正确的扩展名是合理的。我也会跟踪谁上传了什么。如果它是公开的,那么扫描文件类型可能是值得的,但我绝对也会进行病毒扫描。
在 **NIX* 系统上,我们有一个名为file(1)的实用程序。尝试为 Windows 找到类似的东西,但如果 self 已被移植,则文件实用程序。
以下 C++ 代码可以帮助您:
//-1 : File Does not Exist or no access
//0 : not an office document
//1 : (General) MS office 2007
//2 : (General) MS office older than 2007
//3 : MS office 2003 PowerPoint presentation
//4 : MS office 2003 Excel spreadsheet
//5 : MS office applications or others
int IsOffice2007OrOlder(wchar_t * fileName)
{
int iRet = 0;
byte msgFormatChk2007[8] = {0x50, 0x4B, 0x03, 0x04, 0x14, 0x00, 0x06, 0x00}; //offset 0 for office 2007 documents
byte possibleMSOldOffice[8] = {0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1}; //offset 0 for possible office 2003 documents
byte msgFormatChkXLSPPT[4] = {0xFD, 0xFF, 0xFF, 0xFF}; // offset 512: xls, ppt: FD FF FF FF
byte msgFormatChkOnlyPPT[4] = {0x00, 0x6E, 0x1E, 0xF0}; // offset 512: another ppt offset PPT
byte msgFormatChkOnlyDOC[4] = {0xEC, 0xA5, 0xC1, 0x00}; //offset 512: EC A5 C1 00
byte msgFormatChkOnlyXLS[8] = {0x09, 0x08, 0x10, 0x00, 0x00, 0x06, 0x05, 0x00}; //offset 512: XLS
int iMsgChk = 0;
HANDLE fileHandle = CreateFile(fileName, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL );
if(INVALID_HANDLE_VALUE == fileHandle)
{
return -1;
}
byte buff[20];
DWORD bytesRead;
iMsgChk = 1;
if(0 == ReadFile(fileHandle, buff, 8, &bytesRead, NULL ))
{
return -1;
}
if(buff[0] == msgFormatChk2007[0])
{
while(buff[iMsgChk] == msgFormatChk2007[iMsgChk] && iMsgChk < 9)
iMsgChk++;
if(iMsgChk >= 8) {
iRet = 1; //office 2007 file format
}
}
else if(buff[0] == possibleMSOldOffice[0])
{
while(buff[iMsgChk] == possibleMSOldOffice[iMsgChk] && iMsgChk < 9)
iMsgChk++;
if(iMsgChk >= 8)
{
//old office file format, check 512 offset further in order to filter out real office format
iMsgChk = 1;
SetFilePointer(fileHandle, 512, NULL, FILE_BEGIN);
if(ReadFile(fileHandle, buff, 8, &bytesRead, NULL ) == 0) { return 0; }
if(buff[0] == msgFormatChkXLSPPT[0])
{
while(buff[iMsgChk] == msgFormatChkXLSPPT[iMsgChk] && iMsgChk < 5)
iMsgChk++;
if(iMsgChk == 4)
iRet = 2;
}
else if(buff[iMsgChk] == msgFormatChkOnlyDOC[iMsgChk])
{
while(buff[iMsgChk] == msgFormatChkOnlyDOC[iMsgChk] && iMsgChk < 5)
iMsgChk++;
if(iMsgChk == 4)
iRet = 2;
}
else if(buff[0] == msgFormatChkOnlyPPT[0])
{
while(buff[iMsgChk] == msgFormatChkOnlyPPT[iMsgChk] && iMsgChk < 5)
iMsgChk++;
if(iMsgChk == 4)
iRet = 3;
}
else if(buff[0] == msgFormatChkOnlyXLS[0])
{
while(buff[iMsgChk] == msgFormatChkOnlyXLS[iMsgChk] && iMsgChk < 9)
iMsgChk++;
if(iMsgChk == 9)
iRet = 4;
}
if(0 == iRet){
iRet = 5;
}
}
}
CloseHandle(fileHandle);
return iRet;
}