PHP临时文件的安全性分析


Posted in PHP onJuly 04, 2014

一、简介

临时文件,顾名思义是临时产生的文件,且文件的生命周期很短。

然而,很多应用的运行都离不开临时文件,临时文件在我们电脑上无处不在,主要有以下几种形式的临时文件:

1.文件或图形编辑程序,所生成的中间文件
2.数据库查询时,生成的临时缓存文件,提供之前的结果数据而,以减少再次访问数据库的代价;通常用于远程数据库或远程xml的服务
3.文件被上传后在服务端的临时储存,其文件名为php的全局变量$_FILES['userfile']['tmp_name']的值
4.在http请求中,用于存放session的临时文件,这些文件名通常就是sessionid(如 sess_7483ae44d51fe21353afb671d13f7199)
5.在不同应用或相同应用传递数据,而对方要求基于文件的输入,此时用临时文件存放数据

二、临时文件的安全特征

临时文件的最大特征就是它的非持久性,除此之外,从安全性的角度,可以从以下几个方面关注临时文件的其它特点或风险:

1.临时文件的位置

临时文件通常被创建并存放在默认的路径,在一个典型的Linux系统中,至少有两个目录或分区保持着临时文件。其中之一是/tmp目录,再者是/var/tmp。在更新的Linux内核的系统中,还可能有/dev/shm,它是用tmpfs文件系统装载的。有时临时文件,也可能放在用户home目录下的隐藏子目录中。使用默认临时文件目录的好处在于,系统进程可以方便查找和读写。

然而,默认临时文件的存放目录可能成为损害系统安全的僵尸和rootkit的温床。这是因为在多数情况下,任何人(或任何进程)都可以向这些目录写入东西,有不安全的许可问题。比如我们都知道sticky bit,该位可以理解为防删除位。如果希望用户能够添加文件但同时不能删除文件, 则可以对文件使用sticky bit位。设置该位后,就算用户对目录具有写权限,也不能删除该文件。多数Linux发行版本在临时目录上设置sticky位,这意味着用户A不能清除属于用户B的一个文件,反之亦然。但是,根据文件自身的许可,用户A有可能查看并修改那个文件的内容。

2.临时文件的持久性

前面提到临时文件是非持久的,在程序结束时,会被删除,但有的时候临时文件也会被迫持久保存了,没有被删除,如:

2.1 应用程序在关闭前崩溃了,还没有机会删除临时文件
2.2 应用程序还跑着,但操作系统崩溃了
2.3 文件复制过程中由于空间问题而复制失败,导致中间文件没有删除
2.4 操作系统进程通常会定期清空的默认临时文件目录,但可能因为某些原因,而删除失败
2.5 写得不好的应用程序,可能忽略或者忘记了删除临时文件

3.临时文件的风险性

无用的临时文件像幽灵一样存在你的服务器上,一方面占用硬盘,另一方面,可以被其它人非法使用,存着如下一些风险:

3.1 可见性

众所周知,将私有数据公开很有风险。一旦用户通过某些手段(如shell或者ftp)窃取了你的临时文件,就可以获取到用户或企业的私有数据,从而对你造成影响。

例如:临时文件2011_Confidential_Sales_Strategies.tmp,可能暴露你们公司2011年的商业策略,这对你的竞争对手来说,将很有用处;而对于session劫持者来说,存放用户session信息的临时文件sess_95971078f4822605e7a18c612054f658非常关键。

除此之外,还有别的情况临时文件可能会被偷窥,如:一个拼写检查的服务,返回结果的url是:http://bad.example.com/spellcheck.php?tmp_file=spellcheck46 ,攻击者分析你的url参数后使用http://bad.example.com/spellcheck.php?tmp_file=spellcheck45 就可以访问到前一个用户的验证结果了。

3.2 可执行性

通常临时文件是不可执行,但如果攻击者上传了一个php脚本到你的临时目录,而且通过某种方式执行了它,那可能造成悲剧了。

3.3 临时文件被劫持

攻击者可能为了自己的目的,而劫持你的临时文件。他可能替换你的临时文件,也可能在你的临时文件后面追加一些信息。

劫持临时文件的目的包括:

(1)让你的应用程序处理他的数据,而不是你自己的数据
(2)暴露隐私数据,比如系统的密码文件,或者其它php安全模式不能正常读的文件
(3)删除数据,阻碍请求的正常进行
(4)创建并输出虚假的数据,破坏请求的结果
(5)通过提供虚假的数据,对使用数据进行下一步处理的应用程序造成破坏
(6)将你的输出重定向到其它地方,可以方便攻击者访问或者覆盖系统文件

劫持通常与竞争条件相关。当两个不同的进程操作同一个文件的时候,就可能产生竞争条件。例如,一个读进程和一个写进程同时操作一段数据,当写进程只完成了一部分的时候,读进程已经完成,这样读的到内容一部分是新的,一部分是旧的,也就是我们常说的读脏数据。

临时文件的劫持,在一定程度上会造成竞争条件,除非劫持者准确的把握时间和位置,否则就会造成此类安全问题。

三、预防临时文件被恶意使用

