提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
放射型 XSS(Reflected XSS) 是一种跨站脚本攻击(Cross-Site Scripting, XSS)类型,指攻击者将恶意脚本代码作为参数注入到用户的请求中,并通过服务器原样返回这些参数到网页中,导致脚本被立即执行。
特点:
-
非持久性:恶意代码不会被存储在服务器端数据库中。
-
依赖用户点击链接:攻击者需要引诱受害者访问一个包含恶意脚本的 URL。
-
脚本立即在用户浏览器中执行。
提示:以下是本篇文章正文内容,下面案例可供参考
一、low级别源码分析
<?php
// 关闭浏览器的 XSS 防护(IE/Edge 专属,其他浏览器基本无效),以方便演示漏洞
header ("X-XSS-Protection: 0");
// 判断是否存在 name 参数,且不为空
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// 将 name 参数直接输出到页面中,未经过任何转义或过滤 —— 存在反射型 XSS 漏洞
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
- low级别的代码只是判断了
name
参数是否为空,如果不为空的话就直接打印出来,并没有对name
参数做任何的过滤和检查,没用进行任何的对XSS攻击的防御措施,存在非常明显的XSS漏洞,用户输入什么都会被执行; - 用alert进行弹窗测试验证是否存在XSS,输入
<script>alert(1)</script>
查看返回结果:
成功弹窗,说明存在XSS漏洞并且可利用,可以结合beef漏洞利用
二、medium级别的源码分析
<?php
// 关闭浏览器的 XSS 自动防护(主要针对 IE),为了方便测试 XSS
header ("X-XSS-Protection: 0");
// 判断是否存在 name 参数,且不为空
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// 获取用户输入,同时尝试进行简单的过滤:删除字符串中的 <script>
// 但是这种方式不彻底,比如 <ScRiPt> 或 <script > 不会被过滤
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// 输出到页面中,仍然未使用 htmlspecialchars,因此可能存在 XSS 漏洞
echo "<pre>Hello ${name}</pre>";
}
?>
安全性分析(为什么仍然存在 XSS 风险):
-
开发者试图用
str_replace('<script>', '', $_GET['name'])
来过滤<script>
标签,但这是一种不完整的防护方式。-
攻击者可以轻松绕过,例如:
-
<scr<script>ipt>alert(1)</scr<script>ipt>
➜ 仍然有效 -
<ScRiPt>alert(1)</ScRiPt>
➜ 因大小写不同不会被过滤 -
<script >alert(1)</script >
➜ 因空格而绕过
-
-
以上三种测试方式都可以触发弹窗、可自行测试
三、high级别的源码分析
<?php
// 关闭浏览器的 XSS 自动防护(主要对 IE 有效)
header ("X-XSS-Protection: 0");
// 判断是否存在 name 参数,且不为空
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// 对用户输入进行过滤:使用正则表达式去除包含 script 的标签
// 匹配形式为 <(任意字符)s(任意字符)c(任意字符)r(任意字符)i(任意字符)p(任意字符)t>(不区分大小写)
// 示例:<script>、<ScRipT> 等都能被匹配并移除
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// 将过滤后的内容直接输出到页面中(未进行 HTML 实体编码)
echo "<pre>Hello ${name}</pre>";
}
?>
安全分析(仍然存在潜在漏洞点):
-
使用了复杂的正则去匹配
<script>
标签的各种变形,属于一种黑名单式过滤(靠移除危险关键词); -
但是这种过滤方式:
-
仍然不能防止非 script 标签造成的 XSS,如:
<img src=x onerror=alert(1)>
-
正则匹配范围有限,比如:
<scr<script>ipt>alert(1)</scr<script>ipt> // 可能绕过
-
-
没有使用
htmlspecialchars()
来进行 HTML 实体编码,仍存在反射型 XSS 的可能。
用其他html标签也可以验证:
输入:
<img src=x οnerrοr=alert(1)>
<svg οnlοad=alert(1)>
四、impossible级别的源码分析
<?php
// 如果用户点击了“Sign Guestbook”按钮,说明提交了表单
if( isset( $_POST[ 'btnSign' ] ) ) {
// 检查 Anti-CSRF token,防止跨站请求伪造攻击
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// 获取用户输入的留言信息(message)并去除首尾空格
$message = trim( $_POST[ 'mtxMessage' ] );
// 获取用户输入的名字(name)并去除首尾空格
$name = trim( $_POST[ 'txtName' ] );
// 对 message 做转义处理:先去除反斜杠
$message = stripslashes( $message );
// 使用 mysqli_real_escape_string 进行 SQL 注入防护(注意这里可能有兼容问题)
$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)) ? "" : ""));
// 使用 htmlspecialchars 进行 XSS 防护,将 HTML 特殊字符转义
$message = htmlspecialchars( $message );
// 对 name 做转义处理:去除反斜杠
$name = stripslashes( $name );
// 使用 mysqli_real_escape_string 防止 SQL 注入
$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)) ? "" : ""));
// 使用 htmlspecialchars 防止 HTML 注入(XSS)
$name = htmlspecialchars( $name );
// 使用 PDO 预处理语句插入用户输入到数据库(防止 SQL 注入)
$data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
// 绑定参数 message 到 SQL 中的 :message 占位符,类型为字符串
$data->bindParam( ':message', $message, PDO::PARAM_STR );
// 绑定参数 name 到 SQL 中的 :name 占位符,类型为字符串
$data->bindParam( ':name', $name, PDO::PARAM_STR );
// 执行 SQL 插入操作,将留言保存到数据库
$data->execute();
}
// 生成新的 CSRF token,用于防止下一次表单提交被伪造
generateSessionToken();
?>
htmlspecialchars()
是 XSS 防护核心
因为 XSS 的根本是:
用户提交的内容被浏览器“当成代码”执行了,而不是“当成文本”展示出来。
而 htmlspecialchars()
的作用就是:
把
< > " ' &
等特殊字符,转成浏览器不会当成标签或脚本解析的实体字符。