存储型XSS 存储在数据库中。XSS 是永久性的,直到重置数据库或手动删除有效负载。
低水平
1、查找输入输出点,在Name和message输入内容,内容显示在了页面下方,且再次点进该页面,内容仍显示在了页面上,说明输入的内容被存储了。
2、构造脚本,输入带script脚本的XSS,显示了弹窗。 因为内容被存储,所以每次点进该页面,都会出现一个弹窗。
每次刷新页面,都会出现该弹窗。这说明输入的恶意<script>脚本被存储在后端数据库或文件中,在每次打开这个页面的时候,脚本都会执行一遍,系统存在存储型XSS漏洞。
<?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();
}
?>
初级存储型XSS渗透测试环境对输入的“name”和“message”参数值主要使用了3个函数进行处理:trim()函数、stripslashes()函数和mysqli_real_escape_string()函数。trim()函数用于删除字符串两侧的空白字符或者其他自定义字符。源代码并没有指定自定义字符,因此,主要用于删除“name”和“message”参数值两侧的空白字符。stripslashes()函数用于删除字符串中的反斜杠。mysqli_real_escape_string()函数用于转义字符串中的特殊字符。上述3个函数都无法实现JavaScript脚本的过滤,因此,插入数据的SQL语句(变量$query的值)被执行后,将输入的恶意脚本插入到数据库中。
中级
<?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();
}
?>
①对“message”参数加强了防护:使用addslashes()函数在每个双引号“"”前添加反斜杠;使用strip_tags()函数删除字符串中的HTML、XML以及PHP的标签;使用htmlspecialchars()函数将所有的预定义字符转换成了HTML实体,不会将其作为脚本执行。因此,“Message”文本框做了较强的防护,无法通过该输入处插入JavaScript脚本。②对“name”参数也加强了防护:使用“str_replace(′<script>′,′′,$name);”语句将“<script>”字符串替换成空字符串。
- 使用
strip_tags
函数移除$message
中的HTML标签。- 使用
addslashes
函数对$message
进行转义,防止SQL注入。- 使用
mysqli_real_escape_string
函数对$message
进行额外的转义,这是为了防止SQL注入攻击。这个函数会根据当前数据库的字符集,将可能导致SQL语法错误的字符进行转义。- 最后,使用
htmlspecialchars
函数将$message
转换为HTML实体,这样可以防止HTML标签被浏览器解析为HTML代码。
构造攻击脚本,“name”参数仅仅将指定的字符串“<script>”进行了替换,因此,大小写和双写就可以绕过这个防护。同时,在前端页面中,“Name”文本框有字符长度最大为10的限制,我们借助Burp Suite来绕过该字符长度限制。
高水平
开发人员认为他们已经通过删除模式“<s*c*r*i*p*t”来禁用所有脚本使用。
高级存储型XSS渗透测试环境的源代码中“name”参数的值使用preg_replace()函数基于正则表达式进行替换,且添加“i”修饰符,不区分大小写,以防止发生大小写绕过和双写绕过的XSS攻击。与高级反射型XSS渗透测试环境的防护机制相同,因此,考虑使用标签绕过即可。
<?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();
}
?>
不可能的水平
使用内置的PHP函数(如“htmlspecialchars()”), 可以转义任何会改变输入行为的值。
<?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();
?>
不可能水平,
这是一个将用户输入数据插入到数据库中的 PHP 脚本,并增加了防范CSRF攻击的安全措施。
脚本首先使用 `checkToken()` 函数验证用户提交的token是否与当前session中的token相同,以避免CSRF攻击的风险。
随后,脚本使用多种净化方法来加强安全性,包括:
- 使用 `strip_slashes()` 函数删除用户输入消息和用户名中可能存在的反斜杠字符。
- 使用 `mysqli_real_escape_string()` 函数对用户输入数据进行SQL注入攻击的防御。
- 使用 `htmlspecialchars()` 函数转义用户输入消息和用户名中的特殊字符。此外,脚本还使用了PDO数据库抽象层来执行对数据库的操作,避免了使用原生SQL语句时,出现的SQL注入漏洞。
最后,脚本调用 `generateSessionToken()` 函数生成新的Session token。
使用这些净化方法和安全措施可以大大增强Web应用程序的安全性和可靠性,并避免恶意攻击。
防护
- 禁止解析为html标签
有的输入显示没有过滤字符,但是页面不会显示alert,是因为显示被过滤了。复制如下元素,显示为<span></span>10.100.40.35<script>alert(1)</script><span></span> 。