4.10 文件处理相关的问题
Web 应用会通过多种多样的形式和文件打交道。而本节的主题就是处理文件时可能产生的 安全隐患。
在有些 Web 应用中,外界能够通过传入参数的形式来指定服务器中的文件名。比如由外界参数来指定模板文件的情况。这样的 Web 应用可能会招致以下攻击。
- 非法访问 Web 服务器内的文件(目录遍历)
- 调用 OS 命令(OS 命令注入)
其中,目录遍历漏洞将在 4.10.1 节中讲述。此外,通过目录遍历攻击有时还能够执行 OS 命令,不过这里我们将此问题归为 OS 命令注入的范畴并在 4.11 节中讲述。
另外,如果数据文件或配置文件被保存在公开目录中,就可能会被外界浏览而造成信息泄漏。详情将在 4.10.2 节中讲述。
4.10.1 目录遍历漏洞
概要
Web 应用中允许外界以参数的形式来指定服务器上的文件名时,如果没有对文件名进行充分的校验,就可能会造成意料之外的问题,比如文件被浏览、篡改或删除。该安全隐患被称为目录遍历漏洞。
目录遍历漏洞会造成以下影响。
- 浏览 Web 服务器中的文件
- 泄漏重要信息
- 篡改或删除 Web 服务器中的文件
- 篡改网页内容,散布谣言或恶意诽谤他人
- 布下圈套将用户诱导至恶意网站
- 删除脚本文件或配置文件导致服务器宕机
- 通过篡改脚本文件从而在服务器上执行任意脚本
目录遍历漏洞的防范策略如下,执行其中一项即可。
- 避免由外界指定文件名
- 文件名中不允许包含目录名
- 限定文件名中仅包含字母和数字
目录遍历漏洞总览
攻击手段与影响
下面我们就来看一下目录遍历攻击的手段与影响。
以下是能够使用 template= 的形式来指定页面模板文件的脚本。
代码清单 /4a/4a-001.php
<?php
define('TMPLDIR', '/var/www/4a/tmpl/');
$tmpl = $_GET['template'];
?>
<body>
<?php readfile(TMPLDIR . $tmpl . '.html'); ?>
菜单(以下略)
</body>
常量 TMPLDIR
指定的是存放模板文件的目录名。模板文件名由查询字符串中的 template
指定,并被赋值到变量 $tmpl
中。脚本使用 readfile
函数读取模板文件,然后将其原封不动地放到响应信息中。
下面为模板文件的示例。
代码清单 /4a/tmpl/spring.html
你好,已经是春天了呢。<br>
通过以下 URL 执行脚本就能够读取上述模板文件。
http://example.jp/4a/4a-001.php?template=spring
图 4-92 示例脚本的执行例
此时,脚本中被拼接成的文件名如下所示。
正常情况下拼接成的文件名
/var/www/4a/tmpl/spring.html
接下来我们就来看一下如何对其展开攻击。使用以下 URL 执行示例脚本。
http://example.jp/4a/4a-001.php?template=../../../../etc/hosts%00
图 4-93 显示了 Linux 的配置文件内容
页面中显示的为 Linux 的配置文件 /etc/hosts 的内容。也就是说,通过目录遍历攻击能够浏览操作系统的配置文件。此时,脚本内被拼接成的文件名如下所示。其中 [NUL]
为空字节(字符编码为 0 的字符)57 。
57 正如 1. 4.2 节中介绍的那样,空字节在 C 语言中表示字符串的结束。
攻击时拼接成的文件名
/var/www/4a/tmpl/../../../../etc/hosts[NUL] .html
由于 ../ 表示上层目录,空字节又会迫使文件名字符串结束,因此,将此文件名标准化后,实际被访问的文件名即为如下内容。
标准化后的文件名
/etc/hosts
因此,最终页面显示的是 etc/hosts 文件的内容。
由此可见,一旦 Web 应用中存在目录遍历漏洞,攻击者就能够随意访问服务器上的任何文件。
不过上面的例子仅仅展示了读取文件的情况,其实,依据应用的内部实现,有时还能够进行覆盖或删除文件等操作,从而造成数据被篡改。
此外,一旦攻击者能够通过目录遍历来编辑 PHP 等脚本文件,就能将编辑后的脚本在 Web 服务器上运行,从而也就相当于能够执行任意脚本。这时攻击造成的影响与 OS 命令注入(参考 4.11 节)相同,即能使计算机下载恶意程序或对系统进行非法操作等。
专栏:从脚本源码开始的一连串的信息泄漏
通过目录遍历攻击访问 Web 服务器上的文件时需要知道文件名。虽然 /etc/hosts 是操作系统中固定的文件名,但由于一般来说第三方无法得知存储个人信息等文件的文件名,因此有人就会觉得不会有遭到攻击的风险。
然而,还有一种攻击手段为,先通过目录遍历攻击查看脚本的源代码,然后再使用 open 语句等来调查被指定文件的文件名。其中,在试验环境的“/4a/ 菜单”中点击“3. 4a-001: 目录遍历(脚本:显示源码)”链接,就能够查看脚本源码。执行后虽然浏览器上不会显示 PHP 的源码,但通过查看 HTML 的源码就能够确认 PHP 脚本。
安全隐患的产生原因
当应用满足以下 3 个条件时,就有可能产生目录遍历漏洞。
- 外界能够指定文件名
- 能够使用绝对路径或相对路径等形式来指定其他目录的文件名
- 没有校验是否允许访问拼接后的文件名
如果从开发者的角度来考虑的话,笔者觉得,漏洞的产生可能是因为开发者没有考虑到“外界能够指定其他目录”的可能性。
由于目录遍历漏洞的产生需要同时满足以上 3 个条件,因此,只要使其中任意一项无法满足也就能够将漏洞消除。
对策
概要中已经简单介绍过消除目录遍历漏洞的方法,即实施以下任一项。
- 避免由外界指定文件名
- 文件名中不允许包含目录名
- 限定文件名中仅包含字母和数字
下面我们就对以上方法进行详细说明。
- 避免由外界指定文件名
如果能够避免文件名由外界指定,就能从根本上解决目录遍历漏洞。具体方法有如下几种。
- 将文件名固定
- 将文件名保存在会话变量中
- 不直接指定文件名,而是使用编号等方法间接指定
而至于这些方法的具体操作,此处就不再逐一介绍。
- 文件名中不允许包含目录名
如果文件名中不包括目录名(包括 ../),就能确保应用中只能访问给定目录中的文件,从而也就消除了目录遍历漏洞产生的可能性。
表示目录的字符 /、\、: 等因操作系统而异,不同的操作系统应当采用不同的程序库。在 PHP 中则能够使用
basename
函数。basename
函数会接收带有目录的文件名(也包括 Windows 的盘符),并返回末尾的文件名部分。例如basename('../../../../etc/hosts')
返回的结果即为 hosts。利用
basename
函数的对策示例如下所示。代码清单 /4a/4a-001b.php
<?php define('TMPLDIR', '/var/www/4a/tmpl/'); $tmpl = basename($_GET['template']) ; ?> <body> <?php readfile(TMPLDIR . $tmpl . '.html'); ?> 菜单(以下略) </body>
专栏:basename 函数与空字节
PHP 的
basename
函数在处理时不会删除空字节 58 ,因此,即使使用了basename
函数也还是有可能会出现文件扩展名被更改的情况。比如,假设以下脚本中的扩展名被指定为 txt。$file = basename($path) . '.txt';
这时,如果外界传入的文件名为 a.php%00(已经过百分号编码),就会生成如下文件名。
图 4-94 上述脚本生成的文件名
然而,由于 Windows 或 Unix 等多数操作系统中都使用 C 语言形式的字符串,因此文件名中有空字节(\0)时就会被视为文件名结束。这样一来,实际打开的文件就变成了 a.php,应用中指定的 txt 扩展名则被忽略了。
由此可见,文件名由外界传入的情况下,有必要对文件名进行校验以确保其中不包含空字节。
- 限定文件名中仅包含字母和数字
如果能够限制文件名的字符种类仅为字母和数字,那么用于目录遍历攻击的字符就会无法使用,因此这个方法也能作为目录遍历的防范策略。
下面我们就来尝试在 4a-001.php 中实施这一方法,如下所示。
代码清单 /4a/4a-001c.php
<?php define('TMPLDIR', '/var/www/4a/tmpl/'); $tmpl = $_GET['template']; if (! preg_match('/\A[a-z0-9]+\z/ui', $tmpl)) { die('remplate 仅能指定字母或数字 '); } ?> <body> <?php readfile(TMPLDIR . $tmpl . '.html'); ?> 菜单(以下略) </body>
这里通过
preg_match
匹配正则表达式确认了文件名变量$tmpl
中仅包含字母和数字。ereg
函数由于不能正确处理空字节(非二进制安全),因此不能被用于本方法。详情请参考 4.2 节。
58 确认于 PHP5.3.5。
总结
本节讲述了访问文件的处理中容易混入的目录遍历漏洞。解决目录遍历漏洞的最佳方法是不允许外界指定文件名。因此推荐在设计阶段就开始探讨是否能够做到这一点。
4.10.2 内部文件被公开
概要
Web 服务器的公开目录中有时会放置对外保密的文件。这种情况下,外界一旦得知文件的 URL,就能够浏览这些内部文件。
内部文件被公开会造成如下影响。
- 重要信息被泄漏
防范内部文件被公开的对策为,不在公开目录中放置内部文件。或者保险起见,也可以直接禁用目录列表功能。这一点在后面会进行详述。
内部文件被公开总览
攻击手段与影响
首先使用以下 URL 浏览本书提供的虚拟机。
http://example.jp/4a/data/
如下图所示,页面上列出了目录内的所有文件。
图 4-95 目录内的文件一览
像上面这样,使用 URL 指定目录名时,页面上会罗列出目录中的所有文件,这一功能就叫作目录列表(Directory Listing)。
点击页面上的 user.txt 链接,此时页面显示如下。
图 4-96 文件内容被显示
如文件名所示,页面上显示了用户信息文件 user.txt 的内容。
虽然这种攻击的手法很简单,但 2004 年以前发生的 Web 网站泄漏用户个人信息的事件多数都是起因于这种攻击模式。
安全隐患的产生原因
导致内部文件被公开的原因为,内部文件被放在了公开目录中。当应用满足以下条件时,放置在公开目录下的文件就能够被外界访问。
- 文件被放置在公开目录中
- 有方法得知访问文件的 URL
- 没有对文件设置访问权限
其中,得知访问文件的 URL 的手段有如下几种。
- 目录列表功能被设为有效
- 文件名为日期、用户名或连续数值等能够被推测的值
- user.dat、data.txt 等常见文件名
- 通过错误消息或其他安全隐患而得知文件名
- 被外部网站链接进而被搜索引擎收录
Apache 中可以设置 httpd.conf 或 .htaccess 来限制对文件的访问,但仅仅依靠这些设置来禁止访问文件还是存在风险的。因为设置可能一不注意就会被更改。过去发生的信息泄漏事件中,虽然很多在一开始时也都通过设置限制了文件访问,但是在迁移服务器时限制就有可能会被去除,从而就会导致信息泄露的发生。
对策
防范内部文件被公开的根本性对策为,不将内部文件放置在公开目录下。为了做到这一点,可以采用以下方法。
- 设计应用程序时,决定存放文件的安全场所
- 租用服务器时确认能够使用非公开的目录
另外,保险起见还可以将目录列表功能设为无效。其中,Apache 中可以如下编辑 httpd.conf 文件来进行设置。
<Directory 指定路径 >
Options -Indexes 其他选项
其他设置
</Directory>
如果租用服务器不允许更改 httpd.conf,可以在公开目录下放置名为 .htaccess 的文件,并进行如下设置。但是,鉴于有些租用服务器厂商可能不允许使用 .htaccess 来更改设置,因此事先一定要对此加以确认。
Options -Indexes
参考:Apache 中隐藏特定文件的方法
如之前所述,为了防止内部文件被公开,原则上应当彻底贯彻不将非公开文件放置在公开目录下的方针。但是,在既有 Web 网站中存在此问题时,可能就无法通过简单的移动文件的方法来解决问题。这种情况下,可以通过设置禁止外界访问特定文件,来姑且进行暂时性的处理。Apache 中 .htaccess 的设置方法如下所示。该示例中设置了禁止外界浏览扩展名为 txt 的文件。详情请参考 Apache 的说明文档。
代码清单 .htaccess
<Files "*.txt">
deny from all
</Files>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论