xss跨站脚本攻击
xss跨站脚本攻击
1、介绍
XSS攻击,其实就是黑客将恶意的脚本,注入到浏览的网页中从而改变网页的dom结构,当受害者访问这些页面时,浏览器会解析并执行这些恶意代码,从而达到窃取用户身份/钓鱼/传播恶意代码等行为。
类型:反射性,存储型,dom型
场景:搜索框,留言板
环境:windows-s-2008,phpstudy2016,攻击机。
成因:对用户提交的数据没有做很好的过滤所导致
1、 反射型xss
特点:非持久化,每次必须注入才能触发。
由于dom型也算是反射型的一种,所以就拿到一块来分析。
说直白点就是你在网站输入文字后,你的文字会在网站上显示,说明你的文字暂时存放在了html中
举个栗子,我在下载小说的时候在书城搜索1,他会在页面留下如下图。

说明他也存在前端代码中

好!那我们就开始仿造!
<meta charset="UTF-8">
<form action="XSS.php" method="post">
<h1>反射型XSS</h1>
<input type="text" name="keyword" value="<?php echo @$_POST["keyword"]; ?>">
<input type='submit' value="搜索" name="submit">
<p>find:<?php echo @$_POST["keyword"]; ?></p>
</form>

来测试一下

因为有的网站你搜索的文字也会遗留在搜索框中,所以我们也这么处理了,这样来说,可以注入的点就有两个了。再直白点来说你改变了该网站两处地方

好到这了大家就可以理解反射性xss为什么是短暂的,因为你改变的是前端,而所有数据是服务器提供的,所以当你刷新的时候数据就会重置到一开始的样子。
2、存储型XSS
特点:持久化,一但成立不管谁访问都会受到影响
其实如果理解了上述所说的,你改变了网页的html,但是提供数据的是服务器所以刷新或者从新访问就会消失,但是存储型xss是改变了服务器的数据,然后影响了前端页面。所以只要服务器不改变,你注入的代码不被删除,影响就会一直存在。
我们也来模拟
首先我们在数据库中建立一个名为xss的数据库
然后在xss的数据库中创建一个名为message的表
有id,message字段,id为主键
CREATE TABLE `message` (
`id` int(50) NOT NULL AUTO_INCREMENT,
`message` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
模拟代码如下
#### XXS.php
<h1>存储型XSS</h1>
<?php
$host = "127.0.0.1";
$user = "root";
$pass = "root";
$db = "xss";
$link = @mysqli_connect($host,"root",$pass,$db);
if(!$link){
die(mysqli_connect_error());
}
?>
<?php
$sql = "select message,id from message;";
$results = mysqli_query($link,$sql);
if($results === false){
die("[".mysqli_errno($link)."]".mysqli_error($link));
}
?>
<table border="1" width="80%">
<?php
while($result = mysqli_fetch_assoc($results)){
$html = <<<HTML
<tr>
<td>{$result['id']}</td>
<td>{$result['message']}</td>
<td>
<a href="./xssform.php?id={$result['id']}">修改</a>|<a href="delect.php">删除</a>
</tr>
HTML;
echo $html;
}
?>
</table>
#### xssfrom
<?php
$host = "127.0.0.1";
$user = "root";
$pass = "root";
$db = "xss";
$link = @mysqli_connect($host,"root",$pass,$db);
if(!$link){
die(mysqli_connect_error());
}
?>
<?php
@$message = $_POST['message'];
//echo $message;
//echo "<br />";
$id = $_GET['id'];
//echo $id;
$sql = "update message set message='{$message}' where id='{$id}';";
//echo $sql;
$results = mysqli_query($link,$sql);
?>
<meta charset="UTF-8">
<form action="" method="post">
<h1>存储型XSS</h1>
<input type="text" name="message">
<input type='submit' value="确认修改" name="submit1">
<br/>
<a href="./XSS.php">返回上一级</a>
</form>


说明后台读取的数据在前段展现了,可以控制。
2、xss的攻防
攻击语句的构造
思路:既然就是注入一个可运行的html代码,那就可以利用<>.’’.” ”去闭合原来的html标签,再构造新的标签就好。
1、利用JavaScript伪协议
<a href=javascript:alert(/xss/)>click me!</a>
<img src="javascript:alert(/xss/)">
2、利用html中触发事件来攻击
<img onmouseover='alert(/dont touch me!/)' src=""> //鼠标悬在该图片上
<input type="text" onkeydown="alert(/xss/)"> //在该输入框按下键盘任意键
<input type="text" onkeyup="alert(/xss/)"> //在该输入框按下键盘任意键并抬起
这种事件还有很多大家可以去html学习的网站上去查看
代码测试实验
然后实验一下
输入框输入
<scrript>alert(/xss/)</script>

JS语句执行了,说明注入成功了。那我们来看一下现在网站的前端代码

执行的是下面的语句,因为上面的语句作为属性的值被“”引用了,所以我们只需利用 “>去闭合就好
代码就变成了

JS所以逃逸出来了,就执行了。
所以我们要怎么防御呢?
思路:既然是他们是利用“”,<>,script,javascript,img,a等去绕过的,那把这些关键字符过滤掉怎么样
在文件任意读取与下载中我们讲到了可以用str_replace()函数去过滤,但是他的缺点是可以双写或者大小写过滤。所以今天我们换一种函数preg_replace
preg_replace
是正则法则过滤,
preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'keyword' ] );
完美的过滤了<script /<SCRIPT
由于条件原因(token,我们会在csrf中详细讲述)我们借鉴一下DVWA中的xss的无懈可击版本
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {//基本输入都会做空判断
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); //检查token值
// Get input
$name = htmlspecialchars( $_GET[ 'name' ] );//把预定义的字符 "<" (小于)和 ">" (大于)转换为 HTML 实体
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
// Generate Anti-CSRF token
generateSessionToken();
?>
htmlspecialchars
不会让<>下的标签解析
<?php
$str = "This is some <b>bold</b> text.";
echo htmlspecialchars($str);
?>
>>>This is some <b>bold</b> text.
在我们的模拟上实验一哈,


被打印了出来没有被解析,说明成功。
反射型的说完了我们来看看存储型的。

让我们许修改1111为<script>alert(/xss/)</script>,然后返回发现
出现了弹窗,然后我们关闭了弹窗,再刷新一下页面发现还是会有弹窗
!
说明该语句只要存在数据库中就会永远生效。
因为大部分存储型xss在留言或者博客发表文章这一场景中,所以让我们的过滤就有了很多限制,不可能说用户想说的话十个字符,过滤了一半,这样就会极大降低了用户的使用体验。
我们还是先看一下DVWA中 存储型xss的完全防御代码
<?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();
?>
让我们来审计一下
先看一下各个函数的用法
trim
语法:trim(string,charlist)
string: 必需。规定要检查的字符串。
charlist:可选。规定从字符串中删除哪些字符。如果被省略,
则移除以下所有字符: "\0" - NULL
"\t" - 制表符
"\n" - 换行
"\x0B" - 垂直制表符
"\r" - 回车
" " - 空格
stripslashes:删除字符串中的\
bindParam:绑定参数

如果有'',会变成\'\'
3、总结
xss的防御
输入过滤
输入验证:
仅接受指定长度、
仅包含合法字符
仅接收指定范围
特殊的格式严格控制:邮箱、IP
输入过滤:
str_replace
trim
stripslashes
htmlspecialchars
输出编码
黑白名单策略
防御DOM型xss
避免客户端文档重写,重定向或者其他敏感操作!

浙公网安备 33010602011771号