题
我正在开发一个处理文件上传的 ASP 网页。仅允许上传某些类型的文件,例如 .XLS、.XML、.CSV、.TXT、.PDF、.PPT 等。
我必须确定文件是否确实具有与扩展名显示的类型相同的类型。换句话说,如果一个 木马程序 被重命名为 无害.pdf 并上传后,应用程序必须能够发现上传的文件不是 .PDF 文件。
您将使用什么技术来分析这些上传的文件?我在哪里可以获得有关这些文件格式的最佳信息?
解决方案
一个办法是,以检查文件的某些特征或幻数。这个页面有已知文件签名的方便列表,并显得相当最新的:
其他提示
换句话说,如果 trojan.exe 被重命名为 Harmless.pdf 并上传,应用程序必须能够发现上传的文件不是 .PDF 文件。
这并不是什么问题。如果 .exe 以 .pdf 形式上传,并且您正确地将其作为应用程序/pdf 返回给下载器,那么下载器得到的将是一个损坏的 PDF。他们必须手动将其重新输入为 .exe 才会受到伤害。
真正的问题是:
某些浏览器可能会嗅探文件的内容,并认为它们比您更了解文件的类型。IE 在这方面尤其糟糕,如果它发现任何 HTML 标签潜伏在文件开头附近,它倾向于将文件呈现为 HTML。这是特别没有帮助的,因为这意味着脚本可以被注入到您的网站上,可能会损害任何应用程序级别的安全性(cookie 窃取等)。解决方法包括始终使用 Content-Disposition 将文件作为附件提供,和/或从不同的主机名提供文件,这样它就无法跨站点脚本返回到您的主站点。
PDF 文件无论如何都不安全!它们可能充满了脚本,并且存在严重的安全漏洞。利用 PDF 阅读器浏览器插件中的漏洞是目前在网络上安装木马的最常见方法之一。通常您几乎无能为力来尝试检测漏洞,因为它们可能被高度混淆。
获取“安全”的文件类型的文件头 - 可执行文件总是有自己的类型头,并且可以或许检测那些。你必须要熟悉你打算接受各种格式的,但是。
我知道你说的C#,但是这也许可以被移植。另外,它已经含有常见的文件类型的描述符多的XML文件。
这就是所谓的JMimeMagic一个Java库。它在这里: http://jmimemagic.sourceforge.net/
在** NIX *系统,我们有称为效用文件(1)。试着找到为Windows类似的东西,但文件工具,如果自己已经被移植。
也许你可以从不同的方向接近这一点。相反,识别被上传的所有文件类型(Excel的本身似乎就如同一盘散沙对我来说,因为它有多种格式的这些天),为什么不运行所有通过病毒扫描上传?各种各样的文件可能包含病毒和木马。这可能是你的服务器更多的工作,但它是最安全的解决方案。
然后就看用户能够正确识别它们的文件类型,这似乎是合理的。添加了大量的代码(即需要太测试)只是为了仔细检查您的用户似乎是一个很大的进步。如果我说这是一个.pdf2文件将会将其重命名为pdf格式?如果这是在企业环境中则是合理的预期用户对自己的文件正确的扩展名。我想跟踪谁上传了什么为好。如果它是公共然后扫描的文件类型可能是值得的,但我绝对不病毒扫描以及。
下面的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;
}