彻底理解前端安全口试题(1)—— XSS 攻击,3种XSS攻击详解,发起收藏(含 ...

打印 上一主题 下一主题

主题 883|帖子 883|积分 2653

媒介

前端关于网络安全看似高深莫测,其实来来回回就那么点东西,我总结一下就是 3 + 1  = 4,3个用字母形貌的【分别是 XSS、CSRF、CORS】 + 一个中心人攻击。固然 CORS 同源策略是为了防止攻击的安全策略,其他的都是网络攻击。除了这 4 个前端相关的口试题,其他的都是一些不常用的小喽啰。
我将会在我的《口试题一扫而空》专栏中先逐一详细介绍,然后再来一篇文章总结,预计一共5篇文章,欢迎大家关注~
本篇文章是前端网络安全相关的第一篇文章,内容就是 XSS 攻击。
一、预备工作

跨站脚本攻击(cross-site scripting),为了和 css 区分所有才叫 XSS【也叫作代码注入攻击】,重点在【脚本】两个字,以是同样都是利用 script 标签,XSS 和后面说的 CSRF 照旧有区别的。通过在网站注入恶意脚本,使脚本在用户的浏览器上运行,从而偷取用户的信息大概破环页面的结构。
1.1 拉取仓库

许多知识都需要结合实际的代码来学习,以是本篇文章的基础是需要一个服务端的项目,可以跟着我的这篇文章搭建自己的服务端项目。大概直接克隆我的仓库代码在这个提交上拉一个新分支,本篇文章所有的代码都是在这个提交基础上举行的。


 
1.2 新增 xss 文件夹

在项目的根目次增加一个 xss 文件夹,而且在 xss 下面新建 index.html 和 index.js

   __dirname 是 Node.js 中的一个特别变量,表示当前执行脚本地点的目次的绝对路径。它是全局对象 global 的属性之一,可在任何地方使用。详细可以看这个
  
  至于运行的时间为什么加上目次参数,请看这篇文章 。
1.3 提交代码

二、攻击方式

顾名思义,xss 的攻击方式重点是脚本,就是利用 js 脚本干的一些坏事,主要的攻击方式如下:
   

  • 利用脚本获取页面的数据,盗用 cookie、localStorage 等
  • 破坏页面结构,操作 dom
  • DOS 攻击,拒绝服务请求,恶意发送请求,占用服务器资源
  反正 js 醒目的事变都可以利用。
  三、攻击类型

xss 的攻击类型分别是 存储型、反射型、DOM 型,下面开始做详细的讲解
3.1 存储型 XSS 攻击

恶意脚本由前端生成、发送并存在目标服务器上,属于服务器端漏洞,重点是:

  • 这个脚本是前端某个用户写的,好好想想,肯定是这样的,服务端不会无缘无端的多出来数据;
  • 服务器在接收到前端传的内容后,没有颠末检查就存到数据库中
  • 攻击发生在前端再一次访问存储的数据的时间
3.1.1 详细流程


  • 一个用户修改一个公共【文件名】为一段脚本,如 
    1. <img src='invalid-image' onerror='alert("我是秦始皇,加v给我100万")'>
    复制代码
  • 服务器对【文件名】没有过滤(对不合法的文件名没有举行转义等处理),就保存在服务端数据库了
  • 当其他用户访问这个公共【文件名】的时间就会触发攻击
  • 脚本中可以获取网站的 cookie,把 cookie 数据传到黑客服务器【大概是利用脚本破坏页面结构等】
  • 拿到用户 cookie 信息后,就可以利用 cookie 信息在其他呆板上登录改用户的账号,并利用用户账号举行一些恶意操作
3.1.2 代码模拟

我模拟的是【破坏页面结构】的 XSS 攻击,我们再在 XSS 文件夹下新建两个文件

  • data.txt 用来存数据来模拟数据库
  • index2.html 用来展示第二个页面,用于访问数据库中的数据
现在我们总共有四个文件了。

系统功能形貌:

  • 一个公开可访问的书籍列表,一个管理员用户 1 可以在页面 1 (index.html) 修改书籍的名称;
  • 一个普通用户 2, 可以在页面 2(index2.html)访问书籍列表(也就是书籍名)
(1)index.html  的代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.   <head>
  4.     <meta charset="UTF-8" />
  5.     <meta name="viewport" content="width=`, initial-scale=1.0" />
  6.     <title>Document</title>
  7.     <style>
  8.       h1,h2 {
  9.         margin: 0;
  10.       }
  11.       .box {
  12.         display: inline-flex;
  13.         flex-direction: column;
  14.       }
  15.       .book-name {
  16.         margin: 10px 0;
  17.         width: 400px;
  18.         height:100px
  19.       }
  20.       .button {
  21.         width: 100px;
  22.         height: 38px;
  23.       }
  24.     </style>
  25.   </head>
  26.   <body>
  27.     <div class="box">
  28.       <h1>【首页,用户1】</h1>
  29.       <h1>设置一个公开的书籍的名称</h1>
  30.       <div>这个书籍是所用用户都可以访问的</div>
  31.       <div>修改书名:</div>
  32.       <textarea class="book-name" id="name" ></textarea>
  33.       <button class="button" onclick="saveName()">保存</button>
  34.     </div>
  35.     <script>
  36.       function saveName() {
  37.         const name = document.getElementById('name').value;
  38.         if (name) {
  39.           fetch('saveName', {
  40.             method: 'post',
  41.             headers: {
  42.               'content-type': 'application/json'
  43.             },
  44.             body: JSON.stringify({
  45.               name: name
  46.             }),
  47.           }).then(() => {
  48.             console.log('保存成功 姓名:', name)
  49.           })
  50.         }
  51.       }
  52.     </script>
  53.   </body>
  54. </html>
复制代码
(2)index2.html 的代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.   <head>
  4.     <meta charset="UTF-8" />
  5.     <meta name="viewport" content="width=`, initial-scale=1.0" />
  6.     <title>Document</title>
  7.     <style>
  8.       h1,
  9.       h2 {
  10.         margin: 0;
  11.       }
  12.       .box {
  13.         display: inline-flex;
  14.         flex-direction: column;
  15.       }
  16.       .book-name {
  17.         margin: 10px 0;
  18.         width: 400px;
  19.         height: 100px;
  20.       }
  21.       .button {
  22.         width: 100px;
  23.         height: 38px;
  24.       }
  25.     </style>
  26.   </head>
  27.   <body>
  28.     <div class="box">
  29.       <h1>【另一个页面,用户2】</h1>
  30.       <h1>书籍列表页面</h1>
  31.       <div>访问公开的书籍</div>
  32.       <div>书名</div>
  33.       <div id="name"></div>
  34.     </div>
  35.     <script>
  36.       // 初始化的时候就获取数据
  37.       fetch('/getName')
  38.         .then((res) => {
  39.           return res.json();
  40.         })
  41.         .then((data) => {
  42.           const name = data.name;
  43.           const ele = document.getElementById('name');
  44.           // 设置书名
  45.           ele.innerHTML = name;
  46.         });
  47.     </script>
  48.   </body>
  49. </html>
复制代码
(3)index.js 的代码

  1. const express = require('express');
  2. const path = require('path');
  3. const bodyParser = require('body-parser')
  4. const fs = require('fs')
  5. const app = express();
  6. app.use(bodyParser.urlencoded({ extended: true }));
  7. app.use(bodyParser.json());
  8. // 首页 -> 用户1 保存数据
  9. app.get('/', function (req, res) {
  10.   res.sendFile(path.join(__dirname, '/index.html'));
  11. });
  12. // 首页 -> 保存书名
  13. app.post('/saveName', function (req, res) {
  14.   const { name } = req.body
  15.   fs.writeFileSync(path.resolve(__dirname, 'data.txt'), name)
  16.   res.send('保存成功');
  17. });
  18. // 新建另一个页面
  19. app.get('/index2', function (req, res) {
  20.   res.sendFile(path.join(__dirname, '/index2.html'));
  21. });
  22. // 另一个页面 -> 用户2 获取数据
  23. app.get('/getName', function (req, res) {
  24.   // 从 data.txt 中取出存储的书名
  25.   const name = fs.readFileSync(path.resolve(__dirname, 'data.txt')).toString()
  26.   console.log(name)
  27.   res.send(
  28.     JSON.stringify({
  29.       name: name,
  30.     })
  31.   );
  32. });
  33. app.listen(3000);
复制代码
   注意 xss/index.js 中关于使用 express 写服务端代码,有两个知识点
  (1)post 请求需要使用 body-parser 分析 body 的 json 数据
  (2)写入/读取 data.txt 是用node 的核心模块 fs,方法 fs.writeFileSync / s.readFileSync
  (3)fs.readFileSync 的结果是 buffer 需要转成字符串 toString()
  data.txt 内里不必输入任何内容,我们会通过代码举行写入。
(4)运行代码

  1. npm run dev xss
复制代码
访问 localhost:3000,输入一段恶意的代码作为书名,点击保存

 
点击保存之后,输入的内容会保存在 data.txt 中

 再打开 localhost:3000/index2.html,这样一个存储型的、对于 dom 结构的破环的 XSS 攻击就完成了。

   这内里有一个知识点就是我们在 index.html 中保存的恶意脚本是 
  1. <img src='invalid-image' onerror='alert("我是秦始皇,加v给我100万")'>
复制代码
我们用了一个 img 标签,然后使用 onerror 变乱内里写一些脚本,而没有这么写,为啥呢?
  1. <script>alert(1)</script>
复制代码
这是由于我们在 index2.html 内里使用 innerHTML 将数据渲染在页面上,使用 innerHTML 直接渲染 script 字符串,脚本是不会被执行的。 而变乱处理器,如 img 的 onerror 变乱却可以触发。
  (5)提交代码

3.2 反射型 XSS 攻击

恶意脚本在前端访问的 URL 中,要用户自动点击 URL,服务器分析 URL, 并返回恶意脚本,属于服务端漏洞,重点是:

  • 恶意脚本不是前端用户手动写的,和存储型有区别
  • 恶意脚本在 URL 上,需要用户手动点击才气触发攻击
  • 服务器收到访问 URL 的请求时,分析 URL 得到恶意脚本,然后返回给客户端
  • 服务器不会存储恶意脚本,只会返回(反射)它
3.2.1 详细流程


  • 黑客诱导用户访问有恶意代码的 URL ,如 https://danger.com?xss=<script>alert('attack)</script>
  • 服务器接收到访问 URL 的请求
  • 服务器分析 URL ,得到 XSS 的值,但是并没有对 XSS 的值做校验是否合法,对于不合法的没有举行转义
  • 服务器将分析后的结果,反射给浏览器,如返回 { xss : <script>alert('attack)</script> }
  • 浏览器的恶意代码的 URL 页面有渲染 XSS 值的逻辑
  • 此时浏览器弹出告诫框
  • 一个反射型 XSS 攻击就完成了
黑客经常会通过 qq群大概邮件等渠道诱导用户去点击这些恶意链接。
   看到没?没事别惦记七零八落的连接,谨防电信诈骗!
  3.2.2 代码模拟

(1)新建 xss/ index3.html


  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.     <title>Document</title>
  7. </head>
  8. <body>
  9.     <div>反射型 xss 攻击</div>
  10.     <h1><a href="http://localhost:3000/reflect?xss=<script>alert('哈哈,你上当了')</script>"> 点击收款100万 </a></h1>
  11.    
  12. </body>
  13. </html>
复制代码
(2)修改 index.js 


   注意看这个服务端代码关于 reflect 方法的 get 请求,他就是简单的把参数上的获取并返回,浏览器就能运行这段代码。 
  (3)运行代码

  1. npm run dev xss
复制代码
访问 localhost:3000/index3

 跳转之后发生了攻击

(4)提交代码

3.3 DOM 型 XSS 攻击

攻击者通过利用 DOM 来触发攻击,是前端漏洞,不扳连到服务器。重点是:

  • 整个过程服务器不到场
  • 脚本的详细来源照旧利用网页中用户交互的部分, URL 参数、表单输入、cookie 等
  • 多发生在使用 innerHTML 的场景
3.3.1 详细流程


  • 从 URL 中取出恶意代码/大概从用户输入的表单/ cookie 中
  • 把恶意代码使用 innerHTML 插入页面,改变 dom 结构
尤其是在使用 innerHTML 的时间会出现这种问题,可以使用插件如 xss-filters 避免这一类问题,这个插件的原理是将某些字符举行转义,将 < 转成 &lt 等
3.3.2 代码模拟

(1)新增 index4.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
  6.     <title>Document</title>
  7.     <style>
  8.         textarea {
  9.             width: 400px;
  10.             height: 50px;
  11.         }
  12.     </style>
  13. </head>
  14. <body>
  15.     <h1>dom 型 xss 攻击</h1>
  16.     <div>输入</div>
  17.     <textarea id="input"></textarea>
  18.     <button onclick="save()">保存</button>
  19.     <hr>
  20.     <div>输出</div>
  21.     <div id="output"></div>
  22.     <script>
  23.         function save() {
  24.             const text = document.getElementById('input').value
  25.             if (text) {
  26.                 const output = document.getElementById('output')
  27.                 output.innerHTML = text
  28.             }
  29.         }
  30.     </script>
  31. </body>
  32. </html>
复制代码
(2)修改 xss/index.js


(3)运行代码

  1. npm run dev xss
复制代码
 点击保存按钮之后,就出现攻击了。

   注意,这全程没有服务端的到场,但是我们照旧修改 index.js 。但是其实 index.js 内里的代码就是为了起一个服务运行 index4.html 而已, 应该很好理解吧。
  (4)提交代码

好了至此三种类型的 XSS 攻击我们都用代码的形式实现。一点都不难吧,光说不练假把式,如果只看理论知识,肯定云里雾里,你跟着一起写一遍代码就理解了,不用背诵就记下来了。

3.4 XSS 攻击防御方法


  • 不使用服务端渲染,前两种是服务端的安全漏洞,服务器不能信任前端输入的任何东西,服务端要对输入脚本举行过滤大概转码。<script> 标签被转换为 <script> ,注意,https 不能防止安全问题,只会增加攻击难度和资本;
  • 对于 dom 型,对要插入的 html 做好充实的转义, npm 包:xss-filters;
  • 使用内容安全策略,csp 建立白名单,告诉浏览器可以执行和加载哪些功能 Content-Security-Policy 服务端设置【设置 csp 有两种方式 http 头部、meta 标签】
  • x-content-type-options\x-frame-options\x-xss-protentcion 等httip头部设置
  • 敏感信息 cookie 设置 httpOnly
总结

xss 攻击的三种类型都用代码模拟了,我的仓库地址如下,欢迎查看
yangjihong2113/learn-express
内容较多,难免疏漏,如有问题,欢迎指正。
这是一系列的文章,关于网络安全的内容还有 CSRF、CORS 和中心人攻击的内容没有总结,一连更新中,欢迎关注。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

刘俊凯

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表