前面我们介绍了临时文件的概念,以及临时文件被恶用可能带来的危害,这个部分主要介绍一些策略来预防临时文件被恶意利用,以及减少其带来的危害。

1.调整存放位置

防止临时文件被恶意利用的最重要,也是最简单的一步就是让你的临时文件目录以及名字不容易被猜到。任何对临时文件的恶意利用,攻击者都必须知道临时文件的名字和路径,因此你应该尽可能的让他难以猜到你的临时文件名字及路径。

建议你在临时文件目录的选择时,还是将你的临时文件放在默认的目录下吧,这样系统进程可以方便找到以及读写。而把精力花费放在为文件名想个合适的难猜的名字。

php的tempnam()函数,可以创建一个临时文件,并且其自动生成的文件名不会与当前目录下的其它文件名冲突,此函数创建的文件默认权限是600,即rw——-。

例如

$filename = tempnam( ‘..', ‘myTempfile');

运行后可能生成一个名为myTempfile1af的文件,当第二次运行的时候就生成了名为myTempfile1b0的文件名。
也许一些编程实践指南会建议你在使用tempnam()生成文件的时候,用一些有意义的前缀来命名,这样能通过文件名看出文件中包含的数据或者需要此数据的应用,但从安全性的角度来看最好不要这样,这样等于为攻击者指明了方向。

这里介绍一种方法,即能有一定意义的前缀同时也让攻击者不那么好猜,如下:

