存储型xss与反射型xss的区别在于存储型会将用户输入的数据存入服务器,在用户下一次点击时便会触发,由于其隐蔽性较高,所以危害也普遍大于反射型xss。
首先来看low级别的源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
代码中用到的几个函数trim
函数,语法如下:trim(string,charlist)
,其用于移除字符串两侧的空白字符或其他预定义字符,mysqli_real_escape_string
函数,语法如下:mysql_real_escape_string(string,connection)
用于转义 SQL 语句中使用的字符串中的特殊字符,一般用于防止数据库撞库,这个例子将展示不使用mysql_real_escape_string(string,connection)
所带来的后果
<?php
$con = mysql_connect("localhost", "hello", "321");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
$sql = "SELECT * FROM users
WHERE user='{$_POST['user']}'
AND password='{$_POST['pwd']}'";
mysql_query($sql);
// 不检查用户名和密码
// 可以是用户输入的任何内容,比如:
$_POST['user'] = 'john';
$_POST['pwd'] = "' OR ''='";
// 一些代码...
mysql_close($con);
?>
结果如下
SELECT * FROM users
WHERE user='john' AND password='' OR ''=''
意味着攻击者无需输入合理的密码便可以进行登录
注:本实例来自w3schoolstripslashes(string)
函数用于删除反斜杠,可以看到此段代码并未对xss语句进行过滤,并且是存放在数据库中的,因此可以产生存储型XSS,如下图所示,首先我们在message框中插入恶意代码,执行效果如下接下来我们在name框中插入恶意代码,可以发现输入框对长度有所限制,一种方法我们通过bp抓包修改长度,另一种方法我们直接在控制台中修改maxlength来触发xss,如下图所示在长度限制这里,国内有位白帽子曾提出过分割拼接语句来触发xss这一方法,即由于长度限制,将语句分割成几部分分别输入,由于存在数据库中,当其成为一个完整的语句时便会被触发。
接下来来看middle级别的源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
首先来看关键函数strip_tags
其用于剥去字符串中的 HTML、XML 以及 PHP 的标签,但允许使用<b>
标签。addslashes(string)
函数返回在预定义字符之前添加反斜杠的字符串。预定义字符是:单引号(’)双引号(")反斜杠(\)以及NULL,通过这段代码可以看到,message由于对输入的内容进行了实体化编码,所以无法触发xss,而name仅对<script>
进行了过滤以及输入内容转义,所以我们可以使用例如<svg/onload=alert('name')>
的恶意代码来触发xss如下图所示接下来来看high级别的源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
message部分与middle相同,均进行了html实体化来防御xss,在name部分添加了正则匹配函数来进行防御(这一函数在上一篇文章中笔者已经详细描述过便不再赘述)但其忽律了例如img,iframe这样的标签,所以仍存在存储型xss漏洞,如下图所示
最后来看下impossible级别的源代码
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
// Sanitize name input
$name = stripslashes( $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$name = htmlspecialchars( $name );
// Update database
$data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
$data->bindParam( ':message', $message, PDO::PARAM_STR );
$data->bindParam( ':name', $name, PDO::PARAM_STR );
$data->execute();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
可以看到对name以及message均使用了htmlspecialchars函数从而防止了xss。