4.6 不完善的会话管理
Web 应用中经常使用会话管理机制来记忆认证结果等当前状态。当今主流的会话管理机制为,使用 Cookie 等记忆会话 ID 这个标识符,而此会话 ID 的作用就相当于获取服务器端信息的钥匙。
接下来,本节就将讲述会话管理机制以及使用方法不妥善而产生的安全隐患。
4.6.1 会话劫持的原因及影响
如果由于某些原因,某用户的会话 ID 被第三方得知的话,就会出现他人伪装成该用户访问应用的危险。第三方恶意利用会话 ID 来伪装成他人的攻击手段就被称为会话劫持。
第三方获取会话 ID 的手段有如下 3 类。
- 预测会话 ID
- 窃取会话 ID
- 挟持会话 ID
下面我们就来分别看一下以上 3 种手段的概况。
- 预测会话 ID
如果生成会话 ID 的方法不妥善,用户的会话 ID 就可能会被第三方预测成功,进而造成会话劫持。第 3 章中所介绍的连续数值就是一种不妥善的会话 ID,除此之外,基于日期时间或用户名生成的会话 ID 也不安全。开源软件等生成会话 ID 的逻辑对外公开的情况下,外界就能根据代码中的逻辑推测出会话 ID,而源代码或逻辑不公开的情况下外界也有可能稍费时日从而破解出会话 ID 的生成方法。
- 窃取会话 ID
如果会话 ID 被外界窃取,就有可能造成会话劫持。窃取会话 ID 的方法有如下几种。
- 生成 Cookie 时的属性设置不妥善而遭泄漏(参考第 3 章)
- 会话 ID 在网络上被监听(参考 7.3 节)
- 由于跨站脚本等应用中的安全隐患而遭泄漏(后述)
- 由于 PHP 或浏览器等平台的安全隐患而遭泄漏
- 会话 ID 保存在 URL 中时经由 Referer 消息头泄漏(参考 4.6.3 节)
应用中能被用于窃取会话 ID 的代表性安全隐患有以下几种。
- 跨站脚本(XSS)(参考 4.3.1 节)
- HTTP 消息头注入(参考 4.7.2 节)
- 嵌入在 URL 中的会话 ID(参考 4.6.3 节)
关于各隐患的详情请参考各个章节。
- 挟持会话 ID
除了窃取会话 ID 这种方式外,如果能将会话 ID 强制设置到用户的浏览器中,攻击者也就相当于“得知”了用户的会话 ID,因此也就能够形成会话劫持。这种攻击被称为“会话固定攻击”(Session Fixation Attack)。会话固定攻击在第 3 章已经做过概述,其防范策略等详情将于 4.6.4 节讲述。
- 会话劫持的方法总结
接下来,我们将以上介绍的会话劫持的方法加以归纳,如下表所示。
表 4-13 会话劫持总结
分类 攻击对象 攻击方法 安全隐患 解说 预测会话 ID 应用程序 预测会话 ID 自制会话管理机制中的安全隐患 4.6.2 节 中间件 推测会话 ID 中间件的安全隐患 7.1 节 窃取会话 ID 应用程序 XSS XSS 漏洞 4.4.1 节 HTTP 消息头注入 HTTP 消息头注入漏洞 4.7.2 节 恶意利用 Referer 嵌入在 URL 中的会话 ID 4.6.3 节 中间件 同应用程序 中间件的安全隐患 7.1 节 网络 网络监听 Cookie 的安全属性不完善等 4.8.2 节 挟持会话 ID 应用程序 会话固定攻击 会话 ID 固定漏洞 4.6.4 节 由上表可知,造成会话劫持的安全隐患多种多样,因此,应对会话劫持就需要将这些安全隐患各个击破。而本节所要讲述的就是生成会话 ID 时产生的如下安全隐患。
- 会话 ID 可预测
- 会话 ID 嵌入 URL
- 固定会话 ID
其他安全隐患请参考表中“解说”所示页的内容。
- 会话劫持的影响
用户被会话劫持后,他人就能伪装成该用户,进而造成如下影响。
- 查看用户的重要信息(个人信息、邮件等)
- 利用用户的权限进行操作(转账、购物等)
- 使用用户的账号发送邮件、发布文章、更改设置等
4.6.2 会话 ID 可预测
概要
如果 Web 应用中会话 ID 的生成规则不完善,用户的会话 ID 就有可能被他人成功预测,从而造成会话劫持。
会话 ID 被他人预测成功所造成的影响,同前面讲述的会话劫持的影响一样。
为了避免生成可预测的会话 ID 而引入安全隐患,应当停止自己实现会话管理机制,而使用久经考验的编程语言或中间件(PHP、Java/J2EE、ASP.NET 等)提供的会话管理机制。
会话 ID 可预测漏洞总览
攻击手段与影响
首先我们来看一下针对会话 ID 可预测漏洞的典型的攻击模式及其影响。
针对会话 ID 可预测漏洞的攻击有以下三个步骤。
1. 收集对象应用的会话 ID
2. 推测会话 ID 的生成规则
3. 在对象应用中试验推测出的会话 ID
- 常见的会话 ID 生成方法
为了预测会话 ID 的生成规则,首先就需要对常见的会话 ID 生成规则有所了解。由于本书并非攻击指导书,因此不会详细说明推测会话 ID 的方法。但就笔者多年来诊断安全隐患的经验来说,会话 ID 的生成大多都是基于以下项目。
- 用户 ID 或邮箱地址
- 远程 IP 地址
- 日期与时间(UNIX 时间戳或年月日时分秒的字符串)
- 随机数
生成会话 ID 时,有时会原封不动地使用上述值,有时也会选取几种组合使用,然后再进行加密(十六进制或 Base64)或者散列函数处理。图 4-51 即展示了常见的会话 ID 的生成方法。
图 4-51 常见的会话 ID 生成方法
其中,用户 ID 和日期时间是外界能够得知的数据,而这也就是造成安全隐患的根源。
针对会话 ID 可预测漏洞展开攻击时,攻击者会基于已知信息推导会话 ID 的生成规则,将收集到的会话 ID 按照图 4-51 的模型逐个进行验证。
- 使用推测出的会话 ID 尝试伪装
攻击者推测出会话 ID 之后,就会在对象应用中试用。如果攻击取得成功,会话就会处于有效的状态,因此攻击者能立刻得到攻击是否成功的反馈。
- 伪装造成的影响
攻击者成功伪装成用户后,就能够以用户的权限使用对象应用中的所有功能,如查看重要信息、发布 / 更新 / 删除数据或文章、购物、转账等。
但是,那些浏览前需要再次输入密码的页面,即使伪装成功后也无法访问。因为会话劫持的攻击者并不知道用户的密码。因此,关键处理前要求用户再次输入密码(再认证),是防范会话劫持的辅助性对策。
另一方面,如果更改密码时不需要输入当前密码,攻击者就能够通过更改密码而掌握用户的密码,这时攻击将造成更大的危害。
安全隐患的产生原因
正如前面所说,产生会话 ID 可预测漏洞的技术性原因,主要在于会话 ID 是基于可预测的信息生成的。而更深层的原因,则可以说是源于在应用中自制会话管理机制。通常情况下,在 Web 应用开发中,特意去开发生成会话 ID 的程序毫无意义。原因如下。
- 主流的 Web 应用开发工具中提供有会话管理机制
- 开发能够生成安全的会话 ID 的程序有很高的技术要求
而且,即使主流的 Web 应用开发工具中生成会话 ID 的部分存在漏洞,也肯定会有安全性方面的专家指出而使其得到完善。因此,如果是普通用途的 Web 应用,都应当使用开发工具中提供的会话管理机制。
对策
防范会话 ID 可预测漏洞最现实以及最有效的对策,就是使用 Web 应用开发工具中提供的会话管理机制。
由于某些特殊原因而不得不自制会话管理机制时,建议使用密码学级别的伪随机数生成器 33 来生成足够多位数的会话 ID。
33 指理论上能够保证在足够长的时间内无法被预测的随机数。
- 改善 PHP 的会话 ID 的随机性的方法
PHP 中默认生成会话 ID 的方法为将下列值组合后再经过 MD5 散列函数处理。
- 远程 IP 地址
- 当前时间
- 随机数(不是密码学级别的伪随机数)
这也符合图 4-51 中常见的会话 ID 的生成方法。由于其生成会话 ID 的算法相当复杂,目前还没有该会话 ID 的破解方法,但是这样的设计在理论上并不能保证安全性。
但我们可以编辑 php.ini 文件来改善会话 ID 的生成规则,使其生成基于安全的随机数的会话 ID。这里我们将 php.ini 设置如下。
[Session] ;; Windows 中不需要设置 entropy_file session.entropy_file = /dev/urandom session.entropy_length = 32
/dev/urandom 是 Linux 等多数基于 Unix 的操作系统中提供的随机数生成器,可作为设备文件使用。Linux 中的 /dev/urandom 经受了全世界专家的检验,并没有曝出重大问题,因此可以安心使用 34 。
Windows 中没有类似于 /dev/urandom 的功能,但在 PHP5.3.3 以后的版本中,通过将 session.entropy_length 设为 0 以外的值,就能基于 Windows Random API 生成的值来生成会话 ID。
由于此设置不会产生副作用,因此建议读者们在开发时将上述设置作为开发标准。
34 /dev/urandom 的实现方法因 OS 而异,在 Linux 以外的操作系统中使用 /dev/urandom 时,请事先调查确认有无安全隐患的相关记录。
参考:自制会话管理机制产生的其他隐患
自制会话管理机制时,除了会话 ID 可预测漏洞外,还需警惕其他安全隐患。就笔者多年来诊断安全隐患的经验来看,以下安全隐患需要注意。
- SQL 注入漏洞
- 目录遍历漏洞
具体来说,PHP 官方文档中会话管理机制的自定义 API 的示例脚本中就存在目录遍历漏洞 35 。同样,由于自定义 PHP 的会话管理机制而混入 SQL 注入漏洞的案例也时有发生。
35 详情见笔者的博客: http://www.tokumaru.org/d/20080818.html#p01 。写作本书时已经确认在 PHP 的最新版本 5.3.5 中也存在此问题。
正因为存在这些案例,因此,在自制或自定义会话管理机制时,务必要进行慎重的设计和仔细的检查。除非迫不得已,还是推荐直接使用既有的会话管理机制。
4.6.3 会话 ID 嵌入 URL
概要
会话 ID 有时并不保存在 Cookie 中,而是被保存于 URL。PHP、Java 和 ASP.NET 等都提供了将会话 ID 嵌入 URL 的功能。由于一些手机的浏览器不支持 Cookie,因此手机版 Web 应用也广泛采用将会话 ID 嵌入 URL 的做法。而面向 PC 的网站偶尔也能看到 URL 中包含会话 ID。以下就是会话 ID 嵌入 URL 的示例。
http://example.jp/mail/123?SESSID=2F3BE9A31F093C
会话 ID 嵌入 URL 有可能会导致会话 ID 经由 Referer 消息头外泄,从而造成伪装攻击。
而为了防范会话 ID 嵌入 URL 而导致伪装攻击,可以在程序中设置禁止将会话 ID 嵌入 URL。手机版的 Web 应用等有时不得不将会话 ID 嵌入 URL,此情况下的对策请参考 7.4 节。
会话 ID 嵌入 URL 所导致的安全隐患总览
攻击手段与影响
下面我们就来看一下使 URL 中的会话 ID 通过 Referer 外泄的方法,以及会话 ID 外泄后造成的影响。
首先来看 PHP 的情况下是如何使会话 ID 嵌入到 URL 中的。
- 会话 ID 嵌入 URL 所需的条件
前面已经提到过,PHP 可以通过设置将会话 ID 嵌入到 URL 中。设置项目如表 4-14 所示。
表 4-14 php.ini 的会话 ID 设置项目
项目 解说 默认值 session.use_cookies 使用 Cookie 保存会话 ID 有效(On) session.use_only_cookies 仅将会话 ID 保存于 Cookie 有效(On) session.use_trans_sid 自动将会话 ID 嵌入 URL 无效(Off) 将上述设置进行组合后,会话 ID 的保存位置就可以被归纳为下表。
表 4-15 use_cookies 与 use_only_cookies 的组合
会话 ID 的保存位置 use_cookies use_only_cookies 会话 ID 仅保存在 Cookie 中 On On 可以使用 Cookie 时保存在 Cookie 中,不能使用 Cookie 时嵌入 URL On Off 无意义的组合 Off On 始终将会话 ID 嵌入 URL Off Off 其中,session.use_trans_sid 选项设为 On 时会话 ID 会被自动嵌入 URL,而设为 Off 的话则仅当应用中显示将会话 ID 嵌入 URL 时,会话 ID 才会被嵌入 URL。
- 范例脚本解说
以下为将会话 ID 设置为嵌入 URL(不使用 Cookie)的范例脚本。为了不影响应用的全局设置,这里我们在 .htaccess 文件内做如下设置。
代码清单 /462/.htaccess
php_flag session.use_cookies Off php_flag session.use_only_cookies Off php_flag session.use_trans_sid On
示例脚本包含了 3 个 PHP 文件。
- 起始页面
- 包含外部链接的页面
- 外部页面(假定为攻击者用来收集信息的网站)
各自的脚本代码如下所示。
代码清单 /462/46-001.php
<?php session_start(); ?> <body> <a href="46-002.php">Next</a> </body>
代码清单 /462/46-002.php
<?php session_start(); ?> <body> <a href="http://trap.example.com/46/46-900.cgi"> 跳转到外部网站的连接 </a> </body>
代码清单 /462/46-900.cgi【攻击者用来收集信息的网站】
#!/usr/bin/perl use utf8; use strict; use CGI qw/-no_xhtml :standard/; use Encode qw/encode/; my $e_referer = escapeHTML(referer()); print encode('UTF-8', <<END_OF_HTML); Content-Type: text/html; charset=UTF-8 <body> 这是收集会话 ID 的网站。Referer 信息如下 <br> $e_referer </body> END_OF_HTML
图 4-52 展示了页面的跳转过程。点击链接后跳转到外部网站时,URL 中的会话 ID 遭到了泄漏。
图 4-52 示例页面跳转
- 通过 Referer 泄漏会话 ID 所需的条件
网站满足以下两个条件时就有可能通过 Referer 泄漏会话 ID。
- 能够使用被嵌入 URL 的会话 ID
- 存在跳转至外部网站的链接。或用户能够自己发布链接
- 攻击流程
Referer 造成的会话 ID 泄漏可分为偶发事故和有意针对安全隐患实施的攻击这两种情况。其中,后者仅存在于应用的用户能够自己发布链接的网站。比如 Web 邮箱、论坛、博客、社交网站等。
接下来我们就以从 Web 邮箱实施攻击为例进行说明。攻击者发送带有链接的邮件给攻击目标应用的用户。邮件中通过“请看我的个人主页”或者“史上最大让利折扣”等语句引诱用户点击链接跳转至攻击者的网站。
图 4-53 从 Web 邮箱发动攻击
由于多数 Web 邮箱都会将 URL 格式的字符串转换为链接形式,因此,用户一旦点击链接进入攻击者的网站,Web 邮箱的 URL 中嵌入的会话 ID 就会通过 Referer 泄漏到攻击者网站。攻击者利用得到的 Referer 信息,就能够伪装成该用户。
2000 年 12 月,独立行政法人产业技术综合研究所的高木浩光等人组成的小组发表了题为“不用 Cookie 而在 URL 中嵌入 ID 的会话管理方式的安全隐患(1)——通过取得 REFERER 信息劫持免费邮箱网站的问题”的文章 36 。文中列举了在当时的 7 个 Web 邮箱服务中,将会话 ID 嵌入 URL 后会话 ID 通过 Referer 泄漏的状况、原理以及解决方法。虽然距离文章发表已经过去了十几年,但此问题还是没有得到足够的重视。
- 事故性的会话 ID 泄漏
如果网站不允许用户自己发布链接,攻击者就很难将用户诱导至自己的网站,然而,即使在这种情况下,只要网站中存在指向外部网站的链接,就仍然有可能将会话 ID 泄漏至这些外部网站。万一外部网站的管理员心怀不轨,就能够从 Referer 的日志中获取会话 ID 来伪装用户。
此外,也有因用户自己将带有会话 ID 的 URL 发布到论坛等地方,使该 URL 被搜索引擎收录而造成信息泄漏的事件。
- 影响
嵌入 URL 的会话 ID 经由 Referer 泄漏的影响,同前述的会话劫持的影响一样。
36 原文标题为:Cookie を使用せず URL に埋め込む ID に頼ったセッション管理方式の脆弱性 (1)——REFERER 情報取得による脆弱フリーメールサイトの乗っ取り問題——。原始的文章页面已被删除,现在可以从已归档的页面中浏览该文章。 http://web.archive.org/web/20030828174518/http://securit.gtrc.aist.go.jp/SecurIT/advisory/webmail-1/
安全隐患的产生原因
会话 ID 嵌入 URL 的直接原因为设置不完善或者程序中存在问题。
将会话 ID 嵌入 URL 分为有意和无意两种情况。而之所以特意将会话 ID 嵌入 URL,可能是因为以下两点原因。
- 2000 年前后由于隐私方面的问题而兴起了“Cookie 有害论”,造成了部分网站停止使用 Cookie。
- NTT Docomo 的手机浏览器迟迟不支持 Cookie37 ,因此,在面向手机的应用中,将会话 ID 嵌入 URL 至今还是主流方法。
37 2009 年夏季以后的机型终于支持 Cookie 了。
由于第三方 Cookie38 能够追踪用户的访问历史而造成隐私方面的问题,因此便产生了“Cookie 有害论”。但是那次事件以后,浏览器普遍都默认禁用了第三方 Cookie,所以也就没有理由连第一方 Cookie 也都禁用了。而且通常情况下将会话 ID 保存至 Cookie 中是最安全的方法,因此,如果由于厌恶 Cookie 而将会话 ID 嵌入到 URL 中,反而会使个人信息泄漏等事件更易于发生。
38 第三方 Cookie 不是由正在浏览的网站发行的 Cookie,而是指由横幅广告商或其他网站发行的 Cookie。
而手机方面,由于截至写作此书时大部分手机浏览器还不支持 Cookie,因此完全杜绝向 URL 中嵌入会话 ID 是非常艰难的。该问题将在第 7 章中详细讲述。
对策
为了不使用嵌入在 URL 中的会话 ID,就需要通过设置将会话 ID 保存在 Cookie 中。下面就来看一下各编程语言中将会话 ID 保存至 Cookie 的设置或编程方法。
- PHP
PHP 中进行如下设置后,就能将会话 ID 仅保存在 Cookie 中。
[Session] session.use_cookies = 1 session.use_only_cookies = 1
- Java Servlet(J2EE)
J2EE 中将会话 ID 嵌入 URL(J2EE 中称为 URL 重写)需要调用
HttpServletResponse
接口的encodeURL
方法或encodeRedirectURL
方法来重写 URL,因此,只要保证程序中没有调用的相关方法,会话 ID 就不会被嵌入到 URL。 - ASP.NET
ASP.NET 中默认将会话 ID 保存至 Cookie 中,但通过设置 web.config 也能采用将会话 ID 嵌入 URL 的方式。新生成 web.config 时可以不做任何操作,但如果要更改既有网站的设置,就需要进行以下设置将会话 ID 保存至 Cookie。
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <system.web> <sessionState cookieless="false " /> </system.web> </configuration>
4.6.4 固定会话 ID
概要
会话劫持的另一种攻击手段为从外部挟持会话 ID,这被称为会话固定攻击(Session Fixation Attack)。
会话固定攻击的流程如下。
1. 取得会话 ID
2. 强行将步骤 1 中的会话 ID 交给被害人
3. 被害人登录攻击目标 Web 应用
4. 攻击者使用该会话 ID 成功进入目标应用
会话固定攻击造成的影响同窃取会话 ID 一样,即通过伪装用户导致信息泄漏,以及使用被害人的权限恶意使用应用中的功能,如发布、更改或删除数据等。
应对会话固定攻击时,由于想要彻底杜绝上述的步骤 2 非常困难,因此普遍采用在用户登录时更换其会话 ID 的方法,这样就可以使攻击者无从得知用户登录后的会话 ID。
固定会话 ID 所导致的安全隐患总览
攻击手段与影响
接下来我们将通过示例脚本来解说会话固定攻击的方法与影响。
- 示例脚本介绍
为了方便会话固定攻击的实施,该示例脚本通过设置 .htaccess 使会话 ID 同时保存在了 Cookie 和 URL 中。具体设置如下。
代码清单 /463/.htaccess
php_flag session.use_cookies On php_flag session.use_only_cookies Off php_flag session.use_trans_sid On
示例脚本为精简后的认证页面和个人信息显示页面。页面构成如下。
- 用户名输入页面
- 认证页面(演示中不确认密码)
- 个人信息显示页面(显示用户名)
脚本的代码如下。
代码清单 /463/46-010.php
<?php session_start(); ?> <body> <form action="46-011.php" method="POST"> 用户名 :<input name="id" type="text"><br> <input type="submit" value=" 登录 "> </form> </body>
代码清单 /463/46-011.php
<?php session_start(); $id = $_POST['id']; // 任何 ID 都能登录成功 $_SESSION['id'] = $id; // 将用户名保存至会话中 ?> <body> <?php echo htmlspecialchars($id, ENT_COMPAT, 'UTF-8'); ?> 登录成功 <br> <a href="46-012.php"> 个人信息 </a> </body>
代码清单 /463/46-012.php
<?php session_start(); ?> <body> 当前用户名 :<?php echo htmlspecialchars($_SESSION['id'], ENT_COMPAT, 'UTF-8'); ?><br> </body>
该示例脚本在正常情况下的页面跳转如下所示。
图 4-54 示例页面跳转
- 会话固定攻击解说
下面我们来尝试攻击该示例脚本。攻击者使用如下 URL 将应用的用户诱导至登录页面。进行此操作前需要先清空 Cookie,因此请重启浏览器。
http://example.jp/463/46-010.php?PHPSESSID=ABC
下图为用户无意中点击恶意链接后跳转至的登录页面,可以看出用户在页面上输入了用户名(此处为 tanaka)。然后用户点击登录按钮后,认证就将在会话 ID 被固定的状态下进行。
图 4-55 在通过恶意 URL 跳转至的登录页面进行登录
这时,PHPSESSIS=ABC 的会话 ID 生效,用户信息就将被存储在此会话中。攻击者在受害用户进行登录时即可伺机使用如下 URL 访问被害人的个人信息。
http://example.jp/463/46-012.php?PHPSESSID=ABC
攻击者查看被害人的个人信息时的情形如下图所示。为了区别于被害人的页面,这里使用了 Google Chrome 浏览器。
图 4-56 成功查看了被害人的个人信息
由此可见,攻击者能够成功看到被害人的个人信息。
- 登录前的会话固定攻击
前面我们介绍了针对登录后的页面的会话固定攻击,而如果登录前的页面中使用了会话变量,就同样也会遭受会话固定攻击。这被称为登录前的会话固定攻击。下面我们就通过示例脚本来进行讲解。
示例脚本代码如下。代码中包括个人信息输入、个人信息确认、个人信息注册(演示中不执行注册处理)3 个页面。输入的字符串被保存至会话变量,点击确认画面上的“返回”链接时,用户就能看到刚才在文本框中输入的内容。
代码清单 /463/46-020.php
<?php session_start(); $name = @$_SESSION['name']; $mail = @$_SESSION['mail']; ?> <html> <head><title> 输入个人信息 </title></head> <body> <form action="46-021.php" method="POST"> 姓名 :<input name="name" value="<?php echo htmlspecialchars($name, ENT_COMPAT, 'UTF-8'); ?>"><br> 邮箱地址 :<input name="mail" value="<?php echo htmlspecialchars($mail, ENT_COMPAT, 'UTF-8'); ?>"><br> <input type="submit" value=" 确认 "> </form> </body> </html>
代码清单 /463/46-021.php
<?php session_start(); $name = $_SESSION['name'] = $_POST['name']; $mail = $_SESSION['mail'] = $_POST['mail']; ?> <head><title> 确认个人信息 </title></head> <body> <form action="46-022.php" method="POST"> 姓名 :<?php echo htmlspecialchars($name, ENT_COMPAT, 'UTF-8'); ?><br> 邮箱地址 :<?php echo htmlspecialchars($mail, ENT_COMPAT, 'UTF-8'); ?><br> <input type="submit" value=" 注册 "><br> <a href="46-020.php"> 返回 </a> </form> </body> </html>
代码清单 /463/46-022.php
<?php session_start(); $name = $_SESSION['name']; $mail = $_SESSION['mail']; ?> <head><title> 注册个人信息 </title></head> <body> 已注册 <br> 姓名 :<?php echo htmlspecialchars($name, ENT_COMPAT, 'UTF-8'); ?><br> 邮箱地址 :<?php echo htmlspecialchars($mail, ENT_COMPAT, 'UTF-8'); ?><br> </body> </html>
正常情况下的页面跳转如下。
图 4-57 页面跳转
接下来我们就尝试对此应用实施攻击。诱导用户使用以下 URL 访问应用并使其输入个人信息。
http://example.jp/463/46-020.php?PHPSESSID=ABC
受害用户输入自己的个人信息,如下图所示。
图 4-58 受害用户输入个人信息
而在另一边,攻击者会定期监视刚才的 URL 页面。当用户输入个人信息后,如下图所示,攻击者的浏览器中也能显示用户的个人信息。
图 4-59 受害用户的个人信息显示在攻击者的浏览器中
由此可见,不需要认证的网页如果使用了会话变量,也可能会遭受会话固定攻击。
但是,由于此情况下攻击者无法伪装成登录后的用户,也无法使用用户的权限进行恶意操作,因此攻击造成的影响就仅限于用户输入的信息被泄漏。
- 会话采纳
前面所介绍的攻击流程中使用了 PHPSESSID=ABC 这个会话 ID。我们发现,虽然 ABC 是攻击者任意生成的,但是也能够使攻击得到成功。这是因为能够接受来源不明的会话 ID 是 PHP 的特性之一。而此特性就被称为会话采纳(Session Adoption)。除了 PHP,ASP.NET 中也存在会话采纳的特征。而 PHP 和 ASP.NET 以外的中间件,如 Tomcat 等则不存在会话采纳,这种情况下,随意生成的会话 ID 就会被忽略。
在对不存在会话采纳的中间件上运行的应用程序发动攻击时,攻击者会先浏览攻击目标应用,取得有效的会话 ID,然后再利用此会话 ID 布置恶意网站。
由此可见,开发工具中若存在会话采纳就能减少会话固定攻击的步骤,然而,即便不存在会话采纳问题,会话固定攻击也不可能被完全杜绝。
- 仅在 Cookie 中保存会话 ID 的网站固定会话 ID
之前介绍的攻击示例中,我们使用的都是能够将会话 ID 保存在 URL 中的应用程序。这是因为会话 ID 保存在 URL 的情况下攻击起来比较容易。然而,仅将会话 ID 保存在 Cookie 时,会话 ID 还是有可能会被固定化。
通常情况下,从外部设置 Cookie 的会话 ID 是行不通的,但是,如果浏览器或 Web 应用中存在安全隐患就另当别论了。比如,以下安全隐患就有可能造成 Cookie 被第三方设置。
- Cookie Monster Bug(浏览器的安全隐患,参考 3.1 节)
- 跨站脚本漏洞(参考 4.3 节)
- HTTP 消息头注入漏洞(参考 4.7.2)
- 会话固定攻击的影响
一旦会话固定攻击取得成功,由于中招的用户(前例中为 tanaka)已处于登录状态,攻击者就能够使用该用户的权限执行操作或浏览信息等。
安全隐患的产生原因
固定会话 ID 安全隐患产生的根本原因为外界能够劫持会话 ID。因此,彻底应对就需要实施以下所有步骤。
- 不将会话 ID 嵌入 URL
- 不使用(或不让用户使用)存在 Cookie Monster Bug39 的浏览器
- 不使用易发生 Cookie Monster Bug 的地域型域名
- 消除跨站脚本漏洞
- 消除 HTTP 消息头注入漏洞
- 消除其他能够导致 Cookie 被篡改的安全隐患
39 Cookie Monster Bug 的详情请参考 3.1 节的专栏。
但是,想要满足以上所有条目并不简单。比如 Internet Explorer 中使用地域型域名时就存在 Cookie Monster Bug,而微软似乎并没有打算修复该问题。然而,Internet Explorer 又是使用率最高的浏览器,我们不可能强迫所有用户将其舍弃。
因此,目前采取的普遍做法是,姑且允许会话 ID 被外界挟持,而将防范重点放在防止会话固定攻击造成会话劫持上。
在认证成功时更改会话 ID 就是一种行之有效的方法,具体会在后面详述。
对策
正如前面所介绍的那样,会话 ID 被外界固定化的手段多种多样,有时还会恶意利用浏览器的 Bug(安全隐患),因此,Web 应用中防范会话固定攻击可以采取如下策略。
- 认证后更改会话 ID
PHP 中执行此处理可以使用 session_regenerate_id
函数。该函数的格式如下。
格式清单 session_regenerate_id 函数
bool session_regenerate_id([bool $delete_old_session = false])
session_regenerate_id
函数中有一个可省略的参数。但由于该参数会指定是否将变更前的会话 ID 对应的会话信息删除,所以应始终将该参数指定为 true
。
下面为添加了更改会话 ID 这一处理的脚本。
代码清单 /463/46-011a.php
<?php session_start(); $id = $_POST['id']; // 省略登录处理 session_regenerate_id(true); // 更改会话 ID $_SESSION['id'] = $id; // 将用户名保存至会话 ?> <body> <?php echo htmlspecialchars($id, ENT_COMPAT, 'UTF-8'); ?> 登录成功 <br> <a href="46-012.php"> 个人信息 </a> </body>
- 无法更改会话 ID 时采用令牌
有些 Web 应用的开发语言或中间件无法在程序中显式地更改会话 ID。使用此类开发工具时,可以使用令牌来防范会话固定攻击。
具体方法为,在登录时生成一个随机数字符串(令牌),并将其同时保存至 Cookie 和会话变量中。然后在各页面进行认证确认时比较 Cookie 和会话变量中的令牌值,如果两者一致即视为已认证,不一致时即视为认证错误。
由于只有在登录的时候令牌才能够被传到外界,攻击者无法得知令牌值,因此,使用令牌能够成功防御会话固定攻击。
此外,鉴于令牌需要确保在足够长的时间内无法被预测,生成令牌时应当使用密码学级别的伪随机数生成器。由于 PHP 中没有提供能够调用伪随机数生成器的函数,因此,这里我们使用“改善 PHP 的会话 ID 的随机性的方法”中提到的 /dev/urandom 来进行说明。
以下为登录后生成令牌部分的脚本。
代码清单 /463/46-015.php
<?php // /dev/urandom 通过 /dev/urandom 实现伪随机数生成器 function getToken() { $s = file_get_contents('/dev/urandom', false, NULL, 0, 24); return base64_encode($s); } // 假设到这里已经成功通过认证 session_start(); $token = getToken(); // 生成令牌 setcookie('token', $token); // 令牌 Cookie $_SESSION['token'] = $token;
认证后的页面通过以下脚本确认令牌。
代码清单 /463/46-016.php
<?php session_start(); // 确认用户名【省略】 // 确认令牌 $token = $_COOKIE['token']; if (! $token || $token != $_SESSION['token']) { die(' 认证错误 '); } ?> <body> 认证成功 </body>
虽然示例中使用的是 PHP,但由于 PHP 中提供了
session_regenerate_id
函数,因此并非一定要使用令牌。然而,由于令牌也能够作为 4.8.2 节讲述的“Cookie 的安全属性设置不完善”的对策来使用,因此,某些情况下该方法对 PHP 开发者来说会非常有用。详情请参考 4.8 节。 - 登录前的会话固定攻击的对策
如果登录前使用了会话变量,要完全防范会话固定攻击就非常困难。这种情况下,比较现实而有效的对策就是,登录前不使用会话管理机制,而使用 hidden 参数来传递值。
像电子商务网站的购物车功能这种不得不在登录前使用会话变量的情况下,可以参考以下对策。但要注意的是,这些都不是根本性的对策,而只能通过组合使用来提高防御能力。
- 不在登录前的会话变量内存储敏感信息
- 不使用嵌入 URL 的会话 ID
- 不使用地域型域名
总结
本节讲述了不完善的会话管理所导致的会话劫持。会话管理是安全性的要害之处,因此,若出现会话劫持的话就会造成巨大影响。
会话管理不完善的对策如下。
- 不自制会话管理机制而使用 Web 应用开发工具的内置功能
- 将会话 ID 保存至 Cookie 中
- 认证成功时更改会话 ID
- 认证前不在会话变量中存储敏感信息
幸运的是,本节所介绍的安全隐患防范策略的实施场所少而明确,实施成本并不高。因此,建议开发者们从设计阶段就开始有计划地落实防范策略。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论