关于
简介
XSS(Cross Site Scripting),跨站脚本攻击。攻击者通过网站注入点注入客户端可执行解析的payload,当用户访问网页时,恶意payload自动加载并执行,以达到攻击者目的(窃取Cookie、恶意传播、钓鱼欺骗等)。
危害

- 获取用户信息:浏览器信息、IP地址、Cookie等;
- 钓鱼:构造与原网站相似的网站,骗取账号密码;
- 注入密码或广告链接:在主站注入非法网站的链接;
- 后台增删改查数据:配合CSRF,骗取用户点击,利用JS模拟浏览器发包;
- XSS蠕虫:进入网站自动进行某些操作,例如关注博主或者自动回复。
基本类型
主要分为三种:
反射型XSS:非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面。反射型XSS大多数是用来盗取用户的Cookie信息。
储存型XSS:持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie。
DOM型XSS:不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM-XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。
反射型
特点
- 仅执行一次,非持久型;
- 参数型跨站脚本。
攻击流程

源代码
- 前端页面:
<html>
<head>
<title>反射型XSS</title>
</head>
<body>
<form action="xss1.php" method="get">
Please input your name:
<input type="text" name="name" />
<input type="submit" />
</form>
</body>
</html>
- 后端页面:
<?php
if (array_key_exists("name", $_REQUEST) && $_REQUEST['name'] != NULL) {
$name = $_REQUEST['name'];
echo 'Welcome *' . $name . '*';
}
?>
演示
前端是一个提交框,提交数据到后端

后端会把输入的数据展示出来

注入JS:<script>alert(1)</script>
,进入页面会执行

在源码中可以看到已经成功注入

这就是最基本的反射型的XSS漏洞,这种漏洞数据流向是: 前端–>后端–>前端。
储存型
特点
- 持久型;
- 恶意脚本直接储存到数据库中。
攻击流程

源代码
- 前端页面
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>储存型XSS</title>
</head>
<body>
<form action="xss2.php" method="post">
<table>
<tr>
<td>昵称:</td>
<td><input type="text" name="nickName" /></td>
</tr>
<tr>
<td>留言:</td>
<td><textarea name="message" rows="3"></textarea></td>
</tr>
<tr>
<td> </td>
<td>
<input name="sign" value="添加留言" type="submit" onclick="return validate_form(this.form);"></input>
<input name="delete" value="清除留言" type="submit" onclick="return firm();"></input>
</td>
</tr>
</table>
</form>
</body>
<script type="text/javascript">
function validate_required(field, info) {
with (field) {
if (value == null || value == "") {
alert(info);
return false;
} else {
return true;
}
}
}
function validate_form(form) {
with (form) {
if (!validate_required(nickName, "昵称不能为空!")) {
nickName.focus();
return false;
}
if (!validate_required(message, "消息内容不能为空!")) {
message.focus();
return false;
}
}
}
function firm() {
return confirm("确定清除所有留言吗?");
}
</script>
</html>
- 后端页面
<?php
$conn = @mysql_connect('localhost', 'root', 'root') or die('failed');
@mysql_select_db('xss', $conn);
if (array_key_exists("delete", $_POST)) {
$query = "TRUNCATE xss";;
$result = @mysql_query($query, $conn) or die(mysql_error());
echo "<script>alert('清除成功!')</script>";
}
if (isset($_POST['sign'])) {
$message = trim($_POST['message']);
$username = trim($_POST['nickName']);
$query = "INSERT INTO xss ( username, message) VALUES ('$username', '$message');";
$result = @mysql_query($query, $conn) or die(mysql_error());
echo "<script>alert('添加成功!')</script>";
}
$query = "SELECT username, message FROM xss;";
$result = mysql_query($query, $conn) or die(mysql_error());
while ($row = mysql_fetch_assoc($result)) {
$name = $row['username'];
$message = $row['message'];
echo "<div> Name:" . $name . "<br>";
echo " Message:" . $message . "</div><br>";
}
?>
演示
前端是一个表单,用于将数据提交到后端

后端用于接收数据,并将数据展示给用户

注入JS代码:<script>alert(1)</script>
,成功弹窗

查看数据库,成功插入数据

每次进入页面都会执行代码

DOM型
特点
- 全程在前端执行,不经过后端;
- 主要存在于能修改页面的dom。
攻击流程
无交互,全程在前端执行
源代码
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>DOM型XSS</title>
</head>
<body>
<script type="text/javascript">
var pos = document.URL.indexOf("name=") + 5;
eval(document.URL.substring(pos, document.URL.length));
</script>
<h2>This is a xss test ...</h2>
</body>
</html>
演示
一个简单的页面

传入参数后会被执行

防护
总体思路是对用户的输入进行过滤,对输出进行编码,阻止恶意操作的执行。
不管是哪种类型的XSS,都可以在服务端进行过滤。
黑(白)名单
- 黑名单:过滤特殊符号及字符
- 白名单:限制类型或符号
httponly
在setcookie中将httponly选项设置为true
str_replace()
功能:替换字符串,区分大小写
用法:
$name = str_replace('<script>', '', $name);
preg_replace()
功能:对目标字符串进行一次正则替换,i模式下不区分大小写
用法:
$name = preg_replace('/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t(.*)>/i', '', $name);
htmlspecialchars()
功能:把预定义的字符转换为HTML实体,防止被浏览器当做HTML元素
用法
$name = htmlspecialchars($name);
绕过
<>
当<
、>
被过滤时,可以利用标签自带的事件绕过
- 输入框:onfocus、onclick
- svg:onload
script
利用img标签的onerror属性执行JS
<img src="1" onerror="alert(1)"/>