<?php
// define the parts of the filename
define(‘TMP_DIR','/tmp/');
$prefix = ‘skiResort';
// construct the filename
$tempFilename = uniqid( $prefix, TRUE );
// create the file
touch( $tempFilename );
// restrict permissions
chmod ( $tempFilename, 0600 );
// now work with the file
// … assuming data in $value
file_put_contents( $tempFilename, $value );
// …
// when done with temporary file, delete it
unlink ( $tempFilename );
?>

 这个脚本通过uniqid()函数,生成的文件名格式为:/tmp/skiResort392942668f9b396c08.03510070,并通过chmod将文件的权限设置为600。

如果你需要与其它应用共享信息,比如用户密码或运行时生成的随机token,这里你可能需要对文件名加密,只有知道这个密钥的应用程序才能读取或修改文件内容。

如下是一个简单的生成加密文件名文件的示例:

<?php
$pathPrefix = ‘/tmp/skiResort';
// for demonstration, construct a secret here
$secret = ‘Today is ‘ . date( “l, d F.” );
$randomPart = sha1( $secret );
$tempFilename = $pathPrefix . $randomPart;
touch( $tempFilename );
chmod ( $tempFilename, 0600 );
// now work with the file
// … assuming data in $value
file_put_contents( $tempFilename, $value );
// …
// when done with temporary file, delete it
unlink ( $tempFilename );
?>

2.约束访问权限

为了降低临时文件被执行或劫持的可能性,需要设置临时文件和临时文件目录的访问权限。通常情况下,将临时文件的权限设置为rw——-,临时文件目录的权限设置为rwx——。

此外,也可以通过设置apache的配置文件来限制访问(只有你将临时文件放在www目录下的时候),如下:

order deny,allow
deny from all

3.只写已知文件

既然你是临时文件的创建者和作者,那你应该随时知道哪些文件存在,文件里有哪些内容。前面提到的方法,只是让临时文件劫持更困难,但不能完全杜绝劫持者替换文件或者在文件后面追加一些内容的可能,所以在你创建或写文件时,需要仔细检查文件内容是否满足要求。

当你使用w+的方式,创建了一个文件,在你开始写之前,这个文件应该为空,如下

<?php
if ( filesize( $tempFilename ) === 0 ) {
// write to the file
} else {
exit ( “$tempFilename is not empty.\nStart over again.”);
}
?>

如果文件不为空,可能你创建的有问题,也有可能劫持者在你创建与写文件的这个时间段内作了手脚。

还有可能,你第一次成功写入了临时文件,但在你后面的写的过程中,劫持者对这个临时文件进行了一些操作,这种情况可以通过检验码的方式来检查,如下:

<?php
// write something to the file; then hash it
$hashnow = sha1_file( $tempFilename );
$_SESSION['hashnow'] = $hashnow;
// later, get ready to write again
$hashnow = sha1_file( $tempFilename );
if ( $hashnow === $_SESSION['hashnow'] ) {
// write to the file again
// get and save a new hash
$hashnow = sha1_file( $tempFilename );
$_SESSION['hashnow'] = $hashnow;
} else {
exit ( “Temporary file contains unexpected contents.\nStart over again.”);
}
?>

4.只读已知文件

与只写已知文件类似,在读文件前需要检查检验码是否一致,防止临时文件被篡改。除此之外,如果你使用了openssl,可以在写文件的时候,将合法证书放在文件的末尾,这样的读的时候可以先检查文件末尾是否存在合法的证书;如果你没有使用openssl,也可以写入一段特定的算法生成的token,原理类似。

5.检查上传的文件

判断文件是否是通过 HTTP POST 上传的

bool is_uploaded_file ( string $filename )

如果 filename 所给出的文件是通过 HTTP POST 上传的则返回 TRUE。这可以用来确保恶意的用户无法欺骗脚本去访问本不能访问的文件,例如 /etc/passwd。 如果上传的文件有可能会造成对用户或本系统的其他用户显示其内容的话,这种检查显得格外重要。

为了能使 is_uploaded_file() 函数正常工作,必须指定类似$_FILES['userfile']['tmp_name'] 的变量,而不是从客户端上传的文件名 $_FILES['userfile']['name']。需要注意的是is_uploaded_file返回false,不一定是上传文件被劫持了,也有可能是文件太大或者上传部分等,这些可以通过$_FILES['userfile']['error']查看。

PHP 相关文章推荐
从网上搜到的phpwind 0day的代码
Dec 07 PHP
让你成为更出色的PHP开发者的10个技巧
Feb 25 PHP
Zend Studio (eclipse)使用速度优化方法
Mar 23 PHP
两种设置php载入页面时编码的方法
Jul 29 PHP
php递归json类实例
Dec 02 PHP
如何解决phpmyadmin导入数据库文件最大限制2048KB
Oct 09 PHP
yii2中使用Active Record模式的方法
Jan 09 PHP
thinkPHP交易详情查询功能详解
Dec 02 PHP
Thinkphp开发--集成极光推送
Sep 15 PHP
PHP实现双链表删除与插入节点的方法示例
Nov 11 PHP
浅谈PHP中如何实现Hook机制
Nov 14 PHP
PHP类的自动加载机制实现方法分析
Jan 10 PHP
PHP curl实现抓取302跳转后页面的示例
Jul 04 #PHP
PHP不用递归遍历目录下所有文件的代码
Jul 04 #PHP
对于ThinkPHP框架早期版本的一个SQL注入漏洞详细分析
Jul 04 #PHP
PHP+Memcache实现wordpress访问总数统计(非插件)
Jul 04 #PHP
php+memcache实现的网站在线人数统计代码
Jul 04 #PHP
PHP轻量级数据库操作类Medoo增加、删除、修改、查询例子
Jul 04 #PHP
CodeIgniter安全相关设置汇总
Jul 03 #PHP
You might like
mantis安装、配置和使用中的问题小结
2014/07/14 PHP
PHP实现图片压缩的两则实例
2014/07/19 PHP
PHP数组中头部和尾部添加元素的方法(array_unshift,array_push)
2017/04/10 PHP
PHP 年月日的三级联动实例代码
2017/05/24 PHP
laravel 5.1下php artisan migrate的使用注意事项总结
2017/06/07 PHP
jquery 插件开发方法小结
2009/10/23 Javascript
jQuery实现表单input中提示文字value随鼠标焦点移进移出而显示或隐藏的代码
2010/03/21 Javascript
jQuery中after的两种用法实例
2013/07/03 Javascript
js实现简单的星级选择器提交效果适用于评论等
2013/10/18 Javascript
鼠标滚轮改变图片大小的示例代码
2013/11/20 Javascript
点击A元素触发B元素的事件在IE8下会识别成A元素
2014/09/04 Javascript
Nodejs中调用系统命令、Shell脚本和Python脚本的方法和实例
2015/01/01 NodeJs
javascript实现五星评分功能
2015/11/10 Javascript
jQuery实现智能判断固定导航条或侧边栏的方法
2016/09/04 Javascript
微信小程序实现多个按钮toggle功能的实例
2017/06/13 Javascript
使用bootstrap实现下拉框搜索功能的实例讲解
2018/08/10 Javascript
JS返回页面时自动回滚到历史浏览位置
2018/09/26 Javascript
解决前后端分离 vue+springboot 跨域 session+cookie失效问题
2019/05/13 Javascript
JavaScript实现简单计算器功能
2019/12/19 Javascript
angular组件间传值测试的方法详解
2020/05/07 Javascript
django处理select下拉表单实例(从model到前端到post到form)
2020/03/13 Python
python实现处理mysql结果输出方式
2020/04/09 Python
pip已经安装好第三方库但pycharm中import时还是标红的解决方案
2020/10/09 Python
HTML5实现QQ聊天气泡效果
2017/06/26 HTML / CSS
阿拉伯世界最大的电子卖场:Souq埃及
2016/08/01 全球购物
大学本科毕业生的自我鉴定
2013/11/26 职场文书
初三学生个人自我评定
2014/04/06 职场文书
英语自我介绍演讲稿
2014/09/01 职场文书
2014年度党员自我评议
2014/09/13 职场文书
淘宝好评语句大全
2014/12/31 职场文书
雷锋的故事观后感
2015/06/10 职场文书
《水浒传》读后感3篇(范文)
2019/09/19 职场文书
在CSS中映射鼠标位置并实现通过鼠标移动控制页面元素效果(实例代码)
2021/04/22 HTML / CSS
python3+PyQt5+Qt Designer实现界面可视化
2021/06/10 Python
python常见的占位符总结及用法
2021/07/02 Python
MySQ InnoDB和MyISAM存储引擎介绍
2022/04/26 MySQL