■ 河南 许红军
编者按: 对于很多网站来说,都提供了文件上传功能,允许用户上传图片等文件,实现和其他人的互动。但是,这一功能也成了黑客等不法之徒觊觎的目标。黑客往往利用目标网站存在的各种漏洞来上传各种非法文件,进而对网站进行渗透攻击。在很多实际的案例中,都可以看到正是因为存在上传漏洞,黑客才可以很轻松的发起攻击,因此,对文件上传进行严格的管控,对于提高网站的安全性是极为重要的。
和文件上传相关的安全威胁包括以下类型,例如针对文件上传的DoS攻击,就是利用文件上传功能,上传大量的文件,来极大的占用服务器
的存储和带宽资源。黑客还会利用特殊方法,将非法文件(例如脚本等)传输上去,之后在服务器上运行恶意脚本。当然,黑客还可以将恶意文件上传到服务器上,之后通过各种方法诱导用户下载,当用户下载后,黑客就可以利用跨站脚本攻击等方法,来对网站进行渗透。
对于第一种攻击类型来说,黑客会使用Web应用的上传功能,连续发送体积巨大的文件,这就会导致目标网站负荷过载,从而形成拒绝服务。
应对这种攻击方式,可以采用各种方式加以化解。例如对于PHP类型的网站来说,可以在“php.ini”文件中设置允许上传的最大容量,执行“vim /etc/php.ini” 命令,在其中的“file_uploads=”的值设置为“Off”,就会取消上传功能。
在“u p l o a d_m a x_filesize =”栏中设置单个文件的最大上传容量(例如“100K”), 在“max_file_uploads =”栏中设置单次请求最大文件上传数量,在“post_max_size =”栏中设置POST请求正文的最大限制,一般来说上传文件使用的都是POST方法来实现的。在“memory_limit =”栏 中设置脚本所能申请到的最大内存值。
当 然,利 用Apache的“httpd.conf”文件,也可以限制请求正文的最大字节数,通过前期的检查可以将不合法的请求拒绝掉,继而可以有效提高防御DoS的能力。
执行“vim /etc/httpd/conf/https.conf” 命 令,在 对 应 的“” 栏中取消“LimitRequestBody 102400”行前面的“#”号,将该行配置激活,其中的“xxx”为具体的Web路径。执行“systemcrl restart httpd”命令,重启Apache。
这样,在执行上传操作时,文件大量就不能超过预设值(默认为100KB)。
对于非法上传恶意脚本来说,其正是利用了服务器端配置上的某些漏洞来实现的。
因为对于有些文件上传的处理方式来说,会将用户上传的文件保存到Web服务器的公开目录中。如果应用中允许上传的文件扩展名为php、asp、aspx、jsp等脚本文件,黑客就可以将上传的文件作为脚本在服务器上运行。
这样,黑客就可以执行浏览,修改和删除Web服务器上的文件,甚至以此为跳板来攻击其他的服务器。
应对的方法是禁止将上传的文件保存到公开的目录中,防止用户直接查看到具体的访问链接,而应该将其保存到比较隐藏的目录中,当上传之后,禁止显示具体的链接地址,即必须通过特定的脚本才可以访问上传的文件,总之禁止在服务器上直接执行上传的文件。
还可以将文件的扩展名设置为不可执行的脚本文件,即禁止上传脚本类型的文件,当然这只是辅助性质的,因为限制文件扩展名是很可能存在疏漏的。
最关键的是不能将上传文件保存在公开目录中,因为在公开目录中是可以执行脚本的。
例如在设计网站程序时,需要针对上传的文件进行严格的检测。例如在PHP网站中,可以定义一个名为专门的函数对此进行检测,例如输入诸如“define('UPLOADPATH', '/var/upload')”之类的语句,定义上传路径,注意其不能为公开目录。
输入:
“function check_upfile($scfile) {”
“$i n f o=p a t h i n f o($scfile);”
“?$ext= strtolower($info['extension'])”
“if ($exe != 'png'&&$exe != 'jpg' &&$exe !='bmp' ) {”
“die ('只能上传指定格式的图片文件!');”
“}”
“$count=0;”
“do {”
“$file=sprintf('%s/%08x.%s',UPLOADPATH,mt_rand(),$ext);”
“$fp-@fopen === FALSE&& ++$count < 10);”
“if ($fp === FALSE) {”
“die('无法生成文件!');”
“}”
“fclose($fp);”
“return $file;”,“}”等语句,创建所需的函数来检测上传的文件。这里的“$scfile”代表上传的文件,首先分析和提取上传文件的路径信息,并获取其规格化的扩展名,如果扩展名不符合要求(这里只能为图片文件),则弹出警告信息。
之后通过一个循环,在指定的上传路径下创建名称为随机的文件,如果其可以顺利打开的话,则说明创建成功,之后返回该文件名,该文件名中包含了绝对路径信息。
之后执行:
“$scfile= check_upfile($realfile)”
“if(! move_uploaded_file($tmpfile,$scfile)){”
“die('文件不能上传!');”
“}”等语句,将上传的而原始文件写入到上述文件中,“realfile”表示原始文件名。
这样,就将其存储到了非公开的路径中。当用户需要查看上传的文件时,不能让其直接获取到实际的路径,而必须通过特定的脚本来读取,并将读取的信息返回给用户。
例 如 输 入“$imgurl='g i v e t o u s e r f i l e.php?file=',basename($scfile);”语句,调用专用的脚本来读取上传的文件,并获取对应的文件路径信息。
其 中“givetouserfile.php”脚本就是用来获取上传文件信息的。
其内容为:
“
“define('UPLOADPATH','/var/upload');”
“$mimes=array('jpg'=>'image/jpeg','png'=>'image/png','bmp'=>'image/bmp');”
“$f i l e=$_G E T['file'];”
“$i n f o=p a t h i n f o($file);”
“$ext=strtolower($info['extension']);”
“$c o n t e n t_type=$mimes[$ext];”
“if (! $content_type){”
“die('上传格式错误!');}”
“header('Content-Type: '. $content_type);”
“readfile(UPLOADPATH. '/ .basename($file));”
“?>”等行,其作用是将传递的文件名进行分析,来提取其扩展名,并找到扩展名对应的键值,如果找到的话,说明文件类型合规,然后就读取其内容,并将其写入到HTTP数据包中返回给用户。
并 通 过“
但是,用户无法了解其实际的路径。这样就从限制文件格式,存储到非公开目录,不显示具体链接等方面保证了上传文件的安全。即使用户上传了非法文件,也是无法访问和执行的。
当然,这里只是使用了简单的代码进行示例性的说明。
对于黑客来说,为了实现对客户机进行攻击,往往会采取上传包含有恶意代码的图片或者PDF文档等方式,来诱使用户进行下载,当用户下载了看似正常的文件后,在进行浏览时就会激活其中隐藏的恶意代码,从而可对Web服务器造成安全威胁。
对于这种狡猾的“迂回”攻击策略,需要引起管理员的重视。
例如如果网站存在会话管理或者认证机制的话,当黑客实现了攻击后,网站的SessionID和Cookie信息就会泄露出去,黑客就可借此进行认证,从而伪装用户身份进行非法操作。
当因为在用户下载某些文件时,浏览器有时并不能正确识别文件的类型,例如黑客会上传数据中包含HTML标签的图片文件,导致浏览器误认为其是HTML文件,当在浏览器中进行查看时就会触发其中的恶意JavaScript代码。
为了防止这种攻击行为,需要正确设置文件的Content-Type类型信息,这一点非常重要。对上传的文件进行检测,保证文件扩展名和内容保持一致。
例如对于JPG文件来说,必须确保其扩展名和文件头信息匹配。为了避免在浏览器中直接执行文件,而仅仅是下载操作的话,需要在响应头中指定“Content-Disposition:attachment”参数信息。
运行“regedit.exe”程序,在注册表编辑器中打开“HKEY_CLASSES_ROOTMIMEDatabaseContent Type”分支,在其中显示IE可以处理的所有Content-Type类型,例如对于扩展名为“.pdf”的文件来说,其Content-Type的 类 型 为“application/pdf”。
当IE接收到非图片类型的文件时,就会在注册表的上述位置查找与其对应的Content-Type信息,如果服务器端发送的Content-Type信息在本地找不到的话,IE就会根据具体URL地址中的扩展名进行判断。而如果攻击者在构建URL地址时恶意添加了某些特定的信息,最终IE就会根据这些信息认为是一个HTML文件而加以执行。
例如黑客会在特制的PDF文件中嵌入JavaScript代码,之后利用网站设计上的漏洞(例如程序员使用了过时的Content-Type类型等),并以此构建恶意的访问链接,导致用户访问该链接而进行跨站攻击。
对于此类安全威胁,可以在上传和下载两个环节进行管控。
对于上传来说,需要检测文件扩展名是否在允许的范围内,对于图片文件需要确认其文件头信息。
例如对于PHP来说,可以定义一个函数来进行执行检测。
例如输入:
“ function checktype($imgfile,$scfile){”
“$i n f o=p a t h i n f o($scfile);”
“$ext=strtolower($inf o['extension']);”
“if ($ext != 'png' &&$ext != 'jpg' && $ext !='gid'){”
“die('上传的文件是非法的!')”
“$imginfo=getimagesiz e($imgfile);”
“$type=$imginfo[2];”
“if ($ext=='gif' &&$type==IMAGETYPE_GIF)return;”
“if ($ext=='jpg' &&$type==IMAGETYPE_JPEG)return;”
“if ($ext=='png' &&$type==IMAGETYPE_PNG)return;”
“die('图片格式存在问题!')}”等语句。利用这些程序,可以对指定的文件进行检测。如果其格式和类型存在问题,就显示警告信息。
之后使用:
“c h e c k_i m a g e_t y o p e($t m p f i l e,$realfile);”
“$s c f i l e=c h e c k_upfile($realfile)”
“$scfile= check_upfile ($realfile)”
“if(! move_uploaded_file($tmpfile,$scfile)){”,“die(' 文 件 不 能 上传!');}”等语句,来调用该函数,执行检测上传的图片类型,文件格式以及存储到非公开目录等操作。
在下载文件时,需要正确设置Content-Type类型信息,针对图片文件需要检测文件头,在有些时候,需要在响应头中指定“Content-Disposition:attachment”参数信息。
如果在下载的时候,没有经过预设的脚本进行处理,就直接可以在Web服务器上的公开目录上出现的话,就说明服务器端配置存在一定的的问题。
例如对于Apache来说,和Content-Type信息相关的设置保存在名为“mime.types”的文件之中,需要对其进行检测,来查看是否存在错误的Content-Type信息。
因为Apache是基于文件扩展名,来产生相应的Content-Type信息的。在下载图片文件时,依然需要对其文件头进行校验,可以有效提高安全性。
为了避免在浏览器中直接执行文件,而仅仅将其下载到本地,可以在响应消息头中执行“Content-Disposition:attachment”,或者将Content-Type设 置 为“application/octetstream”,就可以实现单纯的下载操作。
例如对于PDF来说,可在网站程序中使用上述“givetouserfile.php” 脚本,所不同的是在其中将的“$mimes=array”行内容修改 为“$mimes=array('pdf'=>'application/octetstream')”。
并且在尾部添加:
“header('Content-Disposition: attachment;filename="'.basename($file).'" ');”
“readfile($path)”等语句,来设置只用于下载的文件信息。
这样当浏览器处理时就直接进行下载了。