Fortify Audit Workbench 笔记 Cross-Site Scripting-Persistent
Cross-Site Scripting: Persistent
Abstract
向 Web 浏览器发送非法数据会导致浏览器执行恶意代码。
Explanation
Cross-Site Scripting (XSS) 漏洞在以下情况下发生:
1.
数据通过一个不可信赖的数据源进入 Web 应用程序。 对于 Persistent(也称为 Stored) XSS,不可信赖的源通常为数据库或其他后端数据存储,而对于Reflected XSS,该源通常为 Web 请求。
2.
在未检验包含数据的动态内容是否存在恶意代码的情况下,便将其传送给了 Web 用户。 传送到 Web 浏览器的恶意内容通常采用 JavaScript 代码片段的形式,但也可能会包含一些 HTML、 Flash 或者其他任意一种可以被浏览器执行的代码。 基于 XSS 的攻击手段花样百出,几乎是无穷无尽的,但通常它们都会包含传输给攻击者的私人数据(如 Cookie 或者其他会话信息)。在攻击者的控制下,指引受害者进入恶意的网络内容;或者利用易受攻击的站点,对用户的机器进行其他恶意操作。
例 1
: 下面的 JSP 代码片段可根据一个已知的雇员 ID 查询数据库,并输出该雇员的姓名。
<%...
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from emp where id="+eid);
if (rs != null) {
rs.next();
String name = rs.getString("name");
}
%>
Employee Name: <%= name %>
如果对 name 的值处理得当,该代码就能正常地执行各种功能;如若处理不当,就会对代码的盗取行为无能为力。 这段代码暴露出的危险较小,因为 name 的值是从数据库中读取的,而且显然这些内容是由应用程序管理的。 然而,如果 name 的值是由用户提供的数据产生,数据库就会成为恶意内容沟通的通道。 如果不对数据库中存储的所有数据进行恰当的输入验证,那么攻击者便能在用户的 Web 浏览器中执行恶意命令。 这种类型的 Persistent XSS(也称为 Stored XSS)盗取极其阴险狡猾,因为数据存储导致的间接性使得辨别威胁的难度增大,而且还提高了一个攻击影响多个用户的可能性。 XSS 盗取会从访问提供留言簿 (guestbook)的网站开始。 攻击者会在这些留言簿的条目中嵌入 JavaScript,接下来所有访问该留言簿的用户都会执行这些恶意代码。
例 2: 下面的 JSP 代码片段可从 HTTP 请求中读取雇员的 ID, eid,并将其显示给用户。
<% String eid = request.getParameter("eid"); %>
...
Employee ID: <%= eid %>
如例 1 中所述,如果 eid 只包含标准的字母或数字文本,此代码就能正确运行。 如果 eid 里有包含元字符或源代码中的值,那么 Web 浏览器就会像显示 HTTP 响应那样执行代码。 起初,这个例子似乎是不会轻易遭受攻击的。 毕竟,有谁会输入导致恶意代码的 URL,并且还在自己的电脑上运行呢? 真正的危险在于攻击者会创建恶意的 URL,然后采用电子邮件或者社会工程的欺骗手段诱使受害者访问此 URL 的链接。 当受害者单击这个链接时,他们不知不觉地通过易受攻击的网络应用程序,将恶意内容带到了自己的电脑中。 这种对易受攻击的 Web 应用程序进行盗取的机制通常被称为反射式 XSS。 正如例子中所显示的, XSS 漏洞是由于 HTTP 响应中包含了未经验证的数据代码而引起的。 受害者遭受 XSS 攻击的途径有三种:
-
如例 1 所述, 应用程序将危险数据储存在一个数据库或其他可信赖的数据存储器中。 这些危险数据随后会被回写到应用程序中,并包含在动态内容中。 Persistent XSS 盗取发生在如下情况:攻击者将危险内容注入到数据存储器中,且该存储器之后会被读取并包含在动态内容中。 从攻击者的角度看,注入恶意内容的最佳位置莫过于一个面向许多用户,尤其是相关用户显示的区域。 相关用户通常在应用程序中具备较高的特权,或相互之间交换敏感数据,这些数据对攻击者来说有利用价值。 如果某一个用户执行了恶意内容,攻击者就有可能以该用户的名义执行某些需要特权的操作,或者获得该用户个人所有的敏感数据的访问权限。
-
如例 2 所述,系统从 HTTP 请求中直接读取数据,并在 HTTP 响应中返回数据。 当攻击者诱使用户为易受攻击的 Web 应用程序提供危险内容,而这些危险内容随后会反馈给用户并在 Web 浏览器中执行,就会发生反射式 XSS 盗取。 发送恶意内容最常用的方法是,把恶意内容作为一个参数包含在公开发表的 URL 中,或者通过电子邮件直接发送给受害者。 以这种手段构造的 URL 构成了多种“网络钓鱼”(phishing) 阴谋的核心,攻击者借此诱骗受害者访问指向易受攻击站点的 URL。 站点将攻击者的内容反馈给受害者以后,便会执行这些内容,接下来会把用户计算机中的各种私密信息(比如包含会话信息的 cookie)传送给攻击者,或者执行其他恶意活动。
-
应用程序以外的源代码会在数据库或其他数据存储器中储存危险数据,这些危险数据随后会被应用程序当作可信赖的数据读取并包含在动态内容之中。 许多现代 Web 框架都提供对用户输入执行验证的机制。 其中包括 Struts 和 Struts 2。 为了突出显示未经验证的输入源,该规则包会对 HP Fortify Static Code Analyzer(HP Fortify 静态代码分析器)报告的问题动态地重新调整优先级,具体方法是在采用框架验证机制时降低这些问题被利用的可能性并提供相应的依据。 我们将这种功能称之为上下文敏感排序。 为了进一步帮助 HPFortify 用户执行审计过程, Fortify 安全研究团队开发了 Data Validation(数据验证)项目模板,该模板根据应用于输入源的验证机制按文件夹对问题进行了分组。
Recommendation
针对 XSS 的解决方法是,确保在适当位置进行验证,并检验其属性是否正确。 由于 XSS 漏洞出现在应用程序的输出中包含恶意数据时,因此,合乎逻辑的做法是在数据流出应用程序的前一刻对其进行验证。 然而,由于 Web 应用程序常常会包含复杂而难以理解的代码,用以生成动态内容,因此,这一方法容易产生遗漏错误(遗漏验证)。 降低这一风险的有效途径是对 XSS 也执行输入验证。 由于 Web 应用程序必须验证输入信息以避免其他漏洞(如 SQL Injection),因此,一种相对简单的解决方法是,加强一个应用程序现有的输入验证机制,将 XSS 检测包括其中。 尽管有一定的价值,但 XSS 输入验证并不能取代严格的输出验证。 应用程序可能通过共享的数据存储或其他可信赖的数据源接受输入,而该数据存储所接受的输入源可能并未执行适当的输入验证。 因此,应用程序不能间接地依赖于该数据或其他任意数据的安全性。 这就意味着,避免XSS 漏洞的最佳方法是验证所有进入应用程序或由应用程序传送至用户端的数据。 针对 XSS 漏洞进行验证最安全的方式是,创建一份安全字符白名单,允许其中的字符出现在 HTTP 内容中,并且只接受完全由这些经认可的字符组成的输入。 例如,有效的用户名可能仅包含字母数字字符,电话号码可能仅包含 0-9 的数字。 然而,这种解决方法在 Web 应用程序中通常是行不通的,因为许多字符对浏览器来说都具有特殊的含义, 在写入代码时,这些字符仍应被视为合法的输入,比如一个 Web 设计版就必须接受带有 HTML 代码片段的输入。 更灵活的解决方法称为黑名单方法,但其安全性较差,这种方法在进行输入之前就有选择地拒绝或避免了潜在的危险字符。 为了创建这样一个列表,首先需要了解对于 Web 浏览器具有特殊含义的字符集。 虽然 HTML 标准定义了哪些字符具有特殊含义,但是许多 Web 浏览器会设法更正 HTML 中的常见错误, 并可能在特定的上下文中认为其他字符具有特殊含义。 卡耐基梅隆大学 (Carnegie Mellon University) 软件工程学院 (Software Engineering Institute) 下属的 CERT(R) (CERT(R) Coordination Center) 合作中心提供了有关各种上下文中认定的特殊字符的具体信息 [1]:在有关块级别元素的内容中(位于一段文本的中间): - "<" 是一个特殊字符,因为它可以引入一个标签。 - "&" 是一个特殊字符,因为它可以引入一个字符实体。 - ">" 是一个特殊字符,之所以某些浏览器将其认定为特殊字符,是基于一种假设,即该页的作者本想在前面添加一个 "<",却错误地将其遗漏了。 下面的这些原则适用于属性值: - 对于外加双引号的属性值, 双引号是特殊字符,因为它们标记了该属性值的结束。 - 对于外加单引号的属性值,单引号是特殊字符, 因为它们标记了该属性值的结束。 - 对于不带任何引号的属性值,空格字符(如空格符和制表符)是特殊字符。 - "&" 与某些特定变量一起使用时是特殊字符,因为它引入了一个字符实体。 例如,在 URL 中,搜索引擎可能会在结果页面内提供一个链接,用户可以点击该链接来重新运行搜索。 可以将这一方法运用于编写 URL 中的搜索查询语句,这将引入更多特殊字符: - 空格符、制表符和换行符是特殊字符,因为它们标记了 URL 的结束。 - "&" 是特殊字符,因为它可引入一个字符实体或分隔 CGI 参数变量。 - 非 ASCII 字符(即 ISO-8859-1 编码表中所有高于 128 的字符)不允许出现在 URL 中,因此在此上下文中也被视为特殊字符。 - 在服务器端对在 HTTP 转义序列中编码的参数进行解码时,必须过滤掉输入中的 "%" 符号。 例如, 当输入中出现 "%68%65%6C%6C%6F" 时,只有从输入的内容中过滤掉 "%",上述字符串才能在网页上显示为 "hello"。 在 的正文内: - 如果可以将文本直接插入到已有的脚本标签中,应该过滤掉分号、省略号、 中括号和换行符。 服务器端脚本: - 如果服务器端脚本会将输入中的感叹号 (!) 转换成输出中的双引号("),则可能需要对此进行更多过滤。 其他可能出现的情况: - 如果攻击者在 UTF-7 中提交了一个请求,那么特殊字符 "<" 可能会显示为 "+ADw-",并可能会绕过过滤。 如果输出包含在没有确切定义编码格式的网页中, 有些浏览器就会设法根据内容自动识别编码(此处采用 UTF-7 格式)。 一旦在应用程序中确定了针对XSS 攻击执行验证的正确要点,以及验证过程中要考虑的特殊字符,下一个难点就是定义验证过程中处理各种特殊字符的方式。 如果应用程序认定某些特殊字符为无效输入,那么您可以拒绝任何带有这些无效特殊字符的输入。 第二种选择就是采用过滤手段来删除这些特殊字符。 然而,过滤的负面作用在于,过滤内容的显示将发生改变。在需要完整显示输入内容的情况下,过滤的这种负面作用可能是无法接受的。 如果必须接受带有特殊字符的输入,并将其准确地显示出来,验证机制一定要对所有特殊字符进行编码,以便删除其具有的含义。 官方的 HTML 规范提供了特殊字符对应的 ISO 8859-1 编码值的完整列表。 许多应用程序服务器都试图避免应用程序出现 Cross-Site Scripting 漏洞,具体做法是为负责设置特定 HTTP 响应内容的函数提供各种实现方式,以检验是否存在进行 Cross-Site Scripting 攻击必需的字符。 不要依赖运行应用程序的服务器, 以此确保该应用程序的安全。 开发了某个应用程序后,并不能保证在其生命周期中它会在哪些应用程序服务器中运行。 由于标准和已知盗取方式的演变,我们不能保证应用程序服务器也会保持同步。
作者:马洪彪
出处:http://www.cnblogs.com/mahongbiao/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。