简介
什么是XSS
XSS 漏洞,通常指的是网站对用户输入数据未做有效过滤,攻击者可以将恶意脚本注入网站页面中,达到执行恶意代码的目的。攻击者只需要诱使受害者打开特定的网址,就可以在受害者的浏览器中执行被注入的恶意代码,从而窃取用户身份,执行一些敏感操作,或是进行其他的危害行为。在常见的Web 漏洞中,XSS(Cross-site Script,跨站脚本)漏洞是最多见的,跨站脚本(Cross-site Script),按理应该简称为 CSS,但为了与层叠样式表(CSS)区分开,特意改为 XSS。
XSS的起源
最早的XSS 漏洞可追溯到 1999 年末,微软安全工程师发现一些网站遭到攻击,网站被插入了一些恶意脚本和图像标签。随后,微软对此类漏洞进行研究分析,并在 2000 年 1 月,正式使用“cross-site scripting”这个名称,然后逐渐被业界采用,留传至今。
XSS漏洞的分类
反射型 XSS
反射型 XSS 又被称为非持久型跨站脚本,它是将攻击代码放在 URL 参数中,而不是存储到服务器,因此需要诱使用户点击才能触发攻击。
如:通过向 name 参数输入以下代码即可触发漏洞
<script>alert(1)</script>
在 Chrome 浏览器中,用“检查”功能看下网页源码,可以发现我们输入的代码被解析并执行了:
漏洞代码也非常简单。从 GET 参数 name 获取用户输入后,未经过滤就直接调用 echo 函数输出到页面,最终导致 XSS的产生。漏洞代码如下:
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
存储型XSS
存储型被称为持久型跨站脚本。攻击者将恶意代码存储到服务器上,只要诱使受害者访问被插入恶意代码的页面即可触发。存储型 XSS 经常出现在一些可以发表评论的地方,如帖子、博客。
如:一个留言本的功能,支持用户发表评论,然后将用户输入的数据直接存储到数据库,并输出到页面上。这个过程中因为未做任何的过滤,导致了 XSS 漏洞的产生。存储型 XSS 的特点就是不需要在诱使用户访问的URL中包含攻击代码,因为它已经存储到了服务器中,只需要让用户访问包含输出攻击代码的页面即可
漏洞代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = mysql_real_escape_string( $message );
// Sanitize name input
$name = mysql_real_escape_string( $name );
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
//mysql_close();
}
?>
从 POST 参数中获取 mtxMessage 和 txtName 参数后,虽然经过一定过滤才插入到数据库中,但是中括号不会被过滤
在 Message 中输入“<script>alert(1)</script>”,点击“Sign Guestbook”提交,即可触发漏洞。
查看页面源码
DOM 型 XSS
DOM 型 XSS是基于文档对象模型(Document Objeet Model,DOM,用于将 Web 页面与脚本语言连接起来的标准编程接口)的一种漏洞,它不经过服务端,而是通过 URL 传入参数去触发,因此也属于反射型 XSS。
由于客户端的JavaScript可以访问浏览器页面中的 DOM 对象,因此它能够决定如何处理当前页面的 URL,比如获取 URL 中的相关数据进行处理,然后动态更新到页面上。这导致 DOM 型 XSS 的漏洞代码常位于网页的 JavaScript 代码中。
看例子:
点击事件函数
用户输入内容后点击"click me"回调函数执行:
第一行代码先通过
document.getElementById("text").value
获取 ID 为“text”的元素内容。其实这就是输入框的内容,输入框的 ID就叫“text”。第二行代码是将获取的输入框内容传递给 ID 为“dom”的元素,并将其写入
innerHTML
,也就是输出到 HTML 页面中,整个过程对用户输入数据都未做任何过滤。直接输入 test 看下:
如此,我们直接利用 JavaScript 伪协议来构造链接触发 JS 代码的执行,输入代码javascript:alert(1)
,然后点击"clickme",等回调函数执行完,点击“whatdo you see?”链接后即可触发
DOM 型 XSS 的相关 DOM 操作函数有很多,如 eval、document.write 等可触发漏洞的数据输出位置。
这是一份关于 DOM XSS 的数据污染源(Source,即用户输入数据)和漏洞触发点(Sink)的列表(虽然不够全面,但可以作为参考)
若数据从 Source 传到 Sink 过程中,未做任何过滤,就有可能存在 DOM XSS。这个思路也常作为动静态检测 DOM XSS 的重要思路